Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(97)

Side by Side Diff: chrome/browser/chromeos/resource_reporter/resource_reporter.cc

Issue 1374283003: Reporting top cpu and memory consumers via rappor on chromeos (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: mpearson's comments Created 5 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2015 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/chromeos/resource_reporter/resource_reporter.h"
6
7 #include <stdint.h>
8
9 #include <queue>
10
11 #include "base/bind.h"
12 #include "base/memory/singleton.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/sys_info.h"
15 #include "chrome/browser/browser_process.h"
16 #include "chrome/browser/task_management/task_manager_interface.h"
17 #include "components/rappor/rappor_service.h"
18 #include "content/public/browser/browser_thread.h"
19
20 namespace chromeos {
21
22 namespace {
23
24 // The task manager refresh interval, currently at 1 minute.
25 const int64_t kRefreshIntervalSeconds = 60;
26
27 // 1 GB in bytes.
28 const int64_t kMemory1GB = 1024 * 1024 * 1024;
29
30 // 800 MB in bytes.
31 const int64_t kMemory800MB = 800 * 1024 * 1024;
32
33 // 600 MB in bytes.
34 const int64_t kMemory600MB = 600 * 1024 * 1024;
35
36 // 400 MB in bytes.
37 const int64_t kMemory400MB = 400 * 1024 * 1024;
38
39 // 200 MB in bytes.
40 const int64_t kMemory200MB = 200 * 1024 * 1024;
41
42 // The name of the Rappor metric to report the CPU usage.
43 const char kCpuRapporMetric[] = "ResourceReporter.Cpu";
44
45 // The name of the Rappor metric to report the memory usage.
46 const char kMemoryRapporMetric[] = "ResourceReporter.Memory";
47
48 // The name of the string field of the Rappor metrics in which we'll record the
49 // task's Rappor sample name.
50 const char kRapporTaskStringField[] = "task";
51
52 // The name of the flags field of the Rappor metrics in which we'll store the
53 // priority of the process on which the task is running.
54 const char kRapporPriorityFlagsField[] = "priority";
55
56 // The name of the flags field of the CPU usage Rappor metrics in which we'll
57 // record the number of cores in the current system.
58 const char kRapporNumCoresRangeFlagsField[] = "num_cores_range";
59
60 // The name of the flags field of the Rappor metrics in which we'll store the
61 // CPU / memory usage ranges.
62 const char kRapporUsageRangeFlagsField[] = "usage_range";
63
64 // A functor to sort the TaskRecords by their |cpu|.
65 struct TaskRecordByCpuSorter {
66 bool operator()(ResourceReporter::TaskRecord* const& lhs,
67 ResourceReporter::TaskRecord* const& rhs) const {
68 if (lhs->cpu == rhs->cpu)
69 return lhs->id < rhs->id;
70 return lhs->cpu < rhs->cpu;
71 }
72 };
73
74 // A functor to sort the TaskRecords by their |memory|.
75 struct TaskRecordByMemorySorter {
76 bool operator()(ResourceReporter::TaskRecord* const& lhs,
77 ResourceReporter::TaskRecord* const& rhs) const {
78 if (lhs->memory == rhs->memory)
79 return lhs->id < rhs->id;
80 return lhs->memory < rhs->memory;
81 }
82 };
83
84 } // namespace
85
86 ResourceReporter::TaskRecord::TaskRecord(task_management::TaskId the_id)
87 : id(the_id), cpu(0.0), memory(0), is_background(false) {
88 }
89
90 ResourceReporter::TaskRecord::TaskRecord(task_management::TaskId the_id,
91 const std::string& task_name,
92 double the_cpu,
93 int64_t the_memory,
94 bool background)
95 : id(the_id),
96 task_name_for_rappor(task_name),
97 cpu(the_cpu),
98 memory(the_memory),
99 is_background(background) {
100 }
101
102 ResourceReporter::~ResourceReporter() {
103 }
104
105 // static
106 ResourceReporter* ResourceReporter::GetInstance() {
107 return base::Singleton<ResourceReporter>::get();
108 }
109
110 // static
111 void ResourceReporter::StartObservingMetricsService() {
112 auto metrics_service = g_browser_process->metrics_service();
113 DCHECK(metrics_service);
114 metrics_service->AddObserver(ResourceReporter::GetInstance());
115 }
116
117 // static
118 void ResourceReporter::StopObservingMetricsService() {
119 // In case we were still monitoring the task manager, we must stop monitoring
120 // here, since we stop observing the MetricService we won't be able to
121 // receive any notifications from it when it actually stops.
122 ResourceReporter::GetInstance()->StopMonitoring();
123 auto metrics_service = g_browser_process->metrics_service();
124 DCHECK(metrics_service);
125 metrics_service->RemoveObserver(ResourceReporter::GetInstance());
126 }
127
128 void ResourceReporter::OnTaskAdded(task_management::TaskId id) {
129 // Ignore this event.
130 }
131
132 void ResourceReporter::OnTaskToBeRemoved(task_management::TaskId id) {
133 auto itr = task_records_.find(id);
134 if (itr == task_records_.end())
135 return;
136
137 // Must be erased from the sorted set first.
138 // Note: this could mean that the sorted records are now less than
139 // |kTopConsumerCount| with other records in |task_records_| that can be
140 // added now. That's ok, we ignore this case.
141 auto cpu_itr = std::find(task_records_by_cpu_.begin(),
142 task_records_by_cpu_.end(),
143 itr->second.get());
144 if (cpu_itr != task_records_by_cpu_.end())
145 task_records_by_cpu_.erase(cpu_itr);
146
147 auto memory_itr = std::find(task_records_by_memory_.begin(),
148 task_records_by_memory_.end(),
149 itr->second.get());
150 if (memory_itr != task_records_by_memory_.end())
151 task_records_by_memory_.erase(memory_itr);
152
153 task_records_.erase(itr);
154 }
155
156 void ResourceReporter::OnTasksRefreshed(
157 const task_management::TaskIdList& task_ids) {
158 // A priority queue to sort the task records by their |cpu|. Greatest |cpu|
159 // first.
160 std::priority_queue<TaskRecord*,
161 std::vector<TaskRecord*>,
162 TaskRecordByCpuSorter> records_by_cpu_queue;
163 // A priority queue to sort the task records by their |memory|. Greatest
164 // |memory| first.
165 std::priority_queue<TaskRecord*,
166 std::vector<TaskRecord*>,
167 TaskRecordByMemorySorter> records_by_memory_queue;
168 task_records_by_cpu_.clear();
169 task_records_by_cpu_.reserve(kTopConsumersCount);
170 task_records_by_memory_.clear();
171 task_records_by_memory_.reserve(kTopConsumersCount);
172
173 for (const auto& id : task_ids) {
174 const double cpu_usage = observed_task_manager()->GetCpuUsage(id);
175 const int64_t memory_usage =
176 observed_task_manager()->GetPhysicalMemoryUsage(id);
177
178 // Browser and GPU processes are reported later using UMA histograms as they
179 // don't have any privacy issues.
180 const auto task_type = observed_task_manager()->GetType(id);
181 switch (task_type) {
182 case task_management::Task::UNKNOWN:
183 case task_management::Task::ZYGOTE:
184 break;
185
186 case task_management::Task::BROWSER:
187 last_browser_process_cpu_ = cpu_usage;
188 last_browser_process_memory_ = memory_usage != -1 ? memory_usage : 0;
189 break;
190
191 case task_management::Task::GPU:
192 last_gpu_process_cpu_ = cpu_usage;
193 last_gpu_process_memory_ = memory_usage != -1 ? memory_usage : 0;
194 break;
195
196 default:
197 // Other tasks types will be reported using Rappor.
198 TaskRecord* task_data = nullptr;
199 auto itr = task_records_.find(id);
200 if (itr == task_records_.end()) {
201 task_data = new TaskRecord(id);
202 task_records_[id] = make_scoped_ptr(task_data);
203 } else {
204 task_data = itr->second.get();
205 }
206
207 DCHECK_EQ(task_data->id, id);
208 task_data->task_name_for_rappor =
209 observed_task_manager()->GetTaskNameForRappor(id);
210 task_data->cpu = cpu_usage;
211 task_data->memory = memory_usage;
212 task_data->is_background =
213 observed_task_manager()->IsTaskOnBackgroundedProcess(id);
214
215 // Push only valid or useful data to both priority queues. They might
216 // end up having more records than |kTopConsumerCount|, that's fine.
217 // We'll take care of that next.
218 if (task_data->cpu > 0)
219 records_by_cpu_queue.push(task_data);
220 if (task_data->memory > 0)
221 records_by_memory_queue.push(task_data);
222 }
223 }
224
225 while (!records_by_cpu_queue.empty() &&
226 task_records_by_cpu_.size() < kTopConsumersCount) {
227 task_records_by_cpu_.push_back(records_by_cpu_queue.top());
228 records_by_cpu_queue.pop();
229 }
230
231 while (!records_by_memory_queue.empty() &&
232 task_records_by_memory_.size() < kTopConsumersCount) {
233 task_records_by_memory_.push_back(records_by_memory_queue.top());
234 records_by_memory_queue.pop();
235 }
236 }
237
238 void ResourceReporter::OnMetricsServiceStart() {
239 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
240
241 StartMonitoring();
242 }
243
244 void ResourceReporter::OnMetricsServiceStop() {
245 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
246
247 StopMonitoring();
248 }
249
250 // static
251 const size_t ResourceReporter::kTopConsumersCount = 10U;
252
253 ResourceReporter::ResourceReporter()
254 : TaskManagerObserver(base::TimeDelta::FromSeconds(kRefreshIntervalSeconds),
255 task_management::REFRESH_TYPE_CPU |
256 task_management::REFRESH_TYPE_MEMORY),
257 system_cpu_cores_range_(GetCurrentSystemCpuCoresRange()),
258 is_monitoring_(false) {
259 }
260
261 // static
262 scoped_ptr<rappor::Sample> ResourceReporter::CreateRapporSample(
263 rappor::RapporService* rappor_service,
264 const ResourceReporter::TaskRecord& task_record) {
265 scoped_ptr<rappor::Sample> sample(rappor_service->CreateSample(
266 rappor::UMA_RAPPOR_TYPE));
267 sample->SetStringField(kRapporTaskStringField,
268 task_record.task_name_for_rappor);
269 sample->SetFlagsField(kRapporPriorityFlagsField,
270 task_record.is_background ? BACKGROUND : FOREGROUND,
271 PRIORITIES_NUM);
272 return sample.Pass();
273 }
274
275 // static
276 ResourceReporter::CpuUsageRange
277 ResourceReporter::GetCpuUsageRange(double cpu) {
278 if (cpu > 60.0)
279 return RANGE_ABOVE_60_PERCENT;
280 if (cpu > 30.0)
281 return RANGE_30_TO_60_PERCENT;
282 if (cpu > 10.0)
283 return RANGE_10_TO_30_PERCENT;
284
285 return RANGE_0_TO_10_PERCENT;
286 }
287
288 // static
289 ResourceReporter::MemoryUsageRange
290 ResourceReporter::GetMemoryUsageRange(int64_t memory_in_bytes) {
291 if (memory_in_bytes > kMemory1GB)
292 return RANGE_ABOVE_1_GB;
293 if (memory_in_bytes > kMemory800MB)
294 return RANGE_800_TO_1_GB;
295 if (memory_in_bytes > kMemory600MB)
296 return RANGE_600_TO_800_MB;
297 if (memory_in_bytes > kMemory400MB)
298 return RANGE_400_TO_600_MB;
299 if (memory_in_bytes > kMemory200MB)
300 return RANGE_200_TO_400_MB;
301
302 return RANGE_0_TO_200_MB;
303 }
304
305 // static
306 ResourceReporter::CpuCoresNumberRange
307 ResourceReporter::GetCurrentSystemCpuCoresRange() {
308 const int cpus = base::SysInfo::NumberOfProcessors();
309
310 if (cpus > 16)
311 return RANGE_CORES_ABOVE_16_CORES;
312 if (cpus > 8)
313 return RANGE_CORES_9_TO_16_CORES;
314 if (cpus > 4)
315 return RANGE_CORES_5_TO_8_CORES;
316 if (cpus > 2)
317 return RANGE_CORES_3_TO_4_CORES;
318 if (cpus == 2)
319 return RANGE_CORES_2_CORES;
320 if (cpus == 1)
321 return RANGE_CORES_1_CORE;
322
323 NOTREACHED();
324 return RANGE_CORES_NA;
325 }
326
327 void ResourceReporter::StartMonitoring() {
328 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
329
330 if (is_monitoring_)
331 return;
332
333 is_monitoring_ = true;
334 task_management::TaskManagerInterface::GetTaskManager()->AddObserver(this);
335 memory_pressure_listener_.reset(new base::MemoryPressureListener(
336 base::Bind(&ResourceReporter::OnMemoryPressure, base::Unretained(this))));
337 }
338
339 void ResourceReporter::StopMonitoring() {
340 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
341
342 if (!is_monitoring_)
343 return;
344
345 is_monitoring_ = false;
346 memory_pressure_listener_.reset();
347 task_management::TaskManagerInterface::GetTaskManager()->RemoveObserver(this);
348 }
349
350 void ResourceReporter::OnMemoryPressure(
351 base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level) {
352 // TODO(afakhry): Double check if we will ever receive a notification when
353 // we don't have memory pressure at all. The target is to report only once
354 // per each level value.
355 if (memory_pressure_level >=
356 base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE &&
357 memory_pressure_level > previous_memory_pressure_level_) {
358 // Report browser and GPU processes usage using UMA histograms.
359 UMA_HISTOGRAM_ENUMERATION("ResourceReporter.BrowserProcess.CpuUsage",
360 GetCpuUsageRange(last_browser_process_cpu_),
361 CPU_RANGES_NUM);
362 UMA_HISTOGRAM_ENUMERATION("ResourceReporter.BrowserProcess.MemoryUsage",
363 GetMemoryUsageRange(last_browser_process_memory_),
364 MEMORY_RANGES_NUM);
365 UMA_HISTOGRAM_ENUMERATION("ResourceReporter.GpuProcess.CpuUsage",
366 GetCpuUsageRange(last_gpu_process_cpu_),
367 CPU_RANGES_NUM);
368 UMA_HISTOGRAM_ENUMERATION("ResourceReporter.GpuProcess.MemoryUsage",
369 GetMemoryUsageRange(last_gpu_process_memory_),
370 MEMORY_RANGES_NUM);
371
372 // For the rest of tasks, report them using Rappor.
373 auto rappor_service = g_browser_process->rappor_service();
374
375 for (const auto& task_data : task_records_by_cpu_) {
376 scoped_ptr<rappor::Sample> sample(CreateRapporSample(rappor_service,
377 *task_data));
378 sample->SetFlagsField(kRapporNumCoresRangeFlagsField,
379 system_cpu_cores_range_,
380 CORES_RANGES_NUM);
381 sample->SetFlagsField(kRapporUsageRangeFlagsField,
382 GetCpuUsageRange(task_data->cpu),
383 CPU_RANGES_NUM);
384 rappor_service->RecordSampleObj(kCpuRapporMetric, sample.Pass());
385 }
386
387 for (const auto& task_data : task_records_by_memory_) {
388 scoped_ptr<rappor::Sample> sample(CreateRapporSample(rappor_service,
389 *task_data));
390 sample->SetFlagsField(kRapporUsageRangeFlagsField,
391 GetMemoryUsageRange(task_data->memory),
392 MEMORY_RANGES_NUM);
393 rappor_service->RecordSampleObj(kMemoryRapporMetric, sample.Pass());
394 }
395 }
396
397 previous_memory_pressure_level_ = memory_pressure_level;
398 }
399
400 } // namespace chromeos
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698