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