1// Copyright 2013 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 <limits>
6
7#include "base/logging.h"
8#include "base/metrics/histogram.h"
9#include "base/process/process_metrics.h"
10
11#include "chrome/browser/performance_monitor/process_metrics_history.h"
12#if defined(OS_MACOSX)
13#include "content/public/browser/browser_child_process_host.h"
14#endif
15#include "content/public/common/process_type.h"
16
17namespace performance_monitor {
18
19// If a process is consistently above this CPU utilization percentage over time,
20// we consider it as high and may take action.
21const float kHighCPUUtilizationThreshold = 90.0f;
22
23ProcessMetricsHistory::ProcessMetricsHistory()
24    : process_handle_(0),
25      process_type_(content::PROCESS_TYPE_UNKNOWN),
26      last_update_sequence_(0) {
27  ResetCounters();
28}
29
30ProcessMetricsHistory::~ProcessMetricsHistory() {}
31
32void ProcessMetricsHistory::ResetCounters() {
33  min_cpu_usage_ = std::numeric_limits<double>::max();
34  accumulated_cpu_usage_ = 0.0;
35  accumulated_private_bytes_ = 0;
36  accumulated_shared_bytes_ = 0;
37  sample_count_ = 0;
38}
39
40void ProcessMetricsHistory::Initialize(base::ProcessHandle process_handle,
41                                       int process_type,
42                                       int initial_update_sequence) {
43  DCHECK(process_handle_ == 0);
44  process_handle_ = process_handle;
45  process_type_ = process_type;
46  last_update_sequence_ = initial_update_sequence;
47
48#if defined(OS_MACOSX)
49  process_metrics_.reset(base::ProcessMetrics::CreateProcessMetrics(
50      process_handle_, content::BrowserChildProcessHost::GetPortProvider()));
51#else
52  process_metrics_.reset(
53      base::ProcessMetrics::CreateProcessMetrics(process_handle_));
54#endif
55}
56
57void ProcessMetricsHistory::SampleMetrics() {
58  double cpu_usage = process_metrics_->GetPlatformIndependentCPUUsage();
59  min_cpu_usage_ = std::min(min_cpu_usage_, cpu_usage);
60  accumulated_cpu_usage_ += cpu_usage;
61
62  size_t private_bytes = 0;
63  size_t shared_bytes = 0;
64  if (!process_metrics_->GetMemoryBytes(&private_bytes, &shared_bytes))
65    LOG(WARNING) << "GetMemoryBytes returned NULL (platform-specific error)";
66
67  accumulated_private_bytes_ += private_bytes;
68  accumulated_shared_bytes_ += shared_bytes;
69
70  sample_count_++;
71}
72
73void ProcessMetricsHistory::EndOfCycle() {
74  RunPerformanceTriggers();
75  ResetCounters();
76}
77
78void ProcessMetricsHistory::RunPerformanceTriggers() {
79  if (sample_count_ == 0)
80    return;
81
82  // We scale up to the equivalent of 64 CPU cores fully loaded. More than this
83  // doesn't really matter, as we're already in a terrible place.
84  const int kHistogramMin = 0;
85  const int kHistogramMax = 6400;
86  const int kHistogramBucketCount = 50;
87
88  const double average_cpu_usage = accumulated_cpu_usage_ / sample_count_;
89
90  // The histogram macros don't support variables as histogram names,
91  // hence the macro duplication for each process type.
92  switch (process_type_) {
93    case content::PROCESS_TYPE_BROWSER:
94      UMA_HISTOGRAM_CUSTOM_COUNTS(
95          "PerformanceMonitor.AverageCPU.BrowserProcess", average_cpu_usage,
96          kHistogramMin, kHistogramMax, kHistogramBucketCount);
97      // If CPU usage has consistently been above our threshold,
98      // we *may* have an issue.
99      if (min_cpu_usage_ > kHighCPUUtilizationThreshold) {
100        UMA_HISTOGRAM_BOOLEAN("PerformanceMonitor.HighCPU.BrowserProcess",
101                              true);
102      }
103      break;
104    case content::PROCESS_TYPE_RENDERER:
105      UMA_HISTOGRAM_CUSTOM_COUNTS(
106          "PerformanceMonitor.AverageCPU.RendererProcess", average_cpu_usage,
107          kHistogramMin, kHistogramMax, kHistogramBucketCount);
108      if (min_cpu_usage_ > kHighCPUUtilizationThreshold) {
109        UMA_HISTOGRAM_BOOLEAN("PerformanceMonitor.HighCPU.RendererProcess",
110                              true);
111      }
112      break;
113    case content::PROCESS_TYPE_PLUGIN:
114      UMA_HISTOGRAM_CUSTOM_COUNTS(
115          "PerformanceMonitor.AverageCPU.PluginProcess", average_cpu_usage,
116          kHistogramMin, kHistogramMax, kHistogramBucketCount);
117      if (min_cpu_usage_ > kHighCPUUtilizationThreshold)
118        UMA_HISTOGRAM_BOOLEAN("PerformanceMonitor.HighCPU.PluginProcess", true);
119      break;
120    case content::PROCESS_TYPE_GPU:
121      UMA_HISTOGRAM_CUSTOM_COUNTS(
122          "PerformanceMonitor.AverageCPU.GPUProcess", average_cpu_usage,
123          kHistogramMin, kHistogramMax, kHistogramBucketCount);
124      if (min_cpu_usage_ > kHighCPUUtilizationThreshold)
125        UMA_HISTOGRAM_BOOLEAN("PerformanceMonitor.HighCPU.GPUProcess", true);
126      break;
127    case content::PROCESS_TYPE_PPAPI_PLUGIN:
128      UMA_HISTOGRAM_CUSTOM_COUNTS(
129          "PerformanceMonitor.AverageCPU.PPAPIProcess", average_cpu_usage,
130          kHistogramMin, kHistogramMax, kHistogramBucketCount);
131      if (min_cpu_usage_ > kHighCPUUtilizationThreshold)
132        UMA_HISTOGRAM_BOOLEAN("PerformanceMonitor.HighCPU.PPAPIProcess", true);
133      break;
134    default:
135      break;
136  }
137}
138
139}  // namespace performance_monitor
140