blob: 238e04636cf00664aa2514fcd5488e7b44514771 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/performance_monitor/performance_monitor.h"
#include <stddef.h>
#include <utility>
#include "base/memory/ptr_util.h"
#include "base/process/process_iterator.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "content/public/browser/browser_child_process_host.h"
#include "content/public/browser/browser_child_process_host_iterator.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/child_process_data.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/common/content_constants.h"
#if defined(ENABLE_EXTENSIONS)
#include "extensions/browser/extension_host.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/common/manifest_handlers/background_info.h"
#endif
using content::BrowserThread;
namespace performance_monitor {
namespace {
// The default interval at which PerformanceMonitor performs its timed
// collections.
const int kGatherIntervalInSeconds = 120;
base::LazyInstance<PerformanceMonitor> g_monitor = LAZY_INSTANCE_INITIALIZER;
void GatherMetricsForRenderProcess(content::RenderProcessHost* host,
ProcessMetricsMetadata* data) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
#if defined(ENABLE_EXTENSIONS)
content::BrowserContext* browser_context = host->GetBrowserContext();
extensions::ProcessMap* extension_process_map =
extensions::ProcessMap::Get(browser_context);
std::set<std::string> extension_ids =
extension_process_map->GetExtensionsInProcess(host->GetID());
// We only collect more granular metrics when there's only one extension
// running in a given renderer, to reduce noise.
if (extension_ids.size() != 1)
return;
extensions::ExtensionRegistry* extension_registry =
extensions::ExtensionRegistry::Get(browser_context);
const extensions::Extension* extension =
extension_registry->enabled_extensions().GetByID(*extension_ids.begin());
if (!extension)
return;
data->process_subtype =
extensions::BackgroundInfo::HasPersistentBackgroundPage(extension)
? kProcessSubtypeExtensionPersistent
: kProcessSubtypeExtensionEvent;
#endif
}
} // namespace
PerformanceMonitor::PerformanceMonitor() {}
PerformanceMonitor::~PerformanceMonitor() {}
// static
PerformanceMonitor* PerformanceMonitor::GetInstance() {
return g_monitor.Pointer();
}
void PerformanceMonitor::StartGatherCycle() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
repeating_timer_.Start(FROM_HERE,
base::TimeDelta::FromSeconds(kGatherIntervalInSeconds),
this, &PerformanceMonitor::GatherMetricsMapOnUIThread);
}
void PerformanceMonitor::GatherMetricsMapOnUIThread() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
static int current_update_sequence = 0;
// Even in the "somewhat" unlikely event this wraps around,
// it doesn't matter. We just check it for inequality.
current_update_sequence++;
// Find all render child processes; has to be done on the UI thread.
for (content::RenderProcessHost::iterator rph_iter =
content::RenderProcessHost::AllHostsIterator();
!rph_iter.IsAtEnd(); rph_iter.Advance()) {
content::RenderProcessHost* host = rph_iter.GetCurrentValue();
ProcessMetricsMetadata data;
data.process_type = content::PROCESS_TYPE_RENDERER;
data.handle = host->GetHandle();
GatherMetricsForRenderProcess(host, &data);
MarkProcessAsAlive(data, current_update_sequence);
}
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
base::Bind(&PerformanceMonitor::GatherMetricsMapOnIOThread,
base::Unretained(this), current_update_sequence));
}
void PerformanceMonitor::MarkProcessAsAlive(
const ProcessMetricsMetadata& process_data,
int current_update_sequence) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
const base::ProcessHandle& handle = process_data.handle;
if (handle == base::kNullProcessHandle) {
// Process may not be valid yet.
return;
}
MetricsMap::iterator process_metrics_iter = metrics_map_.find(handle);
if (process_metrics_iter == metrics_map_.end()) {
// If we're not already watching the process, let's initialize it.
metrics_map_[handle] = base::MakeUnique<ProcessMetricsHistory>();
metrics_map_[handle]->Initialize(process_data, current_update_sequence);
} else {
// If we are watching the process, touch it to keep it alive.
ProcessMetricsHistory* process_metrics = process_metrics_iter->second.get();
process_metrics->set_last_update_sequence(current_update_sequence);
}
}
void PerformanceMonitor::GatherMetricsMapOnIOThread(
int current_update_sequence) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
auto process_data_list =
base::MakeUnique<std::vector<ProcessMetricsMetadata>>();
// Find all child processes (does not include renderers), which has to be
// done on the IO thread.
for (content::BrowserChildProcessHostIterator iter; !iter.Done(); ++iter) {
ProcessMetricsMetadata child_process_data;
child_process_data.handle = iter.GetData().handle;
child_process_data.process_type = iter.GetData().process_type;
if (iter.GetData().name == base::ASCIIToUTF16(content::kFlashPluginName)) {
child_process_data.process_subtype = kProcessSubtypePPAPIFlash;
}
process_data_list->push_back(child_process_data);
}
// Add the current (browser) process.
ProcessMetricsMetadata browser_process_data;
browser_process_data.process_type = content::PROCESS_TYPE_BROWSER;
browser_process_data.handle = base::GetCurrentProcessHandle();
process_data_list->push_back(browser_process_data);
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::Bind(&PerformanceMonitor::MarkProcessesAsAliveOnUIThread,
base::Unretained(this),
base::Passed(std::move(process_data_list)),
current_update_sequence));
}
void PerformanceMonitor::MarkProcessesAsAliveOnUIThread(
std::unique_ptr<std::vector<ProcessMetricsMetadata>> process_data_list,
int current_update_sequence) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
for (const ProcessMetricsMetadata& data : *process_data_list)
MarkProcessAsAlive(data, current_update_sequence);
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
base::Bind(&PerformanceMonitor::UpdateMetricsOnIOThread,
base::Unretained(this), current_update_sequence));
}
void PerformanceMonitor::UpdateMetricsOnIOThread(int current_update_sequence) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
// Update metrics for all watched processes; remove dead entries from the map.
MetricsMap::iterator iter = metrics_map_.begin();
while (iter != metrics_map_.end()) {
ProcessMetricsHistory* process_metrics = iter->second.get();
if (process_metrics->last_update_sequence() != current_update_sequence) {
// Not touched this iteration; let's get rid of it.
metrics_map_.erase(iter++);
} else {
process_metrics->SampleMetrics();
++iter;
}
}
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
base::Bind(&PerformanceMonitor::RunTriggersUIThread,
base::Unretained(this)));
}
void PerformanceMonitor::RunTriggersUIThread() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
for (auto& metrics : metrics_map_)
metrics.second->RunPerformanceTriggers();
StartGatherCycle();
}
} // namespace performance_monitor