blob: 74d93bf6ac039c5917fe73804c0f20432e27ea38 [file] [log] [blame]
[email protected]a40ca4302011-05-14 01:10:241// Copyright (c) 2011 The Chromium Authors. All rights reserved.
[email protected]f164cea2009-11-05 23:37:402// 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
[email protected]b23d0132011-01-12 00:20:457#include <sys/sysctl.h>
8
[email protected]f164cea2009-11-05 23:37:409#include <sstream>
10
[email protected]5d91c9e2010-07-28 17:25:2811#include "base/command_line.h"
[email protected]983ef7f2010-01-04 16:17:1312#include "base/logging.h"
[email protected]ba64e2b2011-06-14 18:18:3813#include "base/mac/mac_util.h"
[email protected]528c56d2010-07-30 19:28:4414#include "base/string_number_conversions.h"
[email protected]f164cea2009-11-05 23:37:4015#include "base/string_util.h"
[email protected]34b99632011-01-01 01:01:0616#include "base/threading/thread.h"
[email protected]f164cea2009-11-05 23:37:4017
[email protected]f164cea2009-11-05 23:37:4018// Default constructor.
19ProcessInfoSnapshot::ProcessInfoSnapshot() { }
20
21// Destructor: just call |Reset()| to release everything.
22ProcessInfoSnapshot::~ProcessInfoSnapshot() {
23 Reset();
24}
25
[email protected]983ef7f2010-01-04 16:17:1326const size_t ProcessInfoSnapshot::kMaxPidListSize = 1000;
27
[email protected]b23d0132011-01-12 00:20:4528static bool GetKInfoForProcessID(pid_t pid, kinfo_proc* kinfo) {
29 int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
30 size_t len = sizeof(*kinfo);
31 if (sysctl(mib, arraysize(mib), kinfo, &len, NULL, 0) != 0) {
32 PLOG(ERROR) << "sysctl() for KERN_PROC";
33 return false;
34 }
[email protected]f164cea2009-11-05 23:37:4035
[email protected]b23d0132011-01-12 00:20:4536 if (len == 0) {
37 // If the process isn't found then sysctl returns a length of 0.
38 return false;
39 }
40
41 return true;
42}
43
44static bool GetExecutableNameForProcessID(
45 pid_t pid,
46 std::string* executable_name) {
47 if (!executable_name) {
[email protected]983ef7f2010-01-04 16:17:1348 NOTREACHED();
49 return false;
50 }
51
[email protected]b23d0132011-01-12 00:20:4552 static int s_arg_max = 0;
53 if (s_arg_max == 0) {
54 int mib[] = {CTL_KERN, KERN_ARGMAX};
55 size_t size = sizeof(s_arg_max);
56 if (sysctl(mib, arraysize(mib), &s_arg_max, &size, NULL, 0) != 0)
57 PLOG(ERROR) << "sysctl() for KERN_ARGMAX";
58 }
59
60 if (s_arg_max == 0)
61 return false;
62
63 int mib[] = {CTL_KERN, KERN_PROCARGS, pid};
64 size_t size = s_arg_max;
65 executable_name->resize(s_arg_max + 1);
66 if (sysctl(mib, arraysize(mib), &(*executable_name)[0],
67 &size, NULL, 0) != 0) {
68 // Don't log the error since it's normal for this to fail.
69 return false;
70 }
71
72 // KERN_PROCARGS returns multiple NULL terminated strings. Truncate
73 // executable_name to just the first string.
74 size_t end_pos = executable_name->find('\0');
75 if (end_pos == std::string::npos) {
76 return false;
77 }
78
79 executable_name->resize(end_pos);
80 return true;
81}
82
83// Converts a byte unit such as 'K' or 'M' into the scale for the unit.
84// The scale can then be used to calculate the number of bytes in a value.
85// The units are based on humanize_number(). See:
86// http://www.opensource.apple.com/source/libutil/libutil-21/humanize_number.c
87static bool ConvertByteUnitToScale(char unit, uint64_t* out_scale) {
88 int shift = 0;
89 switch (unit) {
90 case 'B':
91 shift = 0;
92 break;
93 case 'K':
94 case 'k':
95 shift = 1;
96 break;
97 case 'M':
98 shift = 2;
99 break;
100 case 'G':
101 shift = 3;
102 break;
103 case 'T':
104 shift = 4;
105 break;
106 case 'P':
107 shift = 5;
108 break;
109 case 'E':
110 shift = 6;
111 break;
112 default:
113 return false;
114 }
115
116 uint64_t scale = 1;
117 for (int i = 0; i < shift; i++)
118 scale *= 1024;
119 *out_scale = scale;
120
121 return true;
122}
123
124// Capture the information by calling '/bin/ps'.
125// Note: we ignore the "tsiz" (text size) display option of ps because it's
126// always zero (tested on 10.5 and 10.6).
127static bool GetProcessMemoryInfoUsingPS(
128 const std::vector<base::ProcessId>& pid_list,
129 std::map<int,ProcessInfoSnapshot::ProcInfoEntry>& proc_info_entries) {
[email protected]a40ca4302011-05-14 01:10:24130 const FilePath kProgram("/bin/ps");
131 CommandLine command_line(kProgram);
[email protected]b23d0132011-01-12 00:20:45132
133 // Get resident set size, virtual memory size.
[email protected]a40ca4302011-05-14 01:10:24134 command_line.AppendArg("-o");
135 command_line.AppendArg("pid=,rss=,vsz=");
[email protected]f164cea2009-11-05 23:37:40136 // Only display the specified PIDs.
[email protected]b23d0132011-01-12 00:20:45137 for (std::vector<base::ProcessId>::const_iterator it = pid_list.begin();
[email protected]a40ca4302011-05-14 01:10:24138 it != pid_list.end(); ++it) {
139 command_line.AppendArg("-p");
140 command_line.AppendArg(base::Int64ToString(static_cast<int64>(*it)));
[email protected]f164cea2009-11-05 23:37:40141 }
142
143 std::string output;
[email protected]983ef7f2010-01-04 16:17:13144 // Limit output read to a megabyte for safety.
145 if (!base::GetAppOutputRestricted(command_line, &output, 1024 * 1024)) {
[email protected]a40ca4302011-05-14 01:10:24146 LOG(ERROR) << "Failure running " << kProgram.value() << " to acquire data.";
[email protected]f164cea2009-11-05 23:37:40147 return false;
148 }
149
150 std::istringstream in(output, std::istringstream::in);
151 std::string line;
152
153 // Process lines until done.
154 while (true) {
[email protected]f164cea2009-11-05 23:37:40155 // The format is as specified above to ps (see ps(1)):
[email protected]b23d0132011-01-12 00:20:45156 // "-o pid=,rss=,vsz=".
[email protected]f164cea2009-11-05 23:37:40157 // Try to read the PID; if we get it, we should be able to get the rest of
158 // the line.
[email protected]b23d0132011-01-12 00:20:45159 pid_t pid;
160 in >> pid;
[email protected]f164cea2009-11-05 23:37:40161 if (in.eof())
162 break;
[email protected]b23d0132011-01-12 00:20:45163
164 ProcessInfoSnapshot::ProcInfoEntry proc_info = proc_info_entries[pid];
165 proc_info.pid = pid;
[email protected]f164cea2009-11-05 23:37:40166 in >> proc_info.rss;
167 in >> proc_info.vsize;
[email protected]b23d0132011-01-12 00:20:45168 proc_info.rss *= 1024; // Convert from kilobytes to bytes.
169 proc_info.vsize *= 1024;
[email protected]f164cea2009-11-05 23:37:40170 in.ignore(1, ' '); // Eat the space.
171 std::getline(in, proc_info.command); // Get the rest of the line.
172 if (!in.good()) {
[email protected]a40ca4302011-05-14 01:10:24173 LOG(ERROR) << "Error parsing output from " << kProgram.value() << ".";
[email protected]f164cea2009-11-05 23:37:40174 return false;
175 }
176
[email protected]f164cea2009-11-05 23:37:40177 if (!proc_info.pid || ! proc_info.vsize) {
[email protected]a40ca4302011-05-14 01:10:24178 LOG(WARNING) << "Invalid data from " << kProgram.value() << ".";
[email protected]f164cea2009-11-05 23:37:40179 return false;
180 }
181
182 // Record the process information.
[email protected]b23d0132011-01-12 00:20:45183 proc_info_entries[proc_info.pid] = proc_info;
[email protected]f164cea2009-11-05 23:37:40184 }
185
186 return true;
187}
188
[email protected]b23d0132011-01-12 00:20:45189static bool GetProcessMemoryInfoUsingTop(
190 std::map<int,ProcessInfoSnapshot::ProcInfoEntry>& proc_info_entries) {
[email protected]a40ca4302011-05-14 01:10:24191 const FilePath kProgram("/usr/bin/top");
192 CommandLine command_line(kProgram);
[email protected]b23d0132011-01-12 00:20:45193
194 // -stats tells top to print just the given fields as ordered.
[email protected]a40ca4302011-05-14 01:10:24195 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
[email protected]b23d0132011-01-12 00:20:45201 // Run top in logging (non-interactive) mode.
[email protected]a40ca4302011-05-14 01:10:24202 command_line.AppendArg("-l");
203 command_line.AppendArg("1");
[email protected]b23d0132011-01-12 00:20:45204 // Set the delay between updates to 0.
[email protected]a40ca4302011-05-14 01:10:24205 command_line.AppendArg("-s");
206 command_line.AppendArg("0");
[email protected]b23d0132011-01-12 00:20:45207
208 std::string output;
[email protected]b23d0132011-01-12 00:20:45209 // Limit output read to a megabyte for safety.
210 if (!base::GetAppOutputRestricted(command_line, &output, 1024 * 1024)) {
[email protected]a40ca4302011-05-14 01:10:24211 LOG(ERROR) << "Failure running " << kProgram.value() << " to acquire data.";
[email protected]b23d0132011-01-12 00:20:45212 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
[email protected]f6b8ce32011-03-02 00:03:18248 if (unit.empty())
[email protected]b23d0132011-01-12 00:20:45249 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
271static bool GetProcessMemoryInfoUsingTop_10_5(
272 std::map<int,ProcessInfoSnapshot::ProcInfoEntry>& proc_info_entries) {
[email protected]a40ca4302011-05-14 01:10:24273 const FilePath kProgram("/usr/bin/top");
274 CommandLine command_line(kProgram);
[email protected]b23d0132011-01-12 00:20:45275
276 // -p tells top to print just the given fields as ordered.
[email protected]a40ca4302011-05-14 01:10:24277 command_line.AppendArg("-p");
278 command_line.AppendArg(
279 "^aaaaaaaaaaaaaaaaaaaa " // Process ID (PID)
280 "^jjjjjjjjjjjjjjjjjjjj " // Resident memory (RSIZE)
281 "^iiiiiiiiiiiiiiiiiiii " // Resident shared memory (RSHRD)
282 "^hhhhhhhhhhhhhhhhhhhh " // Resident private memory (RPRVT)
283 "^llllllllllllllllllll"); // Total virtual memory (VSIZE)
[email protected]b23d0132011-01-12 00:20:45284 // Run top in logging (non-interactive) mode.
[email protected]a40ca4302011-05-14 01:10:24285 command_line.AppendArg("-l");
286 command_line.AppendArg("1");
[email protected]b23d0132011-01-12 00:20:45287 // Set the delay between updates to 0.
[email protected]a40ca4302011-05-14 01:10:24288 command_line.AppendArg("-s");
289 command_line.AppendArg("0");
[email protected]b23d0132011-01-12 00:20:45290
291 std::string output;
[email protected]b23d0132011-01-12 00:20:45292 // Limit output read to a megabyte for safety.
293 if (!base::GetAppOutputRestricted(command_line, &output, 1024 * 1024)) {
[email protected]a40ca4302011-05-14 01:10:24294 LOG(ERROR) << "Failure running " << kProgram.value() << " to acquire data.";
[email protected]b23d0132011-01-12 00:20:45295 return false;
296 }
297
298 // Process lines until done. Lines should look something like this:
299 // PID RSIZE RSHRD RPRVT VSIZE
300 // 16943 815104 262144 290816 18489344
301 // 16922 954368 720896 278528 18976768
302 std::istringstream top_in(output, std::istringstream::in);
303 std::string line;
304 while (std::getline(top_in, line)) {
305 std::istringstream in(line, std::istringstream::in);
306
307 // Try to read the PID.
308 pid_t pid;
309 in >> pid;
310 if (in.fail())
311 continue;
312
313 // Make sure that caller is interested in this process.
314 if (proc_info_entries.find(pid) == proc_info_entries.end())
315 continue;
316
317 uint64_t values[4];
318 size_t i;
319 for (i = 0; i < arraysize(values); i++) {
320 in >> values[i];
321 if (in.fail())
322 break;
323 }
324 if (i != arraysize(values))
325 continue;
326
327 ProcessInfoSnapshot::ProcInfoEntry proc_info = proc_info_entries[pid];
328 proc_info.rss = values[0];
329 proc_info.rshrd = values[1];
330 proc_info.rprvt = values[2];
331 proc_info.vsize = values[3];
332 // Record the process information.
333 proc_info_entries[proc_info.pid] = proc_info;
334 }
335
336 return true;
337}
338
339
340bool ProcessInfoSnapshot::Sample(std::vector<base::ProcessId> pid_list) {
341 Reset();
342
343 // Nothing to do if no PIDs given.
[email protected]f6b8ce32011-03-02 00:03:18344 if (pid_list.empty())
[email protected]b23d0132011-01-12 00:20:45345 return true;
346 if (pid_list.size() > kMaxPidListSize) {
347 // The spec says |pid_list| *must* not have more than this many entries.
348 NOTREACHED();
349 return false;
350 }
351
352 // Get basic process info from KERN_PROC.
353 for (std::vector<base::ProcessId>::iterator it = pid_list.begin();
354 it != pid_list.end(); ++it) {
355 ProcInfoEntry proc_info;
356 proc_info.pid = *it;
357
358 kinfo_proc kinfo;
359 if (!GetKInfoForProcessID(*it, &kinfo))
360 return false;
361
362 proc_info.ppid = kinfo.kp_eproc.e_ppid;
363 proc_info.uid = kinfo.kp_eproc.e_pcred.p_ruid;
364 proc_info.euid = kinfo.kp_eproc.e_ucred.cr_uid;
365 // Note, p_comm is truncated to 16 characters.
366 proc_info.command = kinfo.kp_proc.p_comm;
367 proc_info_entries_[*it] = proc_info;
368 }
369
370 // Use KERN_PROCARGS to get the full executable name. This may fail if this
371 // process doesn't have privileges to inspect the target process.
372 for (std::vector<base::ProcessId>::iterator it = pid_list.begin();
373 it != pid_list.end(); ++it) {
374 std::string exectuable_name;
375 if (GetExecutableNameForProcessID(*it, &exectuable_name)) {
376 ProcInfoEntry proc_info = proc_info_entries_[*it];
377 proc_info.command = exectuable_name;
378 }
379 }
380
381 // Get memory information using top.
382 bool memory_info_success = false;
[email protected]ba64e2b2011-06-14 18:18:38383 if (base::mac::IsOSLeopardOrEarlier())
[email protected]b23d0132011-01-12 00:20:45384 memory_info_success = GetProcessMemoryInfoUsingTop_10_5(proc_info_entries_);
[email protected]ba64e2b2011-06-14 18:18:38385 else
[email protected]b23d0132011-01-12 00:20:45386 memory_info_success = GetProcessMemoryInfoUsingTop(proc_info_entries_);
387
388 // If top didn't work then fall back to ps.
389 if (!memory_info_success) {
390 memory_info_success = GetProcessMemoryInfoUsingPS(pid_list,
391 proc_info_entries_);
392 }
393
394 return memory_info_success;
395}
396
[email protected]f164cea2009-11-05 23:37:40397// Clear all the stored information.
398void ProcessInfoSnapshot::Reset() {
399 proc_info_entries_.clear();
400}
401
402bool ProcessInfoSnapshot::GetProcInfo(int pid,
403 ProcInfoEntry* proc_info) const {
404 std::map<int,ProcInfoEntry>::const_iterator it = proc_info_entries_.find(pid);
405 if (it == proc_info_entries_.end())
406 return false;
407
408 *proc_info = it->second;
409 return true;
410}
411
412bool ProcessInfoSnapshot::GetCommittedKBytesOfPID(
413 int pid,
414 base::CommittedKBytes* usage) const {
415 // Try to avoid crashing on a bug; stats aren't usually so crucial.
416 if (!usage) {
417 NOTREACHED();
418 return false;
419 }
420
421 // Failure of |GetProcInfo()| is "normal", due to racing.
422 ProcInfoEntry proc_info;
423 if (!GetProcInfo(pid, &proc_info)) {
424 usage->priv = 0;
425 usage->mapped = 0;
426 usage->image = 0;
427 return false;
428 }
429
[email protected]b23d0132011-01-12 00:20:45430 usage->priv = proc_info.vsize / 1024;
[email protected]f164cea2009-11-05 23:37:40431 usage->mapped = 0;
432 usage->image = 0;
433 return true;
434}
435
436bool ProcessInfoSnapshot::GetWorkingSetKBytesOfPID(
437 int pid,
438 base::WorkingSetKBytes* ws_usage) const {
439 // Try to avoid crashing on a bug; stats aren't usually so crucial.
440 if (!ws_usage) {
441 NOTREACHED();
442 return false;
443 }
444
445 // Failure of |GetProcInfo()| is "normal", due to racing.
446 ProcInfoEntry proc_info;
447 if (!GetProcInfo(pid, &proc_info)) {
448 ws_usage->priv = 0;
449 ws_usage->shareable = 0;
450 ws_usage->shared = 0;
451 return false;
452 }
453
[email protected]b23d0132011-01-12 00:20:45454 ws_usage->priv = proc_info.rprvt / 1024;
455 ws_usage->shareable = proc_info.rss / 1024;
456 ws_usage->shared = proc_info.rshrd / 1024;
[email protected]f164cea2009-11-05 23:37:40457 return true;
458}