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