blob: eef91e13fa740aa421fb2db29a42cd85d639d06b [file] [log] [blame]
// Copyright 2017 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/ui/webui/chromeos/sys_internals/sys_internals_message_handler.h"
#include <inttypes.h>
#include <cstdio>
#include <sstream>
#include <string>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/process/process_metrics.h"
#include "base/sys_info.h"
#include "content/public/browser/browser_thread.h"
namespace {
struct CpuInfo {
int kernel;
int user;
int idle;
int total;
};
// When counter overflow, it will restart from zero. base::Value do not
// supports 64-bit integer, and passing the counter as a double it may cause
// problems. Therefore, only use the last 31 bits of the counter and pass it
// as a 32-bit signed integer.
constexpr uint32_t COUNTER_MAX = 0x7FFFFFFFu;
template <typename T>
inline int ToCounter(T value) {
DCHECK_GE(value, T(0));
return static_cast<int>(value & COUNTER_MAX);
}
bool ParseProcStatLine(const std::string& line, std::vector<CpuInfo>* infos) {
DCHECK(infos);
uint64_t user = 0;
uint64_t nice = 0;
uint64_t sys = 0;
uint64_t idle = 0;
uint32_t cpu_index = 0;
int vals =
sscanf(line.c_str(),
"cpu%" PRIu32 " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64,
&cpu_index, &user, &nice, &sys, &idle);
if (vals != 5 || cpu_index >= infos->size()) {
NOTREACHED();
return false;
}
CpuInfo& cpu_info = (*infos)[cpu_index];
cpu_info.kernel = ToCounter(sys);
cpu_info.user = ToCounter(user + nice);
cpu_info.idle = ToCounter(idle);
cpu_info.total = ToCounter(sys + user + nice + idle);
return true;
}
bool GetCpuInfo(std::vector<CpuInfo>* infos) {
DCHECK(infos);
// WARNING: this method may return incomplete data because some processors may
// be brought offline at runtime. /proc/stat does not report statistics of
// offline processors. CPU usages of offline processors will be filled with
// zeros.
//
// An example of output of /proc/stat when processor 0 and 3 are online, but
// processor 1 and 2 are offline:
//
// cpu 145292 20018 83444 1485410 995 44 3578 0 0 0
// cpu0 138060 19947 78350 1479514 570 44 3576 0 0 0
// cpu3 2033 32 1075 1400 52 0 1 0 0 0
const char kProcStat[] = "/proc/stat";
std::string contents;
if (!base::ReadFileToString(base::FilePath(kProcStat), &contents))
return false;
std::istringstream iss(contents);
std::string line;
// Skip the first line because it is just an aggregated number of
// all cpuN lines.
std::getline(iss, line);
while (std::getline(iss, line)) {
if (line.compare(0, 3, "cpu") != 0)
continue;
if (!ParseProcStatLine(line, infos))
return false;
}
return true;
}
void SetConstValue(base::Value* result) {
DCHECK(result);
int counter_max = static_cast<int>(COUNTER_MAX);
result->SetPath({"const", "counterMax"}, base::Value(counter_max));
}
void SetCpusValue(const std::vector<CpuInfo>& infos, base::Value* result) {
DCHECK(result);
base::Value cpu_results(base::Value::Type::LIST);
for (const CpuInfo& cpu : infos) {
base::Value cpu_result(base::Value::Type::DICTIONARY);
cpu_result.SetKey("user", base::Value(cpu.user));
cpu_result.SetKey("kernel", base::Value(cpu.kernel));
cpu_result.SetKey("idle", base::Value(cpu.idle));
cpu_result.SetKey("total", base::Value(cpu.total));
cpu_results.GetList().push_back(std::move(cpu_result));
}
result->SetKey("cpus", std::move(cpu_results));
}
const double kBytesInKB = 1024;
double GetAvailablePhysicalMemory(const base::SystemMemoryInfoKB& info) {
double available = static_cast<double>(
info.available == 0 ? info.free + info.reclaimable : info.available);
return available * kBytesInKB;
}
void SetMemValue(const base::SystemMemoryInfoKB& info,
const base::VmStatInfo& vmstat,
base::Value* result) {
DCHECK(result);
base::Value mem_result(base::Value::Type::DICTIONARY);
// For values that may exceed the range of 32-bit signed integer, use double.
double total = static_cast<double>(info.total) * kBytesInKB;
mem_result.SetKey("total", base::Value(total));
mem_result.SetKey("available", base::Value(GetAvailablePhysicalMemory(info)));
double swap_total = static_cast<double>(info.swap_total) * kBytesInKB;
mem_result.SetKey("swapTotal", base::Value(swap_total));
double swap_free = static_cast<double>(info.swap_free) * kBytesInKB;
mem_result.SetKey("swapFree", base::Value(swap_free));
mem_result.SetKey("pswpin", base::Value(ToCounter(vmstat.pswpin)));
mem_result.SetKey("pswpout", base::Value(ToCounter(vmstat.pswpout)));
result->SetKey("memory", std::move(mem_result));
}
void SetZramValue(const base::SwapInfo& info, base::Value* result) {
DCHECK(result);
base::Value zram_result(base::Value::Type::DICTIONARY);
zram_result.SetKey("numReads", base::Value(ToCounter(info.num_reads)));
zram_result.SetKey("numWrites", base::Value(ToCounter(info.num_writes)));
// For values that may exceed the range of 32-bit signed integer, use double.
zram_result.SetKey("comprDataSize",
base::Value(static_cast<double>(info.compr_data_size)));
zram_result.SetKey("origDataSize",
base::Value(static_cast<double>(info.orig_data_size)));
zram_result.SetKey("memUsedTotal",
base::Value(static_cast<double>(info.mem_used_total)));
result->SetKey("zram", std::move(zram_result));
}
base::Value GetSysInfo() {
std::vector<CpuInfo> cpu_infos(base::SysInfo::NumberOfProcessors());
if (!GetCpuInfo(&cpu_infos)) {
DLOG(WARNING) << "Failed to get system CPU info.";
cpu_infos.clear();
}
base::SystemMemoryInfoKB mem_info;
if (!GetSystemMemoryInfo(&mem_info)) {
DLOG(WARNING) << "Failed to get system memory info.";
}
base::VmStatInfo vmstat_info;
if (!GetVmStatInfo(&vmstat_info)) {
DLOG(WARNING) << "Failed to get system vmstat info.";
}
base::SwapInfo swap_info;
if (!GetSwapInfo(&swap_info)) {
DLOG(WARNING) << ("Failed to get system zram info.");
}
base::Value result(base::Value::Type::DICTIONARY);
SetConstValue(&result);
SetCpusValue(cpu_infos, &result);
SetMemValue(mem_info, vmstat_info, &result);
SetZramValue(swap_info, &result);
return result;
}
} // namespace
SysInternalsMessageHandler::SysInternalsMessageHandler()
: weak_ptr_factory_(this) {}
SysInternalsMessageHandler::~SysInternalsMessageHandler() {}
void SysInternalsMessageHandler::RegisterMessages() {
web_ui()->RegisterMessageCallback(
"getSysInfo",
base::BindRepeating(&SysInternalsMessageHandler::HandleGetSysInfo,
base::Unretained(this)));
}
void SysInternalsMessageHandler::HandleGetSysInfo(const base::ListValue* args) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
DCHECK(args);
AllowJavascript();
const base::Value::ListStorage& list = args->GetList();
if (list.size() != 1 || !list[0].is_string()) {
NOTREACHED();
return;
}
base::Value callback_id = list[0].Clone();
base::PostTaskWithTraitsAndReplyWithResult(
FROM_HERE, base::MayBlock(), base::BindOnce(&GetSysInfo),
base::BindOnce(&SysInternalsMessageHandler::ReplySysInfo,
weak_ptr_factory_.GetWeakPtr(), std::move(callback_id)));
}
void SysInternalsMessageHandler::ReplySysInfo(base::Value callback_id,
base::Value result) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
ResolveJavascriptCallback(callback_id, result);
}