OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/memory_details.h" | 5 #include "chrome/browser/memory_details.h" |
6 | 6 |
7 #include <algorithm> | |
8 #include <set> | |
Ilya Sherman
2015/01/16 04:57:36
nit: Are these just fixing pre-existing IWYU viola
Alexei Svitkine (slow)
2015/01/16 16:07:49
Yep, as suggested by git cl lint.
| |
9 | |
7 #include "base/bind.h" | 10 #include "base/bind.h" |
8 #include "base/file_version_info.h" | 11 #include "base/file_version_info.h" |
9 #include "base/metrics/histogram.h" | 12 #include "base/metrics/histogram.h" |
10 #include "base/strings/string_util.h" | 13 #include "base/strings/string_util.h" |
11 #include "base/strings/stringprintf.h" | 14 #include "base/strings/stringprintf.h" |
12 #include "base/strings/utf_string_conversions.h" | 15 #include "base/strings/utf_string_conversions.h" |
13 #include "chrome/browser/profiles/profile.h" | 16 #include "chrome/browser/profiles/profile.h" |
14 #include "chrome/common/url_constants.h" | 17 #include "chrome/common/url_constants.h" |
15 #include "chrome/grit/generated_resources.h" | 18 #include "chrome/grit/generated_resources.h" |
16 #include "components/nacl/common/nacl_process_type.h" | 19 #include "components/nacl/common/nacl_process_type.h" |
(...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
107 | 110 |
108 ProcessData::~ProcessData() {} | 111 ProcessData::~ProcessData() {} |
109 | 112 |
110 ProcessData& ProcessData::operator=(const ProcessData& rhs) { | 113 ProcessData& ProcessData::operator=(const ProcessData& rhs) { |
111 name = rhs.name; | 114 name = rhs.name; |
112 process_name = rhs.process_name; | 115 process_name = rhs.process_name; |
113 processes = rhs.processes; | 116 processes = rhs.processes; |
114 return *this; | 117 return *this; |
115 } | 118 } |
116 | 119 |
117 MemoryGrowthTracker::MemoryGrowthTracker() {} | |
118 | |
119 MemoryGrowthTracker::~MemoryGrowthTracker() {} | |
120 | |
121 bool MemoryGrowthTracker::UpdateSample( | |
122 base::ProcessId pid, | |
123 int sample, | |
124 int* diff) { | |
125 // |sample| is memory usage in kB. | |
126 const base::TimeTicks current_time = base::TimeTicks::Now(); | |
127 std::map<base::ProcessId, int>::iterator found_size = memory_sizes_.find(pid); | |
128 if (found_size != memory_sizes_.end()) { | |
129 const int last_size = found_size->second; | |
130 std::map<base::ProcessId, base::TimeTicks>::iterator found_time = | |
131 times_.find(pid); | |
132 const base::TimeTicks last_time = found_time->second; | |
133 if (last_time < (current_time - base::TimeDelta::FromMinutes(30))) { | |
134 // Note that it is undefined how division of a negative integer gets | |
135 // rounded. |*diff| may have a difference of 1 from the correct number | |
136 // if |sample| < |last_size|. We ignore it as 1 is small enough. | |
137 *diff = ((sample - last_size) * 30 / | |
138 (current_time - last_time).InMinutes()); | |
139 found_size->second = sample; | |
140 found_time->second = current_time; | |
141 return true; | |
142 } | |
143 // Skip if a last record is found less than 30 minutes ago. | |
144 } else { | |
145 // Not reporting if it's the first record for |pid|. | |
146 times_[pid] = current_time; | |
147 memory_sizes_[pid] = sample; | |
148 } | |
149 return false; | |
150 } | |
151 | |
152 // About threading: | 120 // About threading: |
153 // | 121 // |
154 // This operation will hit no fewer than 3 threads. | 122 // This operation will hit no fewer than 3 threads. |
155 // | 123 // |
156 // The BrowserChildProcessHostIterator can only be accessed from the IO thread. | 124 // The BrowserChildProcessHostIterator can only be accessed from the IO thread. |
157 // | 125 // |
158 // The RenderProcessHostIterator can only be accessed from the UI thread. | 126 // The RenderProcessHostIterator can only be accessed from the UI thread. |
159 // | 127 // |
160 // This operation can take 30-100ms to complete. We never want to have | 128 // This operation can take 30-100ms to complete. We never want to have |
161 // one task run for that long on the UI or IO threads. So, we run the | 129 // one task run for that long on the UI or IO threads. So, we run the |
162 // expensive parts of this operation over on the file thread. | 130 // expensive parts of this operation over on the file thread. |
163 // | 131 // |
164 void MemoryDetails::StartFetch(UserMetricsMode user_metrics_mode) { | 132 void MemoryDetails::StartFetch() { |
165 // This might get called from the UI or FILE threads, but should not be | 133 // This might get called from the UI or FILE threads, but should not be |
166 // getting called from the IO thread. | 134 // getting called from the IO thread. |
167 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::IO)); | 135 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::IO)); |
168 user_metrics_mode_ = user_metrics_mode; | |
169 | 136 |
170 // In order to process this request, we need to use the plugin information. | 137 // In order to process this request, we need to use the plugin information. |
171 // However, plugin process information is only available from the IO thread. | 138 // However, plugin process information is only available from the IO thread. |
172 BrowserThread::PostTask( | 139 BrowserThread::PostTask( |
173 BrowserThread::IO, FROM_HERE, | 140 BrowserThread::IO, FROM_HERE, |
174 base::Bind(&MemoryDetails::CollectChildInfoOnIOThread, this)); | 141 base::Bind(&MemoryDetails::CollectChildInfoOnIOThread, this)); |
175 } | 142 } |
176 | 143 |
177 MemoryDetails::~MemoryDetails() {} | 144 MemoryDetails::~MemoryDetails() {} |
178 | 145 |
(...skipping 26 matching lines...) Expand all Loading... | |
205 static_cast<int>(iter1->working_set.shared) / 1024); | 172 static_cast<int>(iter1->working_set.shared) / 1024); |
206 #if defined(OS_CHROMEOS) | 173 #if defined(OS_CHROMEOS) |
207 log += StringPrintf(", %d MB swapped", | 174 log += StringPrintf(", %d MB swapped", |
208 static_cast<int>(iter1->working_set.swapped) / 1024); | 175 static_cast<int>(iter1->working_set.swapped) / 1024); |
209 #endif | 176 #endif |
210 log += "\n"; | 177 log += "\n"; |
211 } | 178 } |
212 return log; | 179 return log; |
213 } | 180 } |
214 | 181 |
215 void MemoryDetails::SetMemoryGrowthTracker( | |
216 MemoryGrowthTracker* memory_growth_tracker) { | |
217 memory_growth_tracker_ = memory_growth_tracker; | |
218 } | |
219 | |
220 void MemoryDetails::CollectChildInfoOnIOThread() { | 182 void MemoryDetails::CollectChildInfoOnIOThread() { |
221 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 183 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
222 | 184 |
223 std::vector<ProcessMemoryInformation> child_info; | 185 std::vector<ProcessMemoryInformation> child_info; |
224 | 186 |
225 // Collect the list of child processes. A 0 |handle| means that | 187 // Collect the list of child processes. A 0 |handle| means that |
226 // the process is being launched, so we skip it. | 188 // the process is being launched, so we skip it. |
227 for (BrowserChildProcessHostIterator iter; !iter.Done(); ++iter) { | 189 for (BrowserChildProcessHostIterator iter; !iter.Done(); ++iter) { |
228 ProcessMemoryInformation info; | 190 ProcessMemoryInformation info; |
229 if (!iter.GetData().handle) | 191 if (!iter.GetData().handle) |
(...skipping 170 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
400 for (size_t index = 0; index < chrome_browser->processes.size(); | 362 for (size_t index = 0; index < chrome_browser->processes.size(); |
401 index++) { | 363 index++) { |
402 if (chrome_browser->processes[index].process_type == | 364 if (chrome_browser->processes[index].process_type == |
403 content::PROCESS_TYPE_UNKNOWN) { | 365 content::PROCESS_TYPE_UNKNOWN) { |
404 chrome_browser->processes.erase( | 366 chrome_browser->processes.erase( |
405 chrome_browser->processes.begin() + index); | 367 chrome_browser->processes.begin() + index); |
406 index--; | 368 index--; |
407 } | 369 } |
408 } | 370 } |
409 | 371 |
410 if (user_metrics_mode_ == UPDATE_USER_METRICS) | |
411 UpdateHistograms(); | |
412 | |
413 OnDetailsAvailable(); | 372 OnDetailsAvailable(); |
414 } | 373 } |
415 | |
416 void MemoryDetails::UpdateHistograms() { | |
417 // Reports a set of memory metrics to UMA. | |
418 // Memory is measured in KB. | |
419 | |
420 const ProcessData& browser = *ChromeBrowser(); | |
421 size_t aggregate_memory = 0; | |
422 int chrome_count = 0; | |
423 int extension_count = 0; | |
424 int plugin_count = 0; | |
425 int pepper_plugin_count = 0; | |
426 int pepper_plugin_broker_count = 0; | |
427 int renderer_count = 0; | |
428 int other_count = 0; | |
429 int worker_count = 0; | |
430 int process_limit = content::RenderProcessHost::GetMaxRendererProcessCount(); | |
431 for (size_t index = 0; index < browser.processes.size(); index++) { | |
432 int sample = static_cast<int>(browser.processes[index].working_set.priv); | |
433 aggregate_memory += sample; | |
434 switch (browser.processes[index].process_type) { | |
435 case content::PROCESS_TYPE_BROWSER: | |
436 UMA_HISTOGRAM_MEMORY_KB("Memory.Browser", sample); | |
437 continue; | |
438 case content::PROCESS_TYPE_RENDERER: { | |
439 ProcessMemoryInformation::RendererProcessType renderer_type = | |
440 browser.processes[index].renderer_type; | |
441 switch (renderer_type) { | |
442 case ProcessMemoryInformation::RENDERER_EXTENSION: | |
443 UMA_HISTOGRAM_MEMORY_KB("Memory.Extension", sample); | |
444 extension_count++; | |
445 continue; | |
446 case ProcessMemoryInformation::RENDERER_CHROME: | |
447 UMA_HISTOGRAM_MEMORY_KB("Memory.Chrome", sample); | |
448 chrome_count++; | |
449 continue; | |
450 case ProcessMemoryInformation::RENDERER_UNKNOWN: | |
451 NOTREACHED() << "Unknown renderer process type."; | |
452 continue; | |
453 case ProcessMemoryInformation::RENDERER_NORMAL: | |
454 default: | |
455 // TODO(erikkay): Should we bother splitting out the other subtypes? | |
456 UMA_HISTOGRAM_MEMORY_KB("Memory.Renderer", sample); | |
457 int diff; | |
458 if (memory_growth_tracker_ && | |
459 memory_growth_tracker_->UpdateSample( | |
460 browser.processes[index].pid, sample, &diff)) { | |
461 if (diff < 0) | |
462 UMA_HISTOGRAM_MEMORY_KB("Memory.RendererShrinkIn30Min", -diff); | |
463 else | |
464 UMA_HISTOGRAM_MEMORY_KB("Memory.RendererGrowthIn30Min", diff); | |
465 } | |
466 renderer_count++; | |
467 continue; | |
468 } | |
469 } | |
470 case content::PROCESS_TYPE_PLUGIN: | |
471 UMA_HISTOGRAM_MEMORY_KB("Memory.Plugin", sample); | |
472 plugin_count++; | |
473 continue; | |
474 case content::PROCESS_TYPE_UTILITY: | |
475 UMA_HISTOGRAM_MEMORY_KB("Memory.Utility", sample); | |
476 other_count++; | |
477 continue; | |
478 case content::PROCESS_TYPE_ZYGOTE: | |
479 UMA_HISTOGRAM_MEMORY_KB("Memory.Zygote", sample); | |
480 other_count++; | |
481 continue; | |
482 case content::PROCESS_TYPE_SANDBOX_HELPER: | |
483 UMA_HISTOGRAM_MEMORY_KB("Memory.SandboxHelper", sample); | |
484 other_count++; | |
485 continue; | |
486 case content::PROCESS_TYPE_GPU: | |
487 UMA_HISTOGRAM_MEMORY_KB("Memory.Gpu", sample); | |
488 other_count++; | |
489 continue; | |
490 #if defined(ENABLE_PLUGINS) | |
491 case content::PROCESS_TYPE_PPAPI_PLUGIN: | |
492 { | |
493 const std::vector<base::string16>& titles = | |
494 browser.processes[index].titles; | |
495 if (titles.size() == 1 && | |
496 titles[0] == base::ASCIIToUTF16(content::kFlashPluginName)) { | |
497 UMA_HISTOGRAM_MEMORY_KB("Memory.PepperFlashPlugin", sample); | |
498 } | |
499 } | |
500 UMA_HISTOGRAM_MEMORY_KB("Memory.PepperPlugin", sample); | |
501 pepper_plugin_count++; | |
502 continue; | |
503 case content::PROCESS_TYPE_PPAPI_BROKER: | |
504 UMA_HISTOGRAM_MEMORY_KB("Memory.PepperPluginBroker", sample); | |
505 pepper_plugin_broker_count++; | |
506 continue; | |
507 #endif | |
508 case PROCESS_TYPE_NACL_LOADER: | |
509 UMA_HISTOGRAM_MEMORY_KB("Memory.NativeClient", sample); | |
510 other_count++; | |
511 continue; | |
512 case PROCESS_TYPE_NACL_BROKER: | |
513 UMA_HISTOGRAM_MEMORY_KB("Memory.NativeClientBroker", sample); | |
514 other_count++; | |
515 continue; | |
516 default: | |
517 NOTREACHED(); | |
518 continue; | |
519 } | |
520 } | |
521 #if defined(OS_CHROMEOS) | |
522 // Chrome OS exposes system-wide graphics driver memory which has historically | |
523 // been a source of leak/bloat. | |
524 base::SystemMemoryInfoKB meminfo; | |
525 if (base::GetSystemMemoryInfo(&meminfo) && meminfo.gem_size != -1) | |
526 UMA_HISTOGRAM_MEMORY_MB("Memory.Graphics", meminfo.gem_size / 1024 / 1024); | |
527 #endif | |
528 | |
529 UMA_HISTOGRAM_COUNTS_100("Memory.ProcessLimit", process_limit); | |
530 UMA_HISTOGRAM_COUNTS_100("Memory.ProcessCount", | |
531 static_cast<int>(browser.processes.size())); | |
532 UMA_HISTOGRAM_COUNTS_100("Memory.ChromeProcessCount", chrome_count); | |
533 UMA_HISTOGRAM_COUNTS_100("Memory.ExtensionProcessCount", extension_count); | |
534 UMA_HISTOGRAM_COUNTS_100("Memory.OtherProcessCount", other_count); | |
535 UMA_HISTOGRAM_COUNTS_100("Memory.PluginProcessCount", plugin_count); | |
536 UMA_HISTOGRAM_COUNTS_100("Memory.PepperPluginProcessCount", | |
537 pepper_plugin_count); | |
538 UMA_HISTOGRAM_COUNTS_100("Memory.PepperPluginBrokerProcessCount", | |
539 pepper_plugin_broker_count); | |
540 UMA_HISTOGRAM_COUNTS_100("Memory.RendererProcessCount", renderer_count); | |
541 UMA_HISTOGRAM_COUNTS_100("Memory.WorkerProcessCount", worker_count); | |
542 // TODO(viettrungluu): Do we want separate counts for the other | |
543 // (platform-specific) process types? | |
544 | |
545 int total_sample = static_cast<int>(aggregate_memory / 1000); | |
546 UMA_HISTOGRAM_MEMORY_MB("Memory.Total", total_sample); | |
547 | |
548 #if defined(OS_CHROMEOS) | |
549 UpdateSwapHistograms(); | |
550 #endif | |
551 } | |
552 | |
553 #if defined(OS_CHROMEOS) | |
554 void MemoryDetails::UpdateSwapHistograms() { | |
555 UMA_HISTOGRAM_BOOLEAN("Memory.Swap.HaveSwapped", swap_info_.num_writes > 0); | |
556 if (swap_info_.num_writes == 0) | |
557 return; | |
558 | |
559 // Only record swap info when any swaps have happened, to give us more | |
560 // detail in the histograms. | |
561 const ProcessData& browser = *ChromeBrowser(); | |
562 size_t aggregate_memory = 0; | |
563 for (size_t index = 0; index < browser.processes.size(); index++) { | |
564 int sample = static_cast<int>(browser.processes[index].working_set.swapped); | |
565 aggregate_memory += sample; | |
566 switch (browser.processes[index].process_type) { | |
567 case content::PROCESS_TYPE_BROWSER: | |
568 UMA_HISTOGRAM_MEMORY_KB("Memory.Swap.Browser", sample); | |
569 continue; | |
570 case content::PROCESS_TYPE_RENDERER: { | |
571 ProcessMemoryInformation::RendererProcessType renderer_type = | |
572 browser.processes[index].renderer_type; | |
573 switch (renderer_type) { | |
574 case ProcessMemoryInformation::RENDERER_EXTENSION: | |
575 UMA_HISTOGRAM_MEMORY_KB("Memory.Swap.Extension", sample); | |
576 continue; | |
577 case ProcessMemoryInformation::RENDERER_CHROME: | |
578 UMA_HISTOGRAM_MEMORY_KB("Memory.Swap.Chrome", sample); | |
579 continue; | |
580 case ProcessMemoryInformation::RENDERER_UNKNOWN: | |
581 NOTREACHED() << "Unknown renderer process type."; | |
582 continue; | |
583 case ProcessMemoryInformation::RENDERER_NORMAL: | |
584 default: | |
585 UMA_HISTOGRAM_MEMORY_KB("Memory.Swap.Renderer", sample); | |
586 continue; | |
587 } | |
588 } | |
589 case content::PROCESS_TYPE_PLUGIN: | |
590 UMA_HISTOGRAM_MEMORY_KB("Memory.Swap.Plugin", sample); | |
591 continue; | |
592 case content::PROCESS_TYPE_UTILITY: | |
593 UMA_HISTOGRAM_MEMORY_KB("Memory.Swap.Utility", sample); | |
594 continue; | |
595 case content::PROCESS_TYPE_ZYGOTE: | |
596 UMA_HISTOGRAM_MEMORY_KB("Memory.Swap.Zygote", sample); | |
597 continue; | |
598 case content::PROCESS_TYPE_SANDBOX_HELPER: | |
599 UMA_HISTOGRAM_MEMORY_KB("Memory.Swap.SandboxHelper", sample); | |
600 continue; | |
601 case content::PROCESS_TYPE_GPU: | |
602 UMA_HISTOGRAM_MEMORY_KB("Memory.Swap.Gpu", sample); | |
603 continue; | |
604 case content::PROCESS_TYPE_PPAPI_PLUGIN: | |
605 UMA_HISTOGRAM_MEMORY_KB("Memory.Swap.PepperPlugin", sample); | |
606 continue; | |
607 case content::PROCESS_TYPE_PPAPI_BROKER: | |
608 UMA_HISTOGRAM_MEMORY_KB("Memory.Swap.PepperPluginBroker", sample); | |
609 continue; | |
610 case PROCESS_TYPE_NACL_LOADER: | |
611 UMA_HISTOGRAM_MEMORY_KB("Memory.Swap.NativeClient", sample); | |
612 continue; | |
613 case PROCESS_TYPE_NACL_BROKER: | |
614 UMA_HISTOGRAM_MEMORY_KB("Memory.Swap.NativeClientBroker", sample); | |
615 continue; | |
616 default: | |
617 NOTREACHED(); | |
618 continue; | |
619 } | |
620 } | |
621 | |
622 int total_sample = static_cast<int>(aggregate_memory / 1000); | |
623 UMA_HISTOGRAM_MEMORY_MB("Memory.Swap.Total", total_sample); | |
624 | |
625 UMA_HISTOGRAM_CUSTOM_COUNTS("Memory.Swap.CompressedDataSize", | |
626 swap_info_.compr_data_size / (1024 * 1024), | |
627 1, 4096, 50); | |
628 UMA_HISTOGRAM_CUSTOM_COUNTS("Memory.Swap.OriginalDataSize", | |
629 swap_info_.orig_data_size / (1024 * 1024), | |
630 1, 4096, 50); | |
631 UMA_HISTOGRAM_CUSTOM_COUNTS("Memory.Swap.MemUsedTotal", | |
632 swap_info_.mem_used_total / (1024 * 1024), | |
633 1, 4096, 50); | |
634 UMA_HISTOGRAM_CUSTOM_COUNTS("Memory.Swap.NumReads", | |
635 swap_info_.num_reads, | |
636 1, 100000000, 100); | |
637 UMA_HISTOGRAM_CUSTOM_COUNTS("Memory.Swap.NumWrites", | |
638 swap_info_.num_writes, | |
639 1, 100000000, 100); | |
640 | |
641 if (swap_info_.orig_data_size > 0 && swap_info_.compr_data_size > 0) { | |
642 UMA_HISTOGRAM_CUSTOM_COUNTS( | |
643 "Memory.Swap.CompressionRatio", | |
644 swap_info_.orig_data_size / swap_info_.compr_data_size, | |
645 1, 20, 20); | |
646 } | |
647 } | |
648 | |
649 #endif // defined(OS_CHROMEOS) | |
OLD | NEW |