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