1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/performance_monitor/performance_monitor.h"
6
7#include "base/memory/singleton.h"
8#include "base/process/process_iterator.h"
9#include "base/time/time.h"
10#include "content/public/browser/browser_child_process_host.h"
11#include "content/public/browser/browser_child_process_host_iterator.h"
12#include "content/public/browser/browser_thread.h"
13#include "content/public/browser/child_process_data.h"
14#include "content/public/browser/render_process_host.h"
15
16using content::BrowserThread;
17
18namespace {
19
20// The default interval at which PerformanceMonitor performs its timed
21// collections.
22const int kGatherIntervalInSeconds = 120;
23}
24
25namespace performance_monitor {
26
27PerformanceMonitor::PerformanceMonitor() {
28}
29
30PerformanceMonitor::~PerformanceMonitor() {
31}
32
33// static
34PerformanceMonitor* PerformanceMonitor::GetInstance() {
35  return Singleton<PerformanceMonitor>::get();
36}
37
38void PerformanceMonitor::StartGatherCycle() {
39  DCHECK_CURRENTLY_ON(BrowserThread::UI);
40  repeating_timer_.Start(FROM_HERE,
41                         base::TimeDelta::FromSeconds(kGatherIntervalInSeconds),
42                         this,
43                         &PerformanceMonitor::GatherMetricsMapOnUIThread);
44}
45
46void PerformanceMonitor::GatherMetricsMapOnUIThread() {
47  DCHECK_CURRENTLY_ON(BrowserThread::UI);
48
49  static int current_update_sequence = 0;
50  // Even in the "somewhat" unlikely event this wraps around,
51  // it doesn't matter. We just check it for inequality.
52  current_update_sequence++;
53
54  // Find all render child processes; has to be done on the UI thread.
55  for (content::RenderProcessHost::iterator rph_iter =
56           content::RenderProcessHost::AllHostsIterator();
57       !rph_iter.IsAtEnd(); rph_iter.Advance()) {
58    base::ProcessHandle handle = rph_iter.GetCurrentValue()->GetHandle();
59    MarkProcessAsAlive(handle, content::PROCESS_TYPE_RENDERER,
60                       current_update_sequence);
61  }
62
63  BrowserThread::PostTask(
64      BrowserThread::IO,
65      FROM_HERE,
66      base::Bind(&PerformanceMonitor::GatherMetricsMapOnIOThread,
67                 base::Unretained(this),
68                 current_update_sequence));
69}
70
71void PerformanceMonitor::MarkProcessAsAlive(const base::ProcessHandle& handle,
72                                            int process_type,
73                                            int current_update_sequence) {
74  if (handle == 0) {
75    // Process may not be valid yet.
76    return;
77  }
78
79  MetricsMap::iterator process_metrics_iter = metrics_map_.find(handle);
80  if (process_metrics_iter == metrics_map_.end()) {
81    // If we're not already watching the process, let's initialize it.
82    metrics_map_[handle]
83        .Initialize(handle, process_type, current_update_sequence);
84  } else {
85    // If we are watching the process, touch it to keep it alive.
86    ProcessMetricsHistory& process_metrics = process_metrics_iter->second;
87    process_metrics.set_last_update_sequence(current_update_sequence);
88  }
89}
90
91void PerformanceMonitor::GatherMetricsMapOnIOThread(
92    int current_update_sequence) {
93  DCHECK_CURRENTLY_ON(BrowserThread::IO);
94
95  // Find all child processes (does not include renderers), which has to be
96  // done on the IO thread.
97  for (content::BrowserChildProcessHostIterator iter; !iter.Done(); ++iter) {
98    const content::ChildProcessData& child_process_data = iter.GetData();
99    base::ProcessHandle handle = child_process_data.handle;
100    MarkProcessAsAlive(handle, child_process_data.process_type,
101                       current_update_sequence);
102  }
103
104  // Add the current (browser) process.
105  MarkProcessAsAlive(base::GetCurrentProcessHandle(),
106                     content::PROCESS_TYPE_BROWSER, current_update_sequence);
107
108  double cpu_usage = 0.0;
109  size_t private_memory_sum = 0;
110  size_t shared_memory_sum = 0;
111
112  // Update metrics for all watched processes; remove dead entries from the map.
113  MetricsMap::iterator iter = metrics_map_.begin();
114  while (iter != metrics_map_.end()) {
115    ProcessMetricsHistory& process_metrics = iter->second;
116    if (process_metrics.last_update_sequence() != current_update_sequence) {
117      // Not touched this iteration; let's get rid of it.
118      metrics_map_.erase(iter++);
119    } else {
120      process_metrics.SampleMetrics();
121
122      // Gather averages of previously sampled metrics.
123      cpu_usage += process_metrics.GetAverageCPUUsage();
124
125      size_t private_memory = 0;
126      size_t shared_memory = 0;
127      process_metrics.GetAverageMemoryBytes(&private_memory, &shared_memory);
128      private_memory_sum += private_memory;
129      shared_memory_sum += shared_memory;
130
131      process_metrics.EndOfCycle();
132
133      ++iter;
134    }
135  }
136}
137
138}  // namespace performance_monitor
139