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 3 // found in the LICENSE file. 4 5 #include "chrome/browser/memory_details.h" 6 7 #include "base/bind.h" 8 #include "base/file_version_info.h" 9 #include "base/metrics/histogram.h" 10 #include "base/strings/string_util.h" 11 #include "base/strings/stringprintf.h" 12 #include "base/strings/utf_string_conversions.h" 13 #include "chrome/browser/extensions/extension_process_manager.h" 14 #include "chrome/browser/extensions/extension_service.h" 15 #include "chrome/browser/profiles/profile.h" 16 #include "chrome/common/extensions/extension.h" 17 #include "chrome/common/url_constants.h" 18 #include "components/nacl/common/nacl_process_type.h" 19 #include "content/public/browser/browser_child_process_host_iterator.h" 20 #include "content/public/browser/browser_thread.h" 21 #include "content/public/browser/child_process_data.h" 22 #include "content/public/browser/navigation_controller.h" 23 #include "content/public/browser/navigation_entry.h" 24 #include "content/public/browser/render_process_host.h" 25 #include "content/public/browser/render_view_host.h" 26 #include "content/public/browser/web_contents.h" 27 #include "content/public/common/bindings_policy.h" 28 #include "extensions/browser/view_type_utils.h" 29 #include "grit/chromium_strings.h" 30 #include "grit/generated_resources.h" 31 #include "ui/base/l10n/l10n_util.h" 32 33 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) 34 #include "content/public/browser/zygote_host_linux.h" 35 #endif 36 37 using base::StringPrintf; 38 using content::BrowserChildProcessHostIterator; 39 using content::BrowserThread; 40 using content::NavigationEntry; 41 using content::RenderViewHost; 42 using content::RenderWidgetHost; 43 using content::WebContents; 44 using extensions::Extension; 45 46 // static 47 std::string ProcessMemoryInformation::GetRendererTypeNameInEnglish( 48 RendererProcessType type) { 49 switch (type) { 50 case RENDERER_NORMAL: 51 return "Tab"; 52 case RENDERER_CHROME: 53 return "Tab (Chrome)"; 54 case RENDERER_EXTENSION: 55 return "Extension"; 56 case RENDERER_DEVTOOLS: 57 return "Devtools"; 58 case RENDERER_INTERSTITIAL: 59 return "Interstitial"; 60 case RENDERER_NOTIFICATION: 61 return "Notification"; 62 case RENDERER_BACKGROUND_APP: 63 return "Background App"; 64 case RENDERER_UNKNOWN: 65 default: 66 NOTREACHED() << "Unknown renderer process type!"; 67 return "Unknown"; 68 } 69 } 70 71 // static 72 std::string ProcessMemoryInformation::GetFullTypeNameInEnglish( 73 int process_type, 74 RendererProcessType rtype) { 75 if (process_type == content::PROCESS_TYPE_RENDERER) 76 return GetRendererTypeNameInEnglish(rtype); 77 return content::GetProcessTypeNameInEnglish(process_type); 78 } 79 80 ProcessMemoryInformation::ProcessMemoryInformation() 81 : pid(0), 82 num_processes(0), 83 is_diagnostics(false), 84 process_type(content::PROCESS_TYPE_UNKNOWN), 85 renderer_type(RENDERER_UNKNOWN) { 86 } 87 88 ProcessMemoryInformation::~ProcessMemoryInformation() {} 89 90 bool ProcessMemoryInformation::operator<( 91 const ProcessMemoryInformation& rhs) const { 92 return working_set.priv < rhs.working_set.priv; 93 } 94 95 ProcessData::ProcessData() {} 96 97 ProcessData::ProcessData(const ProcessData& rhs) 98 : name(rhs.name), 99 process_name(rhs.process_name), 100 processes(rhs.processes) { 101 } 102 103 ProcessData::~ProcessData() {} 104 105 ProcessData& ProcessData::operator=(const ProcessData& rhs) { 106 name = rhs.name; 107 process_name = rhs.process_name; 108 processes = rhs.processes; 109 return *this; 110 } 111 112 // About threading: 113 // 114 // This operation will hit no fewer than 3 threads. 115 // 116 // The BrowserChildProcessHostIterator can only be accessed from the IO thread. 117 // 118 // The RenderProcessHostIterator can only be accessed from the UI thread. 119 // 120 // This operation can take 30-100ms to complete. We never want to have 121 // one task run for that long on the UI or IO threads. So, we run the 122 // expensive parts of this operation over on the file thread. 123 // 124 void MemoryDetails::StartFetch(UserMetricsMode user_metrics_mode) { 125 // This might get called from the UI or FILE threads, but should not be 126 // getting called from the IO thread. 127 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::IO)); 128 user_metrics_mode_ = user_metrics_mode; 129 130 // In order to process this request, we need to use the plugin information. 131 // However, plugin process information is only available from the IO thread. 132 BrowserThread::PostTask( 133 BrowserThread::IO, FROM_HERE, 134 base::Bind(&MemoryDetails::CollectChildInfoOnIOThread, this)); 135 } 136 137 MemoryDetails::~MemoryDetails() {} 138 139 std::string MemoryDetails::ToLogString() { 140 std::string log; 141 log.reserve(4096); 142 ProcessMemoryInformationList processes = ChromeBrowser()->processes; 143 // Sort by memory consumption, low to high. 144 std::sort(processes.begin(), processes.end()); 145 // Print from high to low. 146 for (ProcessMemoryInformationList::reverse_iterator iter1 = 147 processes.rbegin(); 148 iter1 != processes.rend(); 149 ++iter1) { 150 log += ProcessMemoryInformation::GetFullTypeNameInEnglish( 151 iter1->process_type, iter1->renderer_type); 152 if (!iter1->titles.empty()) { 153 log += " ["; 154 for (std::vector<string16>::const_iterator iter2 = 155 iter1->titles.begin(); 156 iter2 != iter1->titles.end(); ++iter2) { 157 if (iter2 != iter1->titles.begin()) 158 log += "|"; 159 log += UTF16ToUTF8(*iter2); 160 } 161 log += "]"; 162 } 163 log += StringPrintf(" %d MB private, %d MB shared", 164 static_cast<int>(iter1->working_set.priv) / 1024, 165 static_cast<int>(iter1->working_set.shared) / 1024); 166 #if defined(OS_CHROMEOS) 167 log += StringPrintf(", %d MB swapped", 168 static_cast<int>(iter1->working_set.swapped) / 1024); 169 #endif 170 log += "\n"; 171 } 172 return log; 173 } 174 175 void MemoryDetails::CollectChildInfoOnIOThread() { 176 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 177 178 std::vector<ProcessMemoryInformation> child_info; 179 180 // Collect the list of child processes. A 0 |handle| means that 181 // the process is being launched, so we skip it. 182 for (BrowserChildProcessHostIterator iter; !iter.Done(); ++iter) { 183 ProcessMemoryInformation info; 184 if (!iter.GetData().handle) 185 continue; 186 info.pid = base::GetProcId(iter.GetData().handle); 187 if (!info.pid) 188 continue; 189 190 info.process_type = iter.GetData().process_type; 191 info.renderer_type = ProcessMemoryInformation::RENDERER_UNKNOWN; 192 info.titles.push_back(iter.GetData().name); 193 child_info.push_back(info); 194 } 195 196 // Now go do expensive memory lookups from the file thread. 197 BrowserThread::PostTask( 198 BrowserThread::FILE, FROM_HERE, 199 base::Bind(&MemoryDetails::CollectProcessData, this, child_info)); 200 } 201 202 void MemoryDetails::CollectChildInfoOnUIThread() { 203 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 204 205 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) 206 const pid_t zygote_pid = content::ZygoteHost::GetInstance()->GetPid(); 207 const pid_t sandbox_helper_pid = 208 content::ZygoteHost::GetInstance()->GetSandboxHelperPid(); 209 #endif 210 211 ProcessData* const chrome_browser = ChromeBrowser(); 212 // Get more information about the process. 213 for (size_t index = 0; index < chrome_browser->processes.size(); 214 index++) { 215 // Check if it's a renderer, if so get the list of page titles in it and 216 // check if it's a diagnostics-related process. We skip about:memory pages. 217 // Iterate the RenderProcessHosts to find the tab contents. 218 ProcessMemoryInformation& process = 219 chrome_browser->processes[index]; 220 221 RenderWidgetHost::List widgets = RenderWidgetHost::GetRenderWidgetHosts(); 222 for (size_t i = 0; i < widgets.size(); ++i) { 223 content::RenderProcessHost* render_process_host = 224 widgets[i]->GetProcess(); 225 DCHECK(render_process_host); 226 // Ignore processes that don't have a connection, such as crashed tabs. 227 if (!render_process_host->HasConnection() || 228 process.pid != base::GetProcId(render_process_host->GetHandle())) { 229 continue; 230 } 231 process.process_type = content::PROCESS_TYPE_RENDERER; 232 Profile* profile = 233 Profile::FromBrowserContext( 234 render_process_host->GetBrowserContext()); 235 ExtensionService* extension_service = profile->GetExtensionService(); 236 extensions::ProcessMap* extension_process_map = NULL; 237 // No extensions on Android. So extension_service can be NULL. 238 if (extension_service) 239 extension_process_map = extension_service->process_map(); 240 241 // The RenderProcessHost may host multiple WebContentses. Any 242 // of them which contain diagnostics information make the whole 243 // process be considered a diagnostics process. 244 if (!widgets[i]->IsRenderView()) 245 continue; 246 247 RenderViewHost* host = RenderViewHost::From(widgets[i]); 248 WebContents* contents = WebContents::FromRenderViewHost(host); 249 GURL url; 250 if (contents) { 251 url = contents->GetURL(); 252 SiteData* site_data = 253 &chrome_browser->site_data[contents->GetBrowserContext()]; 254 SiteDetails::CollectSiteInfo(contents, site_data); 255 } 256 extensions::ViewType type = extensions::GetViewType(contents); 257 if (host->GetEnabledBindings() & content::BINDINGS_POLICY_WEB_UI) { 258 process.renderer_type = ProcessMemoryInformation::RENDERER_CHROME; 259 } else if (extension_process_map && 260 extension_process_map->Contains(host->GetProcess()->GetID())) { 261 // For our purposes, don't count processes containing only hosted apps 262 // as extension processes. See also: crbug.com/102533. 263 std::set<std::string> extension_ids = 264 extension_process_map->GetExtensionsInProcess( 265 host->GetProcess()->GetID()); 266 for (std::set<std::string>::iterator iter = extension_ids.begin(); 267 iter != extension_ids.end(); ++iter) { 268 const Extension* extension = 269 extension_service->GetExtensionById(*iter, false); 270 if (extension && !extension->is_hosted_app()) { 271 process.renderer_type = 272 ProcessMemoryInformation::RENDERER_EXTENSION; 273 break; 274 } 275 } 276 } 277 if (extension_process_map && 278 extension_process_map->Contains(host->GetProcess()->GetID())) { 279 const Extension* extension = 280 extension_service->extensions()->GetByID(url.host()); 281 if (extension) { 282 string16 title = UTF8ToUTF16(extension->name()); 283 process.titles.push_back(title); 284 process.renderer_type = 285 ProcessMemoryInformation::RENDERER_EXTENSION; 286 continue; 287 } 288 } 289 290 if (!contents) { 291 process.renderer_type = 292 ProcessMemoryInformation::RENDERER_INTERSTITIAL; 293 continue; 294 } 295 296 if (type == extensions::VIEW_TYPE_BACKGROUND_CONTENTS) { 297 process.titles.push_back(UTF8ToUTF16(url.spec())); 298 process.renderer_type = 299 ProcessMemoryInformation::RENDERER_BACKGROUND_APP; 300 continue; 301 } 302 303 if (type == extensions::VIEW_TYPE_NOTIFICATION) { 304 process.titles.push_back(UTF8ToUTF16(url.spec())); 305 process.renderer_type = 306 ProcessMemoryInformation::RENDERER_NOTIFICATION; 307 continue; 308 } 309 310 // Since we have a WebContents and and the renderer type hasn't been 311 // set yet, it must be a normal tabbed renderer. 312 if (process.renderer_type == ProcessMemoryInformation::RENDERER_UNKNOWN) 313 process.renderer_type = ProcessMemoryInformation::RENDERER_NORMAL; 314 315 string16 title = contents->GetTitle(); 316 if (!title.length()) 317 title = l10n_util::GetStringUTF16(IDS_DEFAULT_TAB_TITLE); 318 process.titles.push_back(title); 319 320 // We need to check the pending entry as well as the virtual_url to 321 // see if it's a chrome://memory URL (we don't want to count these in 322 // the total memory usage of the browser). 323 // 324 // When we reach here, chrome://memory will be the pending entry since 325 // we haven't responded with any data such that it would be committed. 326 // If you have another chrome://memory tab open (which would be 327 // committed), we don't want to count it either, so we also check the 328 // last committed entry. 329 // 330 // Either the pending or last committed entries can be NULL. 331 const NavigationEntry* pending_entry = 332 contents->GetController().GetPendingEntry(); 333 const NavigationEntry* last_committed_entry = 334 contents->GetController().GetLastCommittedEntry(); 335 if ((last_committed_entry && 336 LowerCaseEqualsASCII(last_committed_entry->GetVirtualURL().spec(), 337 chrome::kChromeUIMemoryURL)) || 338 (pending_entry && 339 LowerCaseEqualsASCII(pending_entry->GetVirtualURL().spec(), 340 chrome::kChromeUIMemoryURL))) { 341 process.is_diagnostics = true; 342 } 343 } 344 345 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) 346 if (process.pid == zygote_pid) { 347 process.process_type = content::PROCESS_TYPE_ZYGOTE; 348 } else if (process.pid == sandbox_helper_pid) { 349 process.process_type = content::PROCESS_TYPE_SANDBOX_HELPER; 350 } 351 #endif 352 } 353 354 // Get rid of other Chrome processes that are from a different profile. 355 for (size_t index = 0; index < chrome_browser->processes.size(); 356 index++) { 357 if (chrome_browser->processes[index].process_type == 358 content::PROCESS_TYPE_UNKNOWN) { 359 chrome_browser->processes.erase( 360 chrome_browser->processes.begin() + index); 361 index--; 362 } 363 } 364 365 if (user_metrics_mode_ == UPDATE_USER_METRICS) 366 UpdateHistograms(); 367 368 OnDetailsAvailable(); 369 } 370 371 void MemoryDetails::UpdateHistograms() { 372 // Reports a set of memory metrics to UMA. 373 // Memory is measured in KB. 374 375 const ProcessData& browser = *ChromeBrowser(); 376 size_t aggregate_memory = 0; 377 int chrome_count = 0; 378 int extension_count = 0; 379 int plugin_count = 0; 380 int pepper_plugin_count = 0; 381 int pepper_plugin_broker_count = 0; 382 int renderer_count = 0; 383 int other_count = 0; 384 int worker_count = 0; 385 int process_limit = content::RenderProcessHost::GetMaxRendererProcessCount(); 386 for (size_t index = 0; index < browser.processes.size(); index++) { 387 int sample = static_cast<int>(browser.processes[index].working_set.priv); 388 aggregate_memory += sample; 389 switch (browser.processes[index].process_type) { 390 case content::PROCESS_TYPE_BROWSER: 391 UMA_HISTOGRAM_MEMORY_KB("Memory.Browser", sample); 392 continue; 393 case content::PROCESS_TYPE_RENDERER: { 394 ProcessMemoryInformation::RendererProcessType renderer_type = 395 browser.processes[index].renderer_type; 396 switch (renderer_type) { 397 case ProcessMemoryInformation::RENDERER_EXTENSION: 398 UMA_HISTOGRAM_MEMORY_KB("Memory.Extension", sample); 399 extension_count++; 400 continue; 401 case ProcessMemoryInformation::RENDERER_CHROME: 402 UMA_HISTOGRAM_MEMORY_KB("Memory.Chrome", sample); 403 chrome_count++; 404 continue; 405 case ProcessMemoryInformation::RENDERER_UNKNOWN: 406 NOTREACHED() << "Unknown renderer process type."; 407 continue; 408 case ProcessMemoryInformation::RENDERER_NORMAL: 409 default: 410 // TODO(erikkay): Should we bother splitting out the other subtypes? 411 UMA_HISTOGRAM_MEMORY_KB("Memory.Renderer", sample); 412 renderer_count++; 413 continue; 414 } 415 } 416 case content::PROCESS_TYPE_PLUGIN: 417 UMA_HISTOGRAM_MEMORY_KB("Memory.Plugin", sample); 418 plugin_count++; 419 continue; 420 case content::PROCESS_TYPE_WORKER: 421 UMA_HISTOGRAM_MEMORY_KB("Memory.Worker", sample); 422 worker_count++; 423 continue; 424 case content::PROCESS_TYPE_UTILITY: 425 UMA_HISTOGRAM_MEMORY_KB("Memory.Utility", sample); 426 other_count++; 427 continue; 428 case content::PROCESS_TYPE_ZYGOTE: 429 UMA_HISTOGRAM_MEMORY_KB("Memory.Zygote", sample); 430 other_count++; 431 continue; 432 case content::PROCESS_TYPE_SANDBOX_HELPER: 433 UMA_HISTOGRAM_MEMORY_KB("Memory.SandboxHelper", sample); 434 other_count++; 435 continue; 436 case content::PROCESS_TYPE_GPU: 437 UMA_HISTOGRAM_MEMORY_KB("Memory.Gpu", sample); 438 other_count++; 439 continue; 440 case content::PROCESS_TYPE_PPAPI_PLUGIN: 441 UMA_HISTOGRAM_MEMORY_KB("Memory.PepperPlugin", sample); 442 pepper_plugin_count++; 443 continue; 444 case content::PROCESS_TYPE_PPAPI_BROKER: 445 UMA_HISTOGRAM_MEMORY_KB("Memory.PepperPluginBroker", sample); 446 pepper_plugin_broker_count++; 447 continue; 448 case PROCESS_TYPE_NACL_LOADER: 449 UMA_HISTOGRAM_MEMORY_KB("Memory.NativeClient", sample); 450 other_count++; 451 continue; 452 case PROCESS_TYPE_NACL_BROKER: 453 UMA_HISTOGRAM_MEMORY_KB("Memory.NativeClientBroker", sample); 454 other_count++; 455 continue; 456 default: 457 NOTREACHED(); 458 continue; 459 } 460 } 461 UMA_HISTOGRAM_MEMORY_KB("Memory.BackingStore", 462 RenderWidgetHost::BackingStoreMemorySize() / 1024); 463 #if defined(OS_CHROMEOS) 464 // Chrome OS exposes system-wide graphics driver memory which has historically 465 // been a source of leak/bloat. 466 base::SystemMemoryInfoKB meminfo; 467 if (base::GetSystemMemoryInfo(&meminfo) && meminfo.gem_size != -1) 468 UMA_HISTOGRAM_MEMORY_MB("Memory.Graphics", meminfo.gem_size / 1024 / 1024); 469 #endif 470 471 UMA_HISTOGRAM_COUNTS_100("Memory.ProcessLimit", process_limit); 472 UMA_HISTOGRAM_COUNTS_100("Memory.ProcessCount", 473 static_cast<int>(browser.processes.size())); 474 UMA_HISTOGRAM_COUNTS_100("Memory.ChromeProcessCount", chrome_count); 475 UMA_HISTOGRAM_COUNTS_100("Memory.ExtensionProcessCount", extension_count); 476 UMA_HISTOGRAM_COUNTS_100("Memory.OtherProcessCount", other_count); 477 UMA_HISTOGRAM_COUNTS_100("Memory.PluginProcessCount", plugin_count); 478 UMA_HISTOGRAM_COUNTS_100("Memory.PepperPluginProcessCount", 479 pepper_plugin_count); 480 UMA_HISTOGRAM_COUNTS_100("Memory.PepperPluginBrokerProcessCount", 481 pepper_plugin_broker_count); 482 UMA_HISTOGRAM_COUNTS_100("Memory.RendererProcessCount", renderer_count); 483 UMA_HISTOGRAM_COUNTS_100("Memory.WorkerProcessCount", worker_count); 484 // TODO(viettrungluu): Do we want separate counts for the other 485 // (platform-specific) process types? 486 487 int total_sample = static_cast<int>(aggregate_memory / 1000); 488 UMA_HISTOGRAM_MEMORY_MB("Memory.Total", total_sample); 489 490 // Predict the number of processes needed when isolating all sites and when 491 // isolating only HTTPS sites. 492 int all_renderer_count = renderer_count + chrome_count + extension_count; 493 int non_renderer_count = browser.processes.size() - all_renderer_count; 494 SiteDetails::UpdateHistograms(browser.site_data, all_renderer_count, 495 non_renderer_count); 496 #if defined(OS_CHROMEOS) 497 UpdateSwapHistograms(); 498 #endif 499 500 } 501 502 #if defined(OS_CHROMEOS) 503 void MemoryDetails::UpdateSwapHistograms() { 504 UMA_HISTOGRAM_BOOLEAN("Memory.Swap.HaveSwapped", swap_data_.num_writes > 0); 505 if (swap_data_.num_writes == 0) 506 return; 507 508 // Only record swap info when any swaps have happened, to give us more 509 // detail in the histograms. 510 const ProcessData& browser = *ChromeBrowser(); 511 size_t aggregate_memory = 0; 512 for (size_t index = 0; index < browser.processes.size(); index++) { 513 int sample = static_cast<int>(browser.processes[index].working_set.swapped); 514 aggregate_memory += sample; 515 switch (browser.processes[index].process_type) { 516 case content::PROCESS_TYPE_BROWSER: 517 UMA_HISTOGRAM_MEMORY_KB("Memory.Swap.Browser", sample); 518 continue; 519 case content::PROCESS_TYPE_RENDERER: { 520 ProcessMemoryInformation::RendererProcessType renderer_type = 521 browser.processes[index].renderer_type; 522 switch (renderer_type) { 523 case ProcessMemoryInformation::RENDERER_EXTENSION: 524 UMA_HISTOGRAM_MEMORY_KB("Memory.Swap.Extension", sample); 525 continue; 526 case ProcessMemoryInformation::RENDERER_CHROME: 527 UMA_HISTOGRAM_MEMORY_KB("Memory.Swap.Chrome", sample); 528 continue; 529 case ProcessMemoryInformation::RENDERER_UNKNOWN: 530 NOTREACHED() << "Unknown renderer process type."; 531 continue; 532 case ProcessMemoryInformation::RENDERER_NORMAL: 533 default: 534 UMA_HISTOGRAM_MEMORY_KB("Memory.Swap.Renderer", sample); 535 continue; 536 } 537 } 538 case content::PROCESS_TYPE_PLUGIN: 539 UMA_HISTOGRAM_MEMORY_KB("Memory.Swap.Plugin", sample); 540 continue; 541 case content::PROCESS_TYPE_WORKER: 542 UMA_HISTOGRAM_MEMORY_KB("Memory.Swap.Worker", sample); 543 continue; 544 case content::PROCESS_TYPE_UTILITY: 545 UMA_HISTOGRAM_MEMORY_KB("Memory.Swap.Utility", sample); 546 continue; 547 case content::PROCESS_TYPE_ZYGOTE: 548 UMA_HISTOGRAM_MEMORY_KB("Memory.Swap.Zygote", sample); 549 continue; 550 case content::PROCESS_TYPE_SANDBOX_HELPER: 551 UMA_HISTOGRAM_MEMORY_KB("Memory.Swap.SandboxHelper", sample); 552 continue; 553 case content::PROCESS_TYPE_GPU: 554 UMA_HISTOGRAM_MEMORY_KB("Memory.Swap.Gpu", sample); 555 continue; 556 case content::PROCESS_TYPE_PPAPI_PLUGIN: 557 UMA_HISTOGRAM_MEMORY_KB("Memory.Swap.PepperPlugin", sample); 558 continue; 559 case content::PROCESS_TYPE_PPAPI_BROKER: 560 UMA_HISTOGRAM_MEMORY_KB("Memory.Swap.PepperPluginBroker", sample); 561 continue; 562 case PROCESS_TYPE_NACL_LOADER: 563 UMA_HISTOGRAM_MEMORY_KB("Memory.Swap.NativeClient", sample); 564 continue; 565 case PROCESS_TYPE_NACL_BROKER: 566 UMA_HISTOGRAM_MEMORY_KB("Memory.Swap.NativeClientBroker", sample); 567 continue; 568 default: 569 NOTREACHED(); 570 continue; 571 } 572 } 573 574 int total_sample = static_cast<int>(aggregate_memory / 1000); 575 UMA_HISTOGRAM_MEMORY_MB("Memory.Swap.Total", total_sample); 576 577 UMA_HISTOGRAM_CUSTOM_COUNTS("Memory.Swap.CompressedDataSize", 578 swap_data_.compr_data_size / (1024 * 1024), 579 1, 4096, 50); 580 UMA_HISTOGRAM_CUSTOM_COUNTS("Memory.Swap.OriginalDataSize", 581 swap_data_.orig_data_size / (1024 * 1024), 582 1, 4096, 50); 583 UMA_HISTOGRAM_CUSTOM_COUNTS("Memory.Swap.MemUsedTotal", 584 swap_data_.mem_used_total / (1024 * 1024), 585 1, 4096, 50); 586 UMA_HISTOGRAM_CUSTOM_COUNTS("Memory.Swap.NumReads", 587 swap_data_.num_reads, 588 1, 100000000, 100); 589 UMA_HISTOGRAM_CUSTOM_COUNTS("Memory.Swap.NumWrites", 590 swap_data_.num_writes, 591 1, 100000000, 100); 592 593 if (swap_data_.orig_data_size > 0 && swap_data_.compr_data_size > 0) { 594 UMA_HISTOGRAM_CUSTOM_COUNTS( 595 "Memory.Swap.CompressionRatio", 596 swap_data_.orig_data_size / swap_data_.compr_data_size, 597 1, 20, 20); 598 } 599 } 600 601 #endif 602