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