| // 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 <string> |
| #include <utility> |
| |
| #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<int>(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<TaskManagerDataDumper> { |
| public: |
| TaskManagerDataDumper(scoped_refptr<TaskManagerModel> 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>; |
| ~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<TaskManagerModel> model_; |
| base::File file_; |
| }; |
| |
| } // namespace |
| |
| namespace caps { |
| |
| void GenerateStateJSON( |
| scoped_refptr<TaskManagerModel> model, base::File file) { |
| new TaskManagerDataDumper(model, file.Pass()); |
| } |
| |
| } |