Performance monitor stats gathering.

Re-landing https://chromiumcodereview.appspot.com/10656052 after it was reverted.
See https://chromiumcodereview.appspot.com/10827151/

TBR=brettw@chromium.org

BUG=130212

Review URL: https://chromiumcodereview.appspot.com/10831196

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@150475 0039d316-1c4b-4281-b951-d872f2087c98
parent 593ad7dc
......@@ -42,8 +42,15 @@ const double kMetricCPUUsageTickSize = 100.0;
// Private Memory Usage
const char kMetricPrivateMemoryUsageName[] = "Private Memory Usage";
const char kMetricPrivateMemoryUsageDescription[] =
"The private memory usage measured in bytes.";
const char kMetricPrivateMemoryUsageUnits[] = "percent";
"The total private memory usage of all chrome processes measured in bytes.";
const char kMetricPrivateMemoryUsageUnits[] = "bytes";
const double kMetricPrivateMemoryUsageTickSize = 10000000.0;
// Shared Memory Usage
const char kMetricSharedMemoryUsageName[] = "Shared Memory Usage";
const char kMetricSharedMemoryUsageDescription[] =
"The total shared memory usage of all chrome processes measured in bytes.";
const char kMetricSharedMemoryUsageUnits[] = "bytes";
const double kMetricSharedMemoryUsageTickSize = 10000000.0;
} // namespace performance_monitor
......@@ -28,6 +28,10 @@ extern const char kMetricPrivateMemoryUsageName[];
extern const char kMetricPrivateMemoryUsageDescription[];
extern const char kMetricPrivateMemoryUsageUnits[];
extern const double kMetricPrivateMemoryUsageTickSize;
extern const char kMetricSharedMemoryUsageName[];
extern const char kMetricSharedMemoryUsageDescription[];
extern const char kMetricSharedMemoryUsageUnits[];
extern const double kMetricSharedMemoryUsageTickSize;
} // namespace performance_monitor
......
......@@ -25,6 +25,12 @@ const MetricDetails kMetricDetailsList[] = {
kMetricPrivateMemoryUsageUnits,
kMetricPrivateMemoryUsageTickSize
},
{
kMetricSharedMemoryUsageName,
kMetricSharedMemoryUsageDescription,
kMetricSharedMemoryUsageUnits,
kMetricSharedMemoryUsageTickSize
},
};
COMPILE_ASSERT(ARRAYSIZE_UNSAFE(kMetricDetailsList) == METRIC_NUMBER_OF_METRICS,
metric_names_incorrect_size);
......
......@@ -13,6 +13,7 @@ namespace performance_monitor {
enum MetricType {
METRIC_CPU_USAGE,
METRIC_PRIVATE_MEMORY_USAGE,
METRIC_SHARED_MEMORY_USAGE,
METRIC_NUMBER_OF_METRICS
};
......
......@@ -5,10 +5,12 @@
#include "chrome/browser/performance_monitor/performance_monitor.h"
#include <set>
#include <vector>
#include "base/bind.h"
#include "base/logging.h"
#include "base/process_util.h"
#include "base/stl_util.h"
#include "base/string_number_conversions.h"
#include "base/threading/worker_pool.h"
#include "base/time.h"
......@@ -16,7 +18,6 @@
#include "chrome/browser/browser_shutdown.h"
#include "chrome/browser/extensions/crx_installer.h"
#include "chrome/browser/performance_monitor/constants.h"
#include "chrome/browser/performance_monitor/database.h"
#include "chrome/browser/performance_monitor/performance_monitor_util.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
......@@ -26,6 +27,8 @@
#include "chrome/common/chrome_version_info.h"
#include "chrome/common/extensions/extension.h"
#include "chrome/common/extensions/extension_constants.h"
#include "chrome/test/base/chrome_process_util.h"
#include "content/public/browser/browser_child_process_host.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/notification_types.h"
......@@ -36,6 +39,10 @@ using content::BrowserThread;
using extensions::Extension;
namespace {
const uint32 kAccessFlags = base::kProcessAccessDuplicateHandle |
base::kProcessAccessQueryInformation |
base::kProcessAccessTerminate |
base::kProcessAccessWaitForTermination;
std::string TimeToString(base::Time time) {
int64 time_int64 = time.ToInternalValue();
......@@ -240,6 +247,90 @@ void PerformanceMonitor::NotifyInitialized() {
content::NotificationService::NoDetails());
}
void PerformanceMonitor::GatherStatisticsOnBackgroundThread() {
CHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
// Because the CPU usage is gathered as an average since the last time the
// function was called, while the memory usage is gathered as an instantaneous
// usage, the CPU usage is gathered before the metrics map is wiped.
GatherCPUUsageOnBackgroundThread();
UpdateMetricsMapOnBackgroundThread();
GatherMemoryUsageOnBackgroundThread();
}
void PerformanceMonitor::GatherCPUUsageOnBackgroundThread() {
if (metrics_map_.size()) {
double cpu_usage = 0;
for (MetricsMap::const_iterator iter = metrics_map_.begin();
iter != metrics_map_.end(); ++iter) {
cpu_usage += iter->second->GetCPUUsage();
}
database_->AddMetric(METRIC_CPU_USAGE, base::DoubleToString(cpu_usage));
}
}
void PerformanceMonitor::GatherMemoryUsageOnBackgroundThread() {
size_t private_memory_sum = 0;
size_t shared_memory_sum = 0;
for (MetricsMap::const_iterator iter = metrics_map_.begin();
iter != metrics_map_.end(); ++iter) {
size_t private_memory = 0;
size_t shared_memory = 0;
if (iter->second->GetMemoryBytes(&private_memory, &shared_memory)) {
private_memory_sum += private_memory;
shared_memory_sum += shared_memory;
} else {
LOG(WARNING) << "GetMemoryBytes returned NULL (platform-specific error)";
}
}
database_->AddMetric(METRIC_PRIVATE_MEMORY_USAGE,
base::Uint64ToString(private_memory_sum));
database_->AddMetric(METRIC_SHARED_MEMORY_USAGE,
base::Uint64ToString(shared_memory_sum));
}
void PerformanceMonitor::UpdateMetricsMapOnBackgroundThread() {
// Remove old process handles. Use two iterators to safely call erase() on the
// current element.
for (MetricsMap::iterator iter_next = metrics_map_.begin();
iter_next != metrics_map_.end();) {
MetricsMap::iterator iter = iter_next++;
if (base::GetTerminationStatus(iter->first, NULL) !=
base::TERMINATION_STATUS_STILL_RUNNING) {
base::CloseProcessHandle(iter->first);
metrics_map_.erase(iter);
}
}
// Add new process handles.
base::ProcessId browser_pid = base::GetCurrentProcId();
ChromeProcessList chrome_processes(GetRunningChromeProcesses(browser_pid));
for (ChromeProcessList::const_iterator pid_iter = chrome_processes.begin();
pid_iter != chrome_processes.end(); ++pid_iter) {
base::ProcessHandle process_handle;
if (base::OpenProcessHandleWithAccess(*pid_iter,
kAccessFlags,
&process_handle) &&
!ContainsKey(metrics_map_, process_handle)) {
#if defined(OS_MACOSX)
linked_ptr<base::ProcessMetrics> process_metrics(
base::ProcessMetrics::CreateProcessMetrics(process_handle,
content::BrowserChildProcessHost::GetPortProvider()));
#else
linked_ptr<base::ProcessMetrics> process_metrics(
base::ProcessMetrics::CreateProcessMetrics(process_handle));
#endif
// Prime the CPUUsage to be gathered next time.
process_metrics->GetCPUUsage();
metrics_map_[process_handle] = process_metrics;
}
}
}
void PerformanceMonitor::UpdateLiveProfiles() {
std::string time = TimeToString(base::Time::Now());
scoped_ptr<std::set<std::string> > active_profiles(
......
......@@ -5,12 +5,16 @@
#ifndef CHROME_BROWSER_PERFORMANCE_MONITOR_PERFORMANCE_MONITOR_H_
#define CHROME_BROWSER_PERFORMANCE_MONITOR_PERFORMANCE_MONITOR_H_
#include <map>
#include <string>
#include "base/callback.h"
#include "base/file_path.h"
#include "base/memory/linked_ptr.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/singleton.h"
#include "base/process.h"
#include "base/process_util.h"
#include "base/timer.h"
#include "chrome/browser/performance_monitor/database.h"
#include "chrome/browser/performance_monitor/event.h"
......@@ -26,6 +30,9 @@ class PerformanceMonitor : public content::NotificationObserver {
public:
typedef base::Callback<void(const std::string&)> StateValueCallback;
typedef std::map<base::ProcessHandle,
linked_ptr<base::ProcessMetrics> > MetricsMap;
// Set the path which the PerformanceMonitor should use for the database files
// constructed. This must be done prior to the initialization of the
// PerformanceMonitor. Returns true on success, false on failure (failure
......@@ -41,6 +48,9 @@ class PerformanceMonitor : public content::NotificationObserver {
// start collecting data.
void Start();
// Gathers CPU usage and memory usage of all Chrome processes.
void GatherStatisticsOnBackgroundThread();
// content::NotificationObserver
// Wait for various notifications; insert events into the database upon
// occurance.
......@@ -89,6 +99,16 @@ class PerformanceMonitor : public content::NotificationObserver {
void AddEventOnBackgroundThread(scoped_ptr<Event> event);
// Gathers the CPU usage of every Chrome process that has been running since
// the last call to GatherStatistics().
void GatherCPUUsageOnBackgroundThread();
// Gathers the memory usage of every process in the current list of processes.
void GatherMemoryUsageOnBackgroundThread();
// Updates the ProcessMetrics map with the current list of processes.
void UpdateMetricsMapOnBackgroundThread();
// Gets the corresponding value of |key| from the database, and then runs
// |callback| on the UI thread with that value as a parameter.
void GetStateValueOnBackgroundThread(
......@@ -113,6 +133,9 @@ class PerformanceMonitor : public content::NotificationObserver {
scoped_ptr<Database> database_;
// A map of currently running ProcessHandles to ProcessMetrics.
MetricsMap metrics_map_;
// The timer to signal PerformanceMonitor to perform its timed collections.
base::RepeatingTimer<PerformanceMonitor> timer_;
......
......@@ -20,7 +20,9 @@
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_navigator.h"
#include "chrome/browser/ui/browser_tabstrip.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_notification_types.h"
#include "chrome/common/chrome_paths.h"
......@@ -31,6 +33,7 @@
#include "chrome/test/base/ui_test_utils.h"
#include "content/public/browser/notification_registrar.h"
#include "content/public/browser/notification_service.h"
#include "content/public/common/page_transition_types.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/test_utils.h"
......@@ -38,6 +41,7 @@ using extensions::Extension;
using performance_monitor::Event;
namespace {
// Helper struct to store the information of an extension; this is needed if the
// pointer to the extension ever becomes invalid (e.g., if we uninstall the
// extension).
......@@ -148,6 +152,19 @@ class PerformanceMonitorBrowserTest : public ExtensionBrowserTest {
windowed_observer.Wait();
}
// A handle for gathering statistics from the database, which must be done on
// the background thread. Since we are testing, we can mock synchronicity with
// FlushForTesting().
void GatherStatistics() {
content::BrowserThread::PostBlockingPoolSequencedTask(
Database::kDatabaseSequenceToken,
FROM_HERE,
base::Bind(&PerformanceMonitor::GatherStatisticsOnBackgroundThread,
base::Unretained(performance_monitor())));
content::BrowserThread::GetBlockingPool()->FlushForTesting();
}
void GetEventsOnBackgroundThread(std::vector<linked_ptr<Event> >* events) {
// base::Time is potentially flaky in that there is no guarantee that it
// won't actually decrease between successive calls. If we call GetEvents
......@@ -179,6 +196,31 @@ class PerformanceMonitorBrowserTest : public ExtensionBrowserTest {
return events;
}
void GetStatsOnBackgroundThread(Database::MetricInfoVector* metrics,
MetricType type) {
*metrics = performance_monitor_->database()->GetStatsForActivityAndMetric(
type, base::Time(), base::Time::FromInternalValue(kint64max));
}
// A handle for getting statistics from the database (see previous comments on
// GetEvents() and GetEventsOnBackgroundThread).
Database::MetricInfoVector GetStats(MetricType type) {
content::BrowserThread::GetBlockingPool()->FlushForTesting();
content::RunAllPendingInMessageLoop();
Database::MetricInfoVector metrics;
content::BrowserThread::PostBlockingPoolSequencedTask(
Database::kDatabaseSequenceToken,
FROM_HERE,
base::Bind(&PerformanceMonitorBrowserTest::GetStatsOnBackgroundThread,
base::Unretained(this),
&metrics,
type));
content::BrowserThread::GetBlockingPool()->FlushForTesting();
return metrics;
}
// A handle for inserting a state value into the database, which must be done
// on the background thread. This is useful for mocking up a scenario in which
// the database has prior data stored. We mock synchronicity with
......@@ -462,6 +504,45 @@ IN_PROC_BROWSER_TEST_F(PerformanceMonitorBrowserTest, NewVersionEvent) {
ASSERT_EQ(version_string, current_version);
}
IN_PROC_BROWSER_TEST_F(PerformanceMonitorBrowserTest, GatherStatistics) {
GatherStatistics();
// No stats should be recorded for this CPUUsage because this was the first
// call to GatherStatistics.
Database::MetricInfoVector stats = GetStats(METRIC_CPU_USAGE);
ASSERT_EQ(0u, stats.size());
stats = GetStats(METRIC_PRIVATE_MEMORY_USAGE);
ASSERT_EQ(1u, stats.size());
EXPECT_GT(stats[0].value, 0);
stats = GetStats(METRIC_SHARED_MEMORY_USAGE);
ASSERT_EQ(1u, stats.size());
EXPECT_GT(stats[0].value, 0);
// Open new tabs to incur CPU usage.
for (int i = 0; i < 3; ++i) {
chrome::NavigateParams params(browser(), GURL("http://www.google.com"),
content::PAGE_TRANSITION_LINK);
params.disposition = NEW_BACKGROUND_TAB;
ui_test_utils::NavigateToURL(&params);
}
GatherStatistics();
// One CPUUsage stat should exist now.
stats = GetStats(METRIC_CPU_USAGE);
ASSERT_EQ(1u, stats.size());
EXPECT_GT(stats[0].value, 0);
stats = GetStats(METRIC_PRIVATE_MEMORY_USAGE);
ASSERT_EQ(2u, stats.size());
EXPECT_GT(stats[1].value, 0);
stats = GetStats(METRIC_SHARED_MEMORY_USAGE);
ASSERT_EQ(2u, stats.size());
EXPECT_GT(stats[1].value, 0);
}
#if !defined(OS_WIN)
// Disabled on Windows due to a bug where Windows will return a normal exit
// code in the testing environment, even if the process died (this is not the
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment