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