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/process_map.h" 29#include "extensions/browser/view_type_utils.h" 30#include "extensions/common/extension.h" 31#include "grit/chromium_strings.h" 32#include "grit/generated_resources.h" 33#include "ui/base/l10n/l10n_util.h" 34 35#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) 36#include "content/public/browser/zygote_host_linux.h" 37#endif 38 39using base::StringPrintf; 40using content::BrowserChildProcessHostIterator; 41using content::BrowserThread; 42using content::NavigationEntry; 43using content::RenderViewHost; 44using content::RenderWidgetHost; 45using content::WebContents; 46using extensions::Extension; 47 48// static 49std::string ProcessMemoryInformation::GetRendererTypeNameInEnglish( 50 RendererProcessType type) { 51 switch (type) { 52 case RENDERER_NORMAL: 53 return "Tab"; 54 case RENDERER_CHROME: 55 return "Tab (Chrome)"; 56 case RENDERER_EXTENSION: 57 return "Extension"; 58 case RENDERER_DEVTOOLS: 59 return "Devtools"; 60 case RENDERER_INTERSTITIAL: 61 return "Interstitial"; 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 72std::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 80ProcessMemoryInformation::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 88ProcessMemoryInformation::~ProcessMemoryInformation() {} 89 90bool ProcessMemoryInformation::operator<( 91 const ProcessMemoryInformation& rhs) const { 92 return working_set.priv < rhs.working_set.priv; 93} 94 95ProcessData::ProcessData() {} 96 97ProcessData::ProcessData(const ProcessData& rhs) 98 : name(rhs.name), 99 process_name(rhs.process_name), 100 processes(rhs.processes) { 101} 102 103ProcessData::~ProcessData() {} 104 105ProcessData& 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// 124void 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 137MemoryDetails::~MemoryDetails() {} 138 139std::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<base::string16>::const_iterator iter2 = 155 iter1->titles.begin(); 156 iter2 != iter1->titles.end(); ++iter2) { 157 if (iter2 != iter1->titles.begin()) 158 log += "|"; 159 log += base::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 175void 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 202void 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#endif 208 209 ProcessData* const chrome_browser = ChromeBrowser(); 210 // Get more information about the process. 211 for (size_t index = 0; index < chrome_browser->processes.size(); 212 index++) { 213 // Check if it's a renderer, if so get the list of page titles in it and 214 // check if it's a diagnostics-related process. We skip about:memory pages. 215 // Iterate the RenderProcessHosts to find the tab contents. 216 ProcessMemoryInformation& process = 217 chrome_browser->processes[index]; 218 219 scoped_ptr<content::RenderWidgetHostIterator> widgets( 220 RenderWidgetHost::GetRenderWidgetHosts()); 221 while (content::RenderWidgetHost* widget = widgets->GetNextHost()) { 222 content::RenderProcessHost* render_process_host = 223 widget->GetProcess(); 224 DCHECK(render_process_host); 225 // Ignore processes that don't have a connection, such as crashed tabs. 226 if (!render_process_host->HasConnection() || 227 process.pid != base::GetProcId(render_process_host->GetHandle())) { 228 continue; 229 } 230 process.process_type = content::PROCESS_TYPE_RENDERER; 231 Profile* profile = 232 Profile::FromBrowserContext( 233 render_process_host->GetBrowserContext()); 234 ExtensionService* extension_service = profile->GetExtensionService(); 235 extensions::ProcessMap* extension_process_map = NULL; 236 // No extensions on Android. So extension_service can be NULL. 237 if (extension_service) 238 extension_process_map = extensions::ProcessMap::Get(profile); 239 240 // The RenderProcessHost may host multiple WebContentses. Any 241 // of them which contain diagnostics information make the whole 242 // process be considered a diagnostics process. 243 if (!widget->IsRenderView()) 244 continue; 245 246 RenderViewHost* host = RenderViewHost::From(widget); 247 WebContents* contents = WebContents::FromRenderViewHost(host); 248 GURL url; 249 if (contents) { 250 url = contents->GetURL(); 251 SiteData* site_data = 252 &chrome_browser->site_data[contents->GetBrowserContext()]; 253 SiteDetails::CollectSiteInfo(contents, site_data); 254 } 255 extensions::ViewType type = extensions::GetViewType(contents); 256 if (host->GetEnabledBindings() & content::BINDINGS_POLICY_WEB_UI) { 257 process.renderer_type = ProcessMemoryInformation::RENDERER_CHROME; 258 } else if (extension_process_map && 259 extension_process_map->Contains(host->GetProcess()->GetID())) { 260 // For our purposes, don't count processes containing only hosted apps 261 // as extension processes. See also: crbug.com/102533. 262 std::set<std::string> extension_ids = 263 extension_process_map->GetExtensionsInProcess( 264 host->GetProcess()->GetID()); 265 for (std::set<std::string>::iterator iter = extension_ids.begin(); 266 iter != extension_ids.end(); ++iter) { 267 const Extension* extension = 268 extension_service->GetExtensionById(*iter, false); 269 if (extension && !extension->is_hosted_app()) { 270 process.renderer_type = 271 ProcessMemoryInformation::RENDERER_EXTENSION; 272 break; 273 } 274 } 275 } 276 if (extension_process_map && 277 extension_process_map->Contains(host->GetProcess()->GetID())) { 278 const Extension* extension = 279 extension_service->extensions()->GetByID(url.host()); 280 if (extension) { 281 base::string16 title = base::UTF8ToUTF16(extension->name()); 282 process.titles.push_back(title); 283 process.renderer_type = 284 ProcessMemoryInformation::RENDERER_EXTENSION; 285 continue; 286 } 287 } 288 289 if (!contents) { 290 process.renderer_type = 291 ProcessMemoryInformation::RENDERER_INTERSTITIAL; 292 continue; 293 } 294 295 if (type == extensions::VIEW_TYPE_BACKGROUND_CONTENTS) { 296 process.titles.push_back(base::UTF8ToUTF16(url.spec())); 297 process.renderer_type = 298 ProcessMemoryInformation::RENDERER_BACKGROUND_APP; 299 continue; 300 } 301 302 // Since we have a WebContents and and the renderer type hasn't been 303 // set yet, it must be a normal tabbed renderer. 304 if (process.renderer_type == ProcessMemoryInformation::RENDERER_UNKNOWN) 305 process.renderer_type = ProcessMemoryInformation::RENDERER_NORMAL; 306 307 base::string16 title = contents->GetTitle(); 308 if (!title.length()) 309 title = l10n_util::GetStringUTF16(IDS_DEFAULT_TAB_TITLE); 310 process.titles.push_back(title); 311 312 // We need to check the pending entry as well as the virtual_url to 313 // see if it's a chrome://memory URL (we don't want to count these in 314 // the total memory usage of the browser). 315 // 316 // When we reach here, chrome://memory will be the pending entry since 317 // we haven't responded with any data such that it would be committed. 318 // If you have another chrome://memory tab open (which would be 319 // committed), we don't want to count it either, so we also check the 320 // last committed entry. 321 // 322 // Either the pending or last committed entries can be NULL. 323 const NavigationEntry* pending_entry = 324 contents->GetController().GetPendingEntry(); 325 const NavigationEntry* last_committed_entry = 326 contents->GetController().GetLastCommittedEntry(); 327 if ((last_committed_entry && 328 LowerCaseEqualsASCII(last_committed_entry->GetVirtualURL().spec(), 329 chrome::kChromeUIMemoryURL)) || 330 (pending_entry && 331 LowerCaseEqualsASCII(pending_entry->GetVirtualURL().spec(), 332 chrome::kChromeUIMemoryURL))) { 333 process.is_diagnostics = true; 334 } 335 } 336 337#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) 338 if (process.pid == zygote_pid) { 339 process.process_type = content::PROCESS_TYPE_ZYGOTE; 340 } 341#endif 342 } 343 344 // Get rid of other Chrome processes that are from a different profile. 345 for (size_t index = 0; index < chrome_browser->processes.size(); 346 index++) { 347 if (chrome_browser->processes[index].process_type == 348 content::PROCESS_TYPE_UNKNOWN) { 349 chrome_browser->processes.erase( 350 chrome_browser->processes.begin() + index); 351 index--; 352 } 353 } 354 355 if (user_metrics_mode_ == UPDATE_USER_METRICS) 356 UpdateHistograms(); 357 358 OnDetailsAvailable(); 359} 360 361void MemoryDetails::UpdateHistograms() { 362 // Reports a set of memory metrics to UMA. 363 // Memory is measured in KB. 364 365 const ProcessData& browser = *ChromeBrowser(); 366 size_t aggregate_memory = 0; 367 int chrome_count = 0; 368 int extension_count = 0; 369 int plugin_count = 0; 370 int pepper_plugin_count = 0; 371 int pepper_plugin_broker_count = 0; 372 int renderer_count = 0; 373 int other_count = 0; 374 int worker_count = 0; 375 int process_limit = content::RenderProcessHost::GetMaxRendererProcessCount(); 376 for (size_t index = 0; index < browser.processes.size(); index++) { 377 int sample = static_cast<int>(browser.processes[index].working_set.priv); 378 aggregate_memory += sample; 379 switch (browser.processes[index].process_type) { 380 case content::PROCESS_TYPE_BROWSER: 381 UMA_HISTOGRAM_MEMORY_KB("Memory.Browser", sample); 382 continue; 383 case content::PROCESS_TYPE_RENDERER: { 384 ProcessMemoryInformation::RendererProcessType renderer_type = 385 browser.processes[index].renderer_type; 386 switch (renderer_type) { 387 case ProcessMemoryInformation::RENDERER_EXTENSION: 388 UMA_HISTOGRAM_MEMORY_KB("Memory.Extension", sample); 389 extension_count++; 390 continue; 391 case ProcessMemoryInformation::RENDERER_CHROME: 392 UMA_HISTOGRAM_MEMORY_KB("Memory.Chrome", sample); 393 chrome_count++; 394 continue; 395 case ProcessMemoryInformation::RENDERER_UNKNOWN: 396 NOTREACHED() << "Unknown renderer process type."; 397 continue; 398 case ProcessMemoryInformation::RENDERER_NORMAL: 399 default: 400 // TODO(erikkay): Should we bother splitting out the other subtypes? 401 UMA_HISTOGRAM_MEMORY_KB("Memory.Renderer", sample); 402 renderer_count++; 403 continue; 404 } 405 } 406 case content::PROCESS_TYPE_PLUGIN: 407 UMA_HISTOGRAM_MEMORY_KB("Memory.Plugin", sample); 408 plugin_count++; 409 continue; 410 case content::PROCESS_TYPE_WORKER: 411 UMA_HISTOGRAM_MEMORY_KB("Memory.Worker", sample); 412 worker_count++; 413 continue; 414 case content::PROCESS_TYPE_UTILITY: 415 UMA_HISTOGRAM_MEMORY_KB("Memory.Utility", sample); 416 other_count++; 417 continue; 418 case content::PROCESS_TYPE_ZYGOTE: 419 UMA_HISTOGRAM_MEMORY_KB("Memory.Zygote", sample); 420 other_count++; 421 continue; 422 case content::PROCESS_TYPE_SANDBOX_HELPER: 423 UMA_HISTOGRAM_MEMORY_KB("Memory.SandboxHelper", sample); 424 other_count++; 425 continue; 426 case content::PROCESS_TYPE_GPU: 427 UMA_HISTOGRAM_MEMORY_KB("Memory.Gpu", sample); 428 other_count++; 429 continue; 430 case content::PROCESS_TYPE_PPAPI_PLUGIN: 431 UMA_HISTOGRAM_MEMORY_KB("Memory.PepperPlugin", sample); 432 pepper_plugin_count++; 433 continue; 434 case content::PROCESS_TYPE_PPAPI_BROKER: 435 UMA_HISTOGRAM_MEMORY_KB("Memory.PepperPluginBroker", sample); 436 pepper_plugin_broker_count++; 437 continue; 438 case PROCESS_TYPE_NACL_LOADER: 439 UMA_HISTOGRAM_MEMORY_KB("Memory.NativeClient", sample); 440 other_count++; 441 continue; 442 case PROCESS_TYPE_NACL_BROKER: 443 UMA_HISTOGRAM_MEMORY_KB("Memory.NativeClientBroker", sample); 444 other_count++; 445 continue; 446 default: 447 NOTREACHED(); 448 continue; 449 } 450 } 451#if defined(OS_CHROMEOS) 452 // Chrome OS exposes system-wide graphics driver memory which has historically 453 // been a source of leak/bloat. 454 base::SystemMemoryInfoKB meminfo; 455 if (base::GetSystemMemoryInfo(&meminfo) && meminfo.gem_size != -1) 456 UMA_HISTOGRAM_MEMORY_MB("Memory.Graphics", meminfo.gem_size / 1024 / 1024); 457#endif 458 459 UMA_HISTOGRAM_COUNTS_100("Memory.ProcessLimit", process_limit); 460 UMA_HISTOGRAM_COUNTS_100("Memory.ProcessCount", 461 static_cast<int>(browser.processes.size())); 462 UMA_HISTOGRAM_COUNTS_100("Memory.ChromeProcessCount", chrome_count); 463 UMA_HISTOGRAM_COUNTS_100("Memory.ExtensionProcessCount", extension_count); 464 UMA_HISTOGRAM_COUNTS_100("Memory.OtherProcessCount", other_count); 465 UMA_HISTOGRAM_COUNTS_100("Memory.PluginProcessCount", plugin_count); 466 UMA_HISTOGRAM_COUNTS_100("Memory.PepperPluginProcessCount", 467 pepper_plugin_count); 468 UMA_HISTOGRAM_COUNTS_100("Memory.PepperPluginBrokerProcessCount", 469 pepper_plugin_broker_count); 470 UMA_HISTOGRAM_COUNTS_100("Memory.RendererProcessCount", renderer_count); 471 UMA_HISTOGRAM_COUNTS_100("Memory.WorkerProcessCount", worker_count); 472 // TODO(viettrungluu): Do we want separate counts for the other 473 // (platform-specific) process types? 474 475 int total_sample = static_cast<int>(aggregate_memory / 1000); 476 UMA_HISTOGRAM_MEMORY_MB("Memory.Total", total_sample); 477 478#if defined(OS_CHROMEOS) 479 UpdateSwapHistograms(); 480#endif 481 482} 483 484#if defined(OS_CHROMEOS) 485void MemoryDetails::UpdateSwapHistograms() { 486 UMA_HISTOGRAM_BOOLEAN("Memory.Swap.HaveSwapped", swap_info_.num_writes > 0); 487 if (swap_info_.num_writes == 0) 488 return; 489 490 // Only record swap info when any swaps have happened, to give us more 491 // detail in the histograms. 492 const ProcessData& browser = *ChromeBrowser(); 493 size_t aggregate_memory = 0; 494 for (size_t index = 0; index < browser.processes.size(); index++) { 495 int sample = static_cast<int>(browser.processes[index].working_set.swapped); 496 aggregate_memory += sample; 497 switch (browser.processes[index].process_type) { 498 case content::PROCESS_TYPE_BROWSER: 499 UMA_HISTOGRAM_MEMORY_KB("Memory.Swap.Browser", sample); 500 continue; 501 case content::PROCESS_TYPE_RENDERER: { 502 ProcessMemoryInformation::RendererProcessType renderer_type = 503 browser.processes[index].renderer_type; 504 switch (renderer_type) { 505 case ProcessMemoryInformation::RENDERER_EXTENSION: 506 UMA_HISTOGRAM_MEMORY_KB("Memory.Swap.Extension", sample); 507 continue; 508 case ProcessMemoryInformation::RENDERER_CHROME: 509 UMA_HISTOGRAM_MEMORY_KB("Memory.Swap.Chrome", sample); 510 continue; 511 case ProcessMemoryInformation::RENDERER_UNKNOWN: 512 NOTREACHED() << "Unknown renderer process type."; 513 continue; 514 case ProcessMemoryInformation::RENDERER_NORMAL: 515 default: 516 UMA_HISTOGRAM_MEMORY_KB("Memory.Swap.Renderer", sample); 517 continue; 518 } 519 } 520 case content::PROCESS_TYPE_PLUGIN: 521 UMA_HISTOGRAM_MEMORY_KB("Memory.Swap.Plugin", sample); 522 continue; 523 case content::PROCESS_TYPE_WORKER: 524 UMA_HISTOGRAM_MEMORY_KB("Memory.Swap.Worker", sample); 525 continue; 526 case content::PROCESS_TYPE_UTILITY: 527 UMA_HISTOGRAM_MEMORY_KB("Memory.Swap.Utility", sample); 528 continue; 529 case content::PROCESS_TYPE_ZYGOTE: 530 UMA_HISTOGRAM_MEMORY_KB("Memory.Swap.Zygote", sample); 531 continue; 532 case content::PROCESS_TYPE_SANDBOX_HELPER: 533 UMA_HISTOGRAM_MEMORY_KB("Memory.Swap.SandboxHelper", sample); 534 continue; 535 case content::PROCESS_TYPE_GPU: 536 UMA_HISTOGRAM_MEMORY_KB("Memory.Swap.Gpu", sample); 537 continue; 538 case content::PROCESS_TYPE_PPAPI_PLUGIN: 539 UMA_HISTOGRAM_MEMORY_KB("Memory.Swap.PepperPlugin", sample); 540 continue; 541 case content::PROCESS_TYPE_PPAPI_BROKER: 542 UMA_HISTOGRAM_MEMORY_KB("Memory.Swap.PepperPluginBroker", sample); 543 continue; 544 case PROCESS_TYPE_NACL_LOADER: 545 UMA_HISTOGRAM_MEMORY_KB("Memory.Swap.NativeClient", sample); 546 continue; 547 case PROCESS_TYPE_NACL_BROKER: 548 UMA_HISTOGRAM_MEMORY_KB("Memory.Swap.NativeClientBroker", sample); 549 continue; 550 default: 551 NOTREACHED(); 552 continue; 553 } 554 } 555 556 int total_sample = static_cast<int>(aggregate_memory / 1000); 557 UMA_HISTOGRAM_MEMORY_MB("Memory.Swap.Total", total_sample); 558 559 UMA_HISTOGRAM_CUSTOM_COUNTS("Memory.Swap.CompressedDataSize", 560 swap_info_.compr_data_size / (1024 * 1024), 561 1, 4096, 50); 562 UMA_HISTOGRAM_CUSTOM_COUNTS("Memory.Swap.OriginalDataSize", 563 swap_info_.orig_data_size / (1024 * 1024), 564 1, 4096, 50); 565 UMA_HISTOGRAM_CUSTOM_COUNTS("Memory.Swap.MemUsedTotal", 566 swap_info_.mem_used_total / (1024 * 1024), 567 1, 4096, 50); 568 UMA_HISTOGRAM_CUSTOM_COUNTS("Memory.Swap.NumReads", 569 swap_info_.num_reads, 570 1, 100000000, 100); 571 UMA_HISTOGRAM_CUSTOM_COUNTS("Memory.Swap.NumWrites", 572 swap_info_.num_writes, 573 1, 100000000, 100); 574 575 if (swap_info_.orig_data_size > 0 && swap_info_.compr_data_size > 0) { 576 UMA_HISTOGRAM_CUSTOM_COUNTS( 577 "Memory.Swap.CompressionRatio", 578 swap_info_.orig_data_size / swap_info_.compr_data_size, 579 1, 20, 20); 580 } 581} 582 583#endif 584