- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / process_info_snapshot_mac.cc
1 // Copyright (c) 2011 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/process_info_snapshot.h"
6
7 #include <sys/sysctl.h>
8
9 #include <sstream>
10
11 #include "base/command_line.h"
12 #include "base/files/file_path.h"
13 #include "base/logging.h"
14 #include "base/process/launch.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "base/strings/string_util.h"
17 #include "base/threading/thread.h"
18
19 // Default constructor.
20 ProcessInfoSnapshot::ProcessInfoSnapshot() { }
21
22 // Destructor: just call |Reset()| to release everything.
23 ProcessInfoSnapshot::~ProcessInfoSnapshot() {
24   Reset();
25 }
26
27 const size_t ProcessInfoSnapshot::kMaxPidListSize = 1000;
28
29 static bool GetKInfoForProcessID(pid_t pid, kinfo_proc* kinfo) {
30   int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
31   size_t len = sizeof(*kinfo);
32   if (sysctl(mib, arraysize(mib), kinfo, &len, NULL, 0) != 0) {
33     PLOG(ERROR) << "sysctl() for KERN_PROC";
34     return false;
35   }
36
37   if (len == 0) {
38     // If the process isn't found then sysctl returns a length of 0.
39     return false;
40   }
41
42   return true;
43 }
44
45 static bool GetExecutableNameForProcessID(
46     pid_t pid,
47     std::string* executable_name) {
48   if (!executable_name) {
49     NOTREACHED();
50     return false;
51   }
52
53   static int s_arg_max = 0;
54   if (s_arg_max == 0) {
55     int mib[] = {CTL_KERN, KERN_ARGMAX};
56     size_t size = sizeof(s_arg_max);
57     if (sysctl(mib, arraysize(mib), &s_arg_max, &size, NULL, 0) != 0)
58       PLOG(ERROR) << "sysctl() for KERN_ARGMAX";
59   }
60
61   if (s_arg_max == 0)
62     return false;
63
64   int mib[] = {CTL_KERN, KERN_PROCARGS, pid};
65   size_t size = s_arg_max;
66   executable_name->resize(s_arg_max + 1);
67   if (sysctl(mib, arraysize(mib), &(*executable_name)[0],
68              &size, NULL, 0) != 0) {
69     // Don't log the error since it's normal for this to fail.
70     return false;
71   }
72
73   // KERN_PROCARGS returns multiple NULL terminated strings. Truncate
74   // executable_name to just the first string.
75   size_t end_pos = executable_name->find('\0');
76   if (end_pos == std::string::npos) {
77     return false;
78   }
79
80   executable_name->resize(end_pos);
81   return true;
82 }
83
84 // Converts a byte unit such as 'K' or 'M' into the scale for the unit.
85 // The scale can then be used to calculate the number of bytes in a value.
86 // The units are based on humanize_number(). See:
87 // http://www.opensource.apple.com/source/libutil/libutil-21/humanize_number.c
88 static bool ConvertByteUnitToScale(char unit, uint64_t* out_scale) {
89   int shift = 0;
90   switch (unit) {
91     case 'B':
92       shift = 0;
93       break;
94     case 'K':
95     case 'k':
96       shift = 1;
97       break;
98     case 'M':
99       shift = 2;
100       break;
101     case 'G':
102       shift = 3;
103       break;
104     case 'T':
105       shift = 4;
106       break;
107     case 'P':
108       shift = 5;
109       break;
110     case 'E':
111       shift = 6;
112       break;
113     default:
114       return false;
115   }
116
117   uint64_t scale = 1;
118   for (int i = 0; i < shift; i++)
119     scale *= 1024;
120   *out_scale = scale;
121
122   return true;
123 }
124
125 // Capture the information by calling '/bin/ps'.
126 // Note: we ignore the "tsiz" (text size) display option of ps because it's
127 // always zero (tested on 10.5 and 10.6).
128 static bool GetProcessMemoryInfoUsingPS(
129     const std::vector<base::ProcessId>& pid_list,
130     std::map<int,ProcessInfoSnapshot::ProcInfoEntry>& proc_info_entries) {
131   const base::FilePath kProgram("/bin/ps");
132   CommandLine command_line(kProgram);
133
134   // Get resident set size, virtual memory size.
135   command_line.AppendArg("-o");
136   command_line.AppendArg("pid=,rss=,vsz=");
137   // Only display the specified PIDs.
138   for (std::vector<base::ProcessId>::const_iterator it = pid_list.begin();
139        it != pid_list.end(); ++it) {
140     command_line.AppendArg("-p");
141     command_line.AppendArg(base::Int64ToString(static_cast<int64>(*it)));
142   }
143
144   std::string output;
145   // Limit output read to a megabyte for safety.
146   if (!base::GetAppOutputRestricted(command_line, &output, 1024 * 1024)) {
147     LOG(ERROR) << "Failure running " << kProgram.value() << " to acquire data.";
148     return false;
149   }
150
151   std::istringstream in(output, std::istringstream::in);
152
153   // Process lines until done.
154   while (true) {
155     // The format is as specified above to ps (see ps(1)):
156     //   "-o pid=,rss=,vsz=".
157     // Try to read the PID; if we get it, we should be able to get the rest of
158     // the line.
159     pid_t pid;
160     in >> pid;
161     if (in.eof())
162       break;
163
164     ProcessInfoSnapshot::ProcInfoEntry proc_info = proc_info_entries[pid];
165     proc_info.pid = pid;
166     in >> proc_info.rss;
167     in >> proc_info.vsize;
168     proc_info.rss *= 1024;                // Convert from kilobytes to bytes.
169     proc_info.vsize *= 1024;
170     in.ignore(1, ' ');                    // Eat the space.
171     std::getline(in, proc_info.command);  // Get the rest of the line.
172     if (!in.good()) {
173       LOG(ERROR) << "Error parsing output from " << kProgram.value() << ".";
174       return false;
175     }
176
177     if (!proc_info.pid || ! proc_info.vsize) {
178       LOG(WARNING) << "Invalid data from " << kProgram.value() << ".";
179       return false;
180     }
181
182     // Record the process information.
183     proc_info_entries[proc_info.pid] = proc_info;
184   }
185
186   return true;
187 }
188
189 static bool GetProcessMemoryInfoUsingTop(
190     std::map<int,ProcessInfoSnapshot::ProcInfoEntry>& proc_info_entries) {
191   const base::FilePath kProgram("/usr/bin/top");
192   CommandLine command_line(kProgram);
193
194   // -stats tells top to print just the given fields as ordered.
195   command_line.AppendArg("-stats");
196   command_line.AppendArg("pid,"    // Process ID
197                          "rsize,"  // Resident memory
198                          "rshrd,"  // Resident shared memory
199                          "rprvt,"  // Resident private memory
200                          "vsize"); // Total virtual memory
201   // Run top in logging (non-interactive) mode.
202   command_line.AppendArg("-l");
203   command_line.AppendArg("1");
204   // Set the delay between updates to 0.
205   command_line.AppendArg("-s");
206   command_line.AppendArg("0");
207
208   std::string output;
209   // Limit output read to a megabyte for safety.
210   if (!base::GetAppOutputRestricted(command_line, &output, 1024 * 1024)) {
211     LOG(ERROR) << "Failure running " << kProgram.value() << " to acquire data.";
212     return false;
213   }
214
215   // Process lines until done. Lines should look something like this:
216   // PID    RSIZE  RSHRD  RPRVT  VSIZE
217   // 58539  1276K+ 336K+  740K+  2378M+
218   // 58485  1888K+ 592K+  1332K+ 2383M+
219   std::istringstream top_in(output, std::istringstream::in);
220   std::string line;
221   while (std::getline(top_in, line)) {
222     std::istringstream in(line, std::istringstream::in);
223
224     // Try to read the PID.
225     pid_t pid;
226     in >> pid;
227     if (in.fail())
228       continue;
229
230     // Make sure that caller is interested in this process.
231     if (proc_info_entries.find(pid) == proc_info_entries.end())
232       continue;
233
234     // Skip the - or + sign that top puts after the pid.
235     in.get();
236
237     uint64_t values[4];
238     size_t i;
239     for (i = 0; i < arraysize(values); i++) {
240       in >> values[i];
241       if (in.fail())
242         break;
243       std::string unit;
244       in >> unit;
245       if (in.fail())
246         break;
247
248       if (unit.empty())
249         break;
250
251       uint64_t scale;
252       if (!ConvertByteUnitToScale(unit[0], &scale))
253         break;
254       values[i] *= scale;
255     }
256     if (i != arraysize(values))
257       continue;
258
259     ProcessInfoSnapshot::ProcInfoEntry proc_info = proc_info_entries[pid];
260     proc_info.rss = values[0];
261     proc_info.rshrd = values[1];
262     proc_info.rprvt = values[2];
263     proc_info.vsize = values[3];
264     // Record the process information.
265     proc_info_entries[proc_info.pid] = proc_info;
266   }
267
268   return true;
269 }
270
271 bool ProcessInfoSnapshot::Sample(std::vector<base::ProcessId> pid_list) {
272   Reset();
273
274   // Nothing to do if no PIDs given.
275   if (pid_list.empty())
276     return true;
277   if (pid_list.size() > kMaxPidListSize) {
278     // The spec says |pid_list| *must* not have more than this many entries.
279     NOTREACHED();
280     return false;
281   }
282
283   // Get basic process info from KERN_PROC.
284   for (std::vector<base::ProcessId>::iterator it = pid_list.begin();
285        it != pid_list.end(); ++it) {
286     ProcInfoEntry proc_info;
287     proc_info.pid = *it;
288
289     kinfo_proc kinfo;
290     if (!GetKInfoForProcessID(*it, &kinfo))
291       return false;
292
293     proc_info.ppid = kinfo.kp_eproc.e_ppid;
294     proc_info.uid = kinfo.kp_eproc.e_pcred.p_ruid;
295     proc_info.euid = kinfo.kp_eproc.e_ucred.cr_uid;
296     // Note, p_comm is truncated to 16 characters.
297     proc_info.command = kinfo.kp_proc.p_comm;
298     proc_info_entries_[*it] = proc_info;
299   }
300
301   // Use KERN_PROCARGS to get the full executable name. This may fail if this
302   // process doesn't have privileges to inspect the target process.
303   for (std::vector<base::ProcessId>::iterator it = pid_list.begin();
304        it != pid_list.end(); ++it) {
305     std::string exectuable_name;
306     if (GetExecutableNameForProcessID(*it, &exectuable_name)) {
307       ProcInfoEntry proc_info = proc_info_entries_[*it];
308       proc_info.command = exectuable_name;
309     }
310   }
311
312   // Get memory information using top.
313   bool memory_info_success = GetProcessMemoryInfoUsingTop(proc_info_entries_);
314
315   // If top didn't work then fall back to ps.
316   if (!memory_info_success) {
317     memory_info_success = GetProcessMemoryInfoUsingPS(pid_list,
318                                                       proc_info_entries_);
319   }
320
321   return memory_info_success;
322 }
323
324 // Clear all the stored information.
325 void ProcessInfoSnapshot::Reset() {
326   proc_info_entries_.clear();
327 }
328
329 ProcessInfoSnapshot::ProcInfoEntry::ProcInfoEntry()
330     : pid(0),
331       ppid(0),
332       uid(0),
333       euid(0),
334       rss(0),
335       rshrd(0),
336       rprvt(0),
337       vsize(0) {
338 }
339
340 bool ProcessInfoSnapshot::GetProcInfo(int pid,
341                                       ProcInfoEntry* proc_info) const {
342   std::map<int,ProcInfoEntry>::const_iterator it = proc_info_entries_.find(pid);
343   if (it == proc_info_entries_.end())
344     return false;
345
346   *proc_info = it->second;
347   return true;
348 }
349
350 bool ProcessInfoSnapshot::GetCommittedKBytesOfPID(
351     int pid,
352     base::CommittedKBytes* usage) const {
353   // Try to avoid crashing on a bug; stats aren't usually so crucial.
354   if (!usage) {
355     NOTREACHED();
356     return false;
357   }
358
359   // Failure of |GetProcInfo()| is "normal", due to racing.
360   ProcInfoEntry proc_info;
361   if (!GetProcInfo(pid, &proc_info)) {
362     usage->priv = 0;
363     usage->mapped = 0;
364     usage->image = 0;
365     return false;
366   }
367
368   usage->priv = proc_info.vsize / 1024;
369   usage->mapped = 0;
370   usage->image = 0;
371   return true;
372 }
373
374 bool ProcessInfoSnapshot::GetWorkingSetKBytesOfPID(
375     int pid,
376     base::WorkingSetKBytes* ws_usage) const {
377   // Try to avoid crashing on a bug; stats aren't usually so crucial.
378   if (!ws_usage) {
379     NOTREACHED();
380     return false;
381   }
382
383   // Failure of |GetProcInfo()| is "normal", due to racing.
384   ProcInfoEntry proc_info;
385   if (!GetProcInfo(pid, &proc_info)) {
386     ws_usage->priv = 0;
387     ws_usage->shareable = 0;
388     ws_usage->shared = 0;
389     return false;
390   }
391
392   ws_usage->priv = proc_info.rprvt / 1024;
393   ws_usage->shareable = proc_info.rss / 1024;
394   ws_usage->shared = proc_info.rshrd / 1024;
395   return true;
396 }