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

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

Powered by Google App Engine
This is Rietveld 408576698