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