| 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> |
| 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 |