// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/memory_details.h" #include #include #include #include #include "base/bind.h" #include "base/files/file_util.h" #include "base/process/process_iterator.h" #include "base/process/process_metrics.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "chrome/common/chrome_constants.h" #include "chrome/grit/chromium_strings.h" #include "content/public/browser/browser_thread.h" #include "content/public/common/process_type.h" #include "ui/base/l10n/l10n_util.h" using base::ProcessEntry; using content::BrowserThread; // Known browsers which we collect details for. enum BrowserType { CHROME = 0, FIREFOX, ICEWEASEL, OPERA, KONQUEROR, EPIPHANY, MIDORI, MAX_BROWSERS } BrowserProcess; // The pretty printed names of those browsers. Matches up with enum // BrowserType. static const char kBrowserPrettyNames[][10] = { "Chrome", "Firefox", "Iceweasel", "Opera", "Konqueror", "Epiphany", "Midori", }; // A mapping from process name to the type of browser. static const struct { const char process_name[16]; BrowserType browser; } kBrowserBinaryNames[] = { { "firefox", FIREFOX }, { "firefox-3.5", FIREFOX }, { "firefox-3.0", FIREFOX }, { "firefox-bin", FIREFOX }, { "iceweasel", ICEWEASEL }, { "opera", OPERA }, { "konqueror", KONQUEROR }, { "epiphany-browse", EPIPHANY }, { "epiphany", EPIPHANY }, { "midori", MIDORI }, { "", MAX_BROWSERS }, }; MemoryDetails::MemoryDetails() : user_metrics_mode_(UPDATE_USER_METRICS), memory_growth_tracker_(NULL) { } ProcessData* MemoryDetails::ChromeBrowser() { return &process_data_[0]; } struct Process { pid_t pid; pid_t parent; std::string name; }; typedef std::map ProcessMap; // Get information on all the processes running on the system. static ProcessMap GetProcesses() { ProcessMap map; base::ProcessIterator process_iter(NULL); while (const ProcessEntry* process_entry = process_iter.NextProcessEntry()) { Process process; process.pid = process_entry->pid(); process.parent = process_entry->parent_pid(); process.name = process_entry->exe_file(); map.insert(std::make_pair(process.pid, process)); } return map; } // Given a process name, return the type of the browser which created that // process, or |MAX_BROWSERS| if we don't know about it. static BrowserType GetBrowserType(const std::string& process_name) { for (unsigned i = 0; kBrowserBinaryNames[i].process_name[0]; ++i) { if (strcmp(process_name.c_str(), kBrowserBinaryNames[i].process_name) == 0) return kBrowserBinaryNames[i].browser; } return MAX_BROWSERS; } // For each of a list of pids, collect memory information about that process. static ProcessData GetProcessDataMemoryInformation( const std::vector& pids) { ProcessData process_data; for (std::vector::const_iterator iter = pids.begin(); iter != pids.end(); ++iter) { ProcessMemoryInformation pmi; pmi.pid = *iter; pmi.num_processes = 1; if (pmi.pid == base::GetCurrentProcId()) pmi.process_type = content::PROCESS_TYPE_BROWSER; else pmi.process_type = content::PROCESS_TYPE_UNKNOWN; base::ProcessMetrics* metrics = base::ProcessMetrics::CreateProcessMetrics(*iter); metrics->GetWorkingSetKBytes(&pmi.working_set); delete metrics; process_data.processes.push_back(pmi); } return process_data; } // Find all children of the given process with pid |root|. static std::vector GetAllChildren(const ProcessMap& processes, const pid_t root) { std::vector children; children.push_back(root); std::set wavefront, next_wavefront; wavefront.insert(root); while (wavefront.size()) { for (ProcessMap::const_iterator iter = processes.begin(); iter != processes.end(); ++iter) { const Process& process = iter->second; if (wavefront.count(process.parent)) { children.push_back(process.pid); next_wavefront.insert(process.pid); } } wavefront.clear(); wavefront.swap(next_wavefront); } return children; } void MemoryDetails::CollectProcessData( const std::vector& child_info) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); ProcessMap process_map = GetProcesses(); std::set browsers_found; // For each process on the system, if it appears to be a browser process and // it's parent isn't a browser process, then record it in |browsers_found|. for (ProcessMap::const_iterator iter = process_map.begin(); iter != process_map.end(); ++iter) { const Process& current_process = iter->second; const BrowserType type = GetBrowserType(current_process.name); if (type == MAX_BROWSERS) continue; ProcessMap::const_iterator parent_iter = process_map.find(current_process.parent); if (parent_iter == process_map.end()) { browsers_found.insert(current_process.pid); continue; } if (GetBrowserType(parent_iter->second.name) != type) { // We found a process whose type is diffent from its parent's type. // That means it is the root process of the browser. browsers_found.insert(current_process.pid); break; } } ProcessData current_browser = GetProcessDataMemoryInformation(GetAllChildren(process_map, getpid())); current_browser.name = l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_NAME); current_browser.process_name = base::ASCIIToUTF16("chrome"); for (std::vector::iterator i = current_browser.processes.begin(); i != current_browser.processes.end(); ++i) { // Check if this is one of the child processes whose data we collected // on the IO thread, and if so copy over that data. for (size_t child = 0; child < child_info.size(); child++) { if (child_info[child].pid != i->pid) continue; i->titles = child_info[child].titles; i->process_type = child_info[child].process_type; break; } } process_data_.push_back(current_browser); // For each browser process, collect a list of its children and get the // memory usage of each. for (std::set::const_iterator iter = browsers_found.begin(); iter != browsers_found.end(); ++iter) { std::vector browser_processes = GetAllChildren(process_map, *iter); ProcessData browser = GetProcessDataMemoryInformation(browser_processes); ProcessMap::const_iterator process_iter = process_map.find(*iter); if (process_iter == process_map.end()) continue; BrowserType type = GetBrowserType(process_iter->second.name); if (type != MAX_BROWSERS) browser.name = base::ASCIIToUTF16(kBrowserPrettyNames[type]); process_data_.push_back(browser); } #if defined(OS_CHROMEOS) base::GetSwapInfo(&swap_info_); #endif // Finally return to the browser thread. BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind(&MemoryDetails::CollectChildInfoOnUIThread, this)); }