1 // Copyright 2017 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/ui/webui/chromeos/sys_internals/sys_internals_message_handler.h"
6
7 #include <inttypes.h>
8 #include <cstdio>
9 #include <sstream>
10 #include <string>
11 #include <utility>
12 #include <vector>
13
14 #include "base/bind.h"
15 #include "base/callback_helpers.h"
16 #include "base/files/file_util.h"
17 #include "base/logging.h"
18 #include "base/process/process_metrics.h"
19 #include "base/system/sys_info.h"
20 #include "base/task/thread_pool.h"
21 #include "content/public/browser/browser_thread.h"
22
23 namespace {
24
25 struct CpuInfo {
26 int kernel;
27 int user;
28 int idle;
29 int total;
30 };
31
32 // When counter overflow, it will restart from zero. base::Value do not
33 // supports 64-bit integer, and passing the counter as a double it may cause
34 // problems. Therefore, only use the last 31 bits of the counter and pass it
35 // as a 32-bit signed integer.
36 constexpr uint32_t COUNTER_MAX = 0x7FFFFFFFu;
37
38 template <typename T>
ToCounter(T value)39 inline int ToCounter(T value) {
40 DCHECK_GE(value, T(0));
41 return static_cast<int>(value & COUNTER_MAX);
42 }
43
ParseProcStatLine(const std::string & line,std::vector<CpuInfo> * infos)44 bool ParseProcStatLine(const std::string& line, std::vector<CpuInfo>* infos) {
45 DCHECK(infos);
46 uint64_t user = 0;
47 uint64_t nice = 0;
48 uint64_t sys = 0;
49 uint64_t idle = 0;
50 uint32_t cpu_index = 0;
51 int vals =
52 sscanf(line.c_str(),
53 "cpu%" PRIu32 " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64,
54 &cpu_index, &user, &nice, &sys, &idle);
55 if (vals != 5 || cpu_index >= infos->size()) {
56 NOTREACHED();
57 return false;
58 }
59
60 CpuInfo& cpu_info = (*infos)[cpu_index];
61 cpu_info.kernel = ToCounter(sys);
62 cpu_info.user = ToCounter(user + nice);
63 cpu_info.idle = ToCounter(idle);
64 cpu_info.total = ToCounter(sys + user + nice + idle);
65
66 return true;
67 }
68
GetCpuInfo(std::vector<CpuInfo> * infos)69 bool GetCpuInfo(std::vector<CpuInfo>* infos) {
70 DCHECK(infos);
71
72 // WARNING: this method may return incomplete data because some processors may
73 // be brought offline at runtime. /proc/stat does not report statistics of
74 // offline processors. CPU usages of offline processors will be filled with
75 // zeros.
76 //
77 // An example of output of /proc/stat when processor 0 and 3 are online, but
78 // processor 1 and 2 are offline:
79 //
80 // cpu 145292 20018 83444 1485410 995 44 3578 0 0 0
81 // cpu0 138060 19947 78350 1479514 570 44 3576 0 0 0
82 // cpu3 2033 32 1075 1400 52 0 1 0 0 0
83 const char kProcStat[] = "/proc/stat";
84 std::string contents;
85 if (!base::ReadFileToString(base::FilePath(kProcStat), &contents))
86 return false;
87
88 std::istringstream iss(contents);
89 std::string line;
90
91 // Skip the first line because it is just an aggregated number of
92 // all cpuN lines.
93 std::getline(iss, line);
94 while (std::getline(iss, line)) {
95 if (line.compare(0, 3, "cpu") != 0)
96 continue;
97 if (!ParseProcStatLine(line, infos))
98 return false;
99 }
100
101 return true;
102 }
103
SetConstValue(base::Value * result)104 void SetConstValue(base::Value* result) {
105 DCHECK(result);
106 int counter_max = static_cast<int>(COUNTER_MAX);
107 result->SetPath({"const", "counterMax"}, base::Value(counter_max));
108 }
109
SetCpusValue(const std::vector<CpuInfo> & infos,base::Value * result)110 void SetCpusValue(const std::vector<CpuInfo>& infos, base::Value* result) {
111 DCHECK(result);
112 base::Value cpu_results(base::Value::Type::LIST);
113 for (const CpuInfo& cpu : infos) {
114 base::Value cpu_result(base::Value::Type::DICTIONARY);
115 cpu_result.SetKey("user", base::Value(cpu.user));
116 cpu_result.SetKey("kernel", base::Value(cpu.kernel));
117 cpu_result.SetKey("idle", base::Value(cpu.idle));
118 cpu_result.SetKey("total", base::Value(cpu.total));
119 cpu_results.Append(std::move(cpu_result));
120 }
121 result->SetKey("cpus", std::move(cpu_results));
122 }
123
124 const double kBytesInKB = 1024;
125
GetAvailablePhysicalMemory(const base::SystemMemoryInfoKB & info)126 double GetAvailablePhysicalMemory(const base::SystemMemoryInfoKB& info) {
127 double available = static_cast<double>(
128 info.available == 0 ? info.free + info.reclaimable : info.available);
129
130 return available * kBytesInKB;
131 }
132
SetMemValue(const base::SystemMemoryInfoKB & info,const base::VmStatInfo & vmstat,base::Value * result)133 void SetMemValue(const base::SystemMemoryInfoKB& info,
134 const base::VmStatInfo& vmstat,
135 base::Value* result) {
136 DCHECK(result);
137 base::Value mem_result(base::Value::Type::DICTIONARY);
138
139 // For values that may exceed the range of 32-bit signed integer, use double.
140 double total = static_cast<double>(info.total) * kBytesInKB;
141 mem_result.SetKey("total", base::Value(total));
142 mem_result.SetKey("available", base::Value(GetAvailablePhysicalMemory(info)));
143 double swap_total = static_cast<double>(info.swap_total) * kBytesInKB;
144 mem_result.SetKey("swapTotal", base::Value(swap_total));
145 double swap_free = static_cast<double>(info.swap_free) * kBytesInKB;
146 mem_result.SetKey("swapFree", base::Value(swap_free));
147
148 mem_result.SetKey("pswpin", base::Value(ToCounter(vmstat.pswpin)));
149 mem_result.SetKey("pswpout", base::Value(ToCounter(vmstat.pswpout)));
150
151 result->SetKey("memory", std::move(mem_result));
152 }
153
SetZramValue(const base::SwapInfo & info,base::Value * result)154 void SetZramValue(const base::SwapInfo& info, base::Value* result) {
155 DCHECK(result);
156 base::Value zram_result(base::Value::Type::DICTIONARY);
157
158 zram_result.SetKey("numReads", base::Value(ToCounter(info.num_reads)));
159 zram_result.SetKey("numWrites", base::Value(ToCounter(info.num_writes)));
160
161 // For values that may exceed the range of 32-bit signed integer, use double.
162 zram_result.SetKey("comprDataSize",
163 base::Value(static_cast<double>(info.compr_data_size)));
164 zram_result.SetKey("origDataSize",
165 base::Value(static_cast<double>(info.orig_data_size)));
166 zram_result.SetKey("memUsedTotal",
167 base::Value(static_cast<double>(info.mem_used_total)));
168
169 result->SetKey("zram", std::move(zram_result));
170 }
171
GetSysInfo()172 base::Value GetSysInfo() {
173 std::vector<CpuInfo> cpu_infos(base::SysInfo::NumberOfProcessors());
174 if (!GetCpuInfo(&cpu_infos)) {
175 DLOG(WARNING) << "Failed to get system CPU info.";
176 cpu_infos.clear();
177 }
178 base::SystemMemoryInfoKB mem_info;
179 if (!GetSystemMemoryInfo(&mem_info)) {
180 DLOG(WARNING) << "Failed to get system memory info.";
181 }
182 base::VmStatInfo vmstat_info;
183 if (!GetVmStatInfo(&vmstat_info)) {
184 DLOG(WARNING) << "Failed to get system vmstat info.";
185 }
186 base::SwapInfo swap_info;
187 if (!GetSwapInfo(&swap_info)) {
188 DLOG(WARNING) << ("Failed to get system zram info.");
189 }
190
191 base::Value result(base::Value::Type::DICTIONARY);
192 SetConstValue(&result);
193 SetCpusValue(cpu_infos, &result);
194 SetMemValue(mem_info, vmstat_info, &result);
195 SetZramValue(swap_info, &result);
196
197 return result;
198 }
199
200 } // namespace
201
SysInternalsMessageHandler()202 SysInternalsMessageHandler::SysInternalsMessageHandler() {}
203
~SysInternalsMessageHandler()204 SysInternalsMessageHandler::~SysInternalsMessageHandler() {}
205
RegisterMessages()206 void SysInternalsMessageHandler::RegisterMessages() {
207 web_ui()->RegisterMessageCallback(
208 "getSysInfo",
209 base::BindRepeating(&SysInternalsMessageHandler::HandleGetSysInfo,
210 base::Unretained(this)));
211 }
212
HandleGetSysInfo(const base::ListValue * args)213 void SysInternalsMessageHandler::HandleGetSysInfo(const base::ListValue* args) {
214 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
215 DCHECK(args);
216
217 AllowJavascript();
218 base::Value::ConstListView list = args->GetList();
219 if (list.size() != 1 || !list[0].is_string()) {
220 NOTREACHED();
221 return;
222 }
223
224 base::Value callback_id = list[0].Clone();
225 base::ThreadPool::PostTaskAndReplyWithResult(
226 FROM_HERE, {base::MayBlock()}, base::BindOnce(&GetSysInfo),
227 base::BindOnce(&SysInternalsMessageHandler::ReplySysInfo,
228 weak_ptr_factory_.GetWeakPtr(), std::move(callback_id)));
229 }
230
ReplySysInfo(base::Value callback_id,base::Value result)231 void SysInternalsMessageHandler::ReplySysInfo(base::Value callback_id,
232 base::Value result) {
233 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
234
235 ResolveJavascriptCallback(callback_id, result);
236 }
237