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