// Copyright 2015 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/caps/generate_state_json.h" #include #include #include "base/bind.h" #include "base/cpu.h" #include "base/files/file.h" #include "base/json/json_writer.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/message_loop/message_loop.h" #include "base/strings/stringprintf.h" #include "base/sys_info.h" #include "base/threading/thread_restrictions.h" #include "base/time/time.h" #include "base/values.h" #include "chrome/browser/task_manager/task_manager.h" namespace { std::string Key(base::ProcessId pid, const char* category) { return category ? base::StringPrintf("process.%d.%s", pid, category) : base::StringPrintf("process.%d", pid); } using MemoryFn1 = bool (TaskManagerModel::*)( int index, size_t* result1) const; using MemoryFn2 = bool (TaskManagerModel::*)( int index, size_t* result1, bool*) const; int InMBFromB(size_t result_in_bytes) { return static_cast(result_in_bytes / (1024 * 1024)); } int InMBFromB(const TaskManagerModel* model, MemoryFn1 mfn, int index) { size_t result_in_bytes = 0; bool res = (model->*mfn)(index, &result_in_bytes); return res ? InMBFromB(result_in_bytes) : 0; } int InMBFromB(const TaskManagerModel* model, MemoryFn2 mfn, int index) { size_t result_in_bytes = 0; bool ignored; bool res = (model->*mfn)(index, &result_in_bytes, &ignored); return res ? InMBFromB(result_in_bytes) : 0; } class TaskManagerDataDumper : public base::RefCountedThreadSafe { public: TaskManagerDataDumper(scoped_refptr model, base::File file) : model_(model), file_(file.Pass()) { model_->RegisterOnDataReadyCallback( base::Bind(&TaskManagerDataDumper::OnDataReady, this)); model->StartListening(); // Note that GenerateStateJSON 'new's this object which is reference // counted. AddRef(); } private: friend class base::RefCountedThreadSafe; ~TaskManagerDataDumper() { } void OnDataReady() { // Some data (for example V8 memory) has not yet arrived, so we wait. // TODO(cpu): Figure out how to make this reliable. static base::TimeDelta delay = base::TimeDelta::FromMilliseconds(250); base::MessageLoop::current()->PostDelayedTask( FROM_HERE, base::Bind(&TaskManagerDataDumper::OnDataReadyDelayed, this), delay); } void OnDataReadyDelayed() { model_->StopListening(); // Task manager finally computed (most) values. Lets generate the JSON // data and send it over the pipe. base::DictionaryValue dict; GatherComputerValues(&dict); GatherChromeValues(&dict); std::string json; auto options = base::JSONWriter::OPTIONS_PRETTY_PRINT; if (!base::JSONWriter::WriteWithOptions(&dict, options, &json)) return; file_.WriteAtCurrentPos(json.c_str(), json.size()); file_.Close(); // this Release() causes our destruction. Release(); } private: // TODO(cpu): split the key names below into a separate header that both // caps and chrome can use. void GatherComputerValues(base::DictionaryValue* dict) { base::CPU cpu; dict->SetInteger("system.cpu.type", cpu.type()); dict->SetInteger("system.cpu.family", cpu.family()); dict->SetInteger("system.cpu.model", cpu.model()); dict->SetInteger("system.cpu.stepping", cpu.stepping()); dict->SetString("system.cpu.brand", cpu.cpu_brand()); dict->SetInteger("system.cpu.logicalprocessors", base::SysInfo::NumberOfProcessors()); dict->SetInteger("system.cpu.logicalprocessors", base::SysInfo::NumberOfProcessors()); int64 memory = base::SysInfo::AmountOfPhysicalMemory(); dict->SetInteger("system.memory.physical", InMBFromB(memory)); memory = base::SysInfo::AmountOfAvailablePhysicalMemory(); dict->SetInteger("system.memory.available", InMBFromB(memory)); dict->SetInteger("system.uptime", base::SysInfo::Uptime() / 1000 ); dict->SetString("os.name", base::SysInfo::OperatingSystemName()); #if !defined(OS_LINUX) int32 major, minor, bugfix; base::SysInfo::OperatingSystemVersionNumbers(&major, &minor, &bugfix); dict->SetInteger("os.version.major", major); dict->SetInteger("os.version.minor", minor); dict->SetInteger("os.version.bugfix", bugfix); dict->SetString("os.arch", base::SysInfo::OperatingSystemArchitecture()); #endif } void GatherChromeValues(base::DictionaryValue* dict) { for (int index = 0; index != model_->ResourceCount(); ++index) { auto pid = model_->GetProcessId(index); auto tabs_key = Key(pid, "tabs"); base::ListValue* tabs; if (!dict->GetList(tabs_key, &tabs)) { tabs = new base::ListValue; dict->Set(tabs_key, tabs); tabs->AppendString(model_->GetResourceTitle(index)); dict->SetInteger(Key(pid, "memory.physical"), InMBFromB(model_.get(), &TaskManagerModel::GetPhysicalMemory, index)); dict->SetInteger(Key(pid, "memory.private"), InMBFromB(model_.get(), &TaskManagerModel::GetPrivateMemory, index)); dict->SetInteger(Key(pid, "memory.shared"), InMBFromB(model_.get(), &TaskManagerModel::GetSharedMemory, index)); dict->SetInteger(Key(pid, "memory.video"), InMBFromB(model_.get(), &TaskManagerModel::GetVideoMemory, index)); dict->SetInteger(Key(pid, "memory.V8.total"), InMBFromB(model_.get(), &TaskManagerModel::GetV8Memory, index)); dict->SetInteger(Key(pid, "memory.V8.used"), InMBFromB(model_.get(), &TaskManagerModel::GetV8MemoryUsed, index)); dict->SetInteger(Key(pid, "memory.sqlite"), InMBFromB(model_.get(), &TaskManagerModel::GetSqliteMemoryUsedBytes, index)); dict->SetString(Key(pid, "uptime"), ProcessUptime(model_->GetProcess(index))); } else { // TODO(cpu): Probably best to write the MD5 hash of the title. tabs->AppendString(model_->GetResourceTitle(index)); } } } std::string ProcessUptime(base::ProcessHandle process) { #if defined(OS_WIN) FILETIME creation_time; FILETIME exit_time; FILETIME kernel_time; FILETIME user_time; if (!GetProcessTimes(process, &creation_time, &exit_time, &kernel_time, &user_time)) return std::string("~"); auto ct_delta = base::Time::Now() - base::Time::FromFileTime(creation_time); return base::StringPrintf("%lld", ct_delta.InSeconds()); #else return std::string(); #endif } scoped_refptr model_; base::File file_; }; } // namespace namespace caps { void GenerateStateJSON( scoped_refptr model, base::File file) { new TaskManagerDataDumper(model, file.Pass()); } }