1// Copyright (c) 2011 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 <psapi.h>
8
9#include "base/file_path.h"
10#include "base/file_version_info.h"
11#include "base/string_util.h"
12#include "base/utf_string_conversions.h"
13#include "base/win/scoped_handle.h"
14#include "base/win/windows_version.h"
15#include "chrome/common/chrome_version_info.h"
16#include "chrome/common/url_constants.h"
17#include "content/browser/browser_child_process_host.h"
18#include "content/browser/browser_thread.h"
19#include "content/browser/renderer_host/backing_store_manager.h"
20#include "content/browser/renderer_host/render_process_host.h"
21#include "content/browser/tab_contents/navigation_entry.h"
22#include "grit/chromium_strings.h"
23#include "ui/base/l10n/l10n_util.h"
24
25// Known browsers which we collect details for.
26enum {
27  CHROME_BROWSER = 0,
28  CHROME_NACL_PROCESS,
29  IE_BROWSER,
30  FIREFOX_BROWSER,
31  OPERA_BROWSER,
32  SAFARI_BROWSER,
33  IE_64BIT_BROWSER,
34  KONQUEROR_BROWSER,
35  MAX_BROWSERS
36} BrowserProcess;
37
38MemoryDetails::MemoryDetails() {
39  static const std::wstring google_browser_name =
40      UTF16ToWide(l10n_util::GetStringUTF16(IDS_PRODUCT_NAME));
41  struct {
42    const wchar_t* name;
43    const wchar_t* process_name;
44  } process_template[MAX_BROWSERS] = {
45    { google_browser_name.c_str(), L"chrome.exe", },
46    { google_browser_name.c_str(), L"nacl64.exe", },
47    { L"IE", L"iexplore.exe", },
48    { L"Firefox", L"firefox.exe", },
49    { L"Opera", L"opera.exe", },
50    { L"Safari", L"safari.exe", },
51    { L"IE (64bit)", L"iexplore.exe", },
52    { L"Konqueror", L"konqueror.exe", },
53  };
54
55  for (int index = 0; index < MAX_BROWSERS; ++index) {
56    ProcessData process;
57    process.name = process_template[index].name;
58    process.process_name = process_template[index].process_name;
59    process_data_.push_back(process);
60  }
61}
62
63ProcessData* MemoryDetails::ChromeBrowser() {
64  return &process_data_[CHROME_BROWSER];
65}
66
67void MemoryDetails::CollectProcessData(
68    const std::vector<ProcessMemoryInformation>& child_info) {
69  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
70
71  // Clear old data.
72  for (unsigned int index = 0; index < process_data_.size(); index++)
73    process_data_[index].processes.clear();
74
75  base::win::OSInfo::WindowsArchitecture windows_architecture =
76      base::win::OSInfo::GetInstance()->architecture();
77
78  base::win::ScopedHandle snapshot(
79      ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0));
80  PROCESSENTRY32 process_entry = {sizeof(PROCESSENTRY32)};
81  if (!snapshot.Get()) {
82    LOG(ERROR) << "CreateToolhelp32Snaphot failed: " << GetLastError();
83    return;
84  }
85  if (!::Process32First(snapshot, &process_entry)) {
86    LOG(ERROR) << "Process32First failed: " << GetLastError();
87    return;
88  }
89  do {
90    base::ProcessId pid = process_entry.th32ProcessID;
91    base::win::ScopedHandle process_handle(::OpenProcess(
92        PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid));
93    if (!process_handle.Get())
94      continue;
95    bool is_64bit_process =
96        ((windows_architecture == base::win::OSInfo::X64_ARCHITECTURE) ||
97         (windows_architecture == base::win::OSInfo::IA64_ARCHITECTURE)) &&
98        (base::win::OSInfo::GetWOW64StatusForProcess(process_handle) ==
99            base::win::OSInfo::WOW64_DISABLED);
100    for (unsigned int index2 = 0; index2 < process_data_.size(); index2++) {
101      if (_wcsicmp(process_data_[index2].process_name.c_str(),
102                   process_entry.szExeFile) != 0)
103        continue;
104      if (index2 == IE_BROWSER && is_64bit_process)
105        continue;  // Should use IE_64BIT_BROWSER
106      // Get Memory Information.
107      ProcessMemoryInformation info;
108      info.pid = pid;
109      if (info.pid == GetCurrentProcessId())
110        info.type = ChildProcessInfo::BROWSER_PROCESS;
111      else
112        info.type = ChildProcessInfo::UNKNOWN_PROCESS;
113
114      scoped_ptr<base::ProcessMetrics> metrics;
115      metrics.reset(base::ProcessMetrics::CreateProcessMetrics(process_handle));
116      metrics->GetCommittedKBytes(&info.committed);
117      metrics->GetWorkingSetKBytes(&info.working_set);
118
119      // Get Version Information.
120      TCHAR name[MAX_PATH];
121      if (index2 == CHROME_BROWSER || index2 == CHROME_NACL_PROCESS) {
122        chrome::VersionInfo version_info;
123        if (version_info.is_valid())
124          info.version = ASCIIToWide(version_info.Version());
125        // Check if this is one of the child processes whose data we collected
126        // on the IO thread, and if so copy over that data.
127        for (size_t child = 0; child < child_info.size(); child++) {
128          if (child_info[child].pid != info.pid)
129            continue;
130          info.titles = child_info[child].titles;
131          info.type = child_info[child].type;
132          break;
133        }
134      } else if (GetModuleFileNameEx(process_handle, NULL, name,
135                                     MAX_PATH - 1)) {
136        std::wstring str_name(name);
137        scoped_ptr<FileVersionInfo> version_info(
138            FileVersionInfo::CreateFileVersionInfo(FilePath(str_name)));
139        if (version_info != NULL) {
140          info.version = version_info->product_version();
141          info.product_name = version_info->product_name();
142        }
143      }
144
145      // Add the process info to our list.
146      if (index2 == CHROME_NACL_PROCESS) {
147        // Add NaCl processes to Chrome's list
148        process_data_[CHROME_BROWSER].processes.push_back(info);
149      } else {
150        process_data_[index2].processes.push_back(info);
151      }
152      break;
153    }
154  } while (::Process32Next(snapshot, &process_entry));
155
156  // Finally return to the browser thread.
157  BrowserThread::PostTask(
158      BrowserThread::UI, FROM_HERE,
159      NewRunnableMethod(this, &MemoryDetails::CollectChildInfoOnUIThread));
160}
161