Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / process_info_snapshot_mac.cc
blob36d505c57032ed5d6cb4680c95f293e5fd4fc16e
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/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"
19 // Default constructor.
20 ProcessInfoSnapshot::ProcessInfoSnapshot() { }
22 // Destructor: just call |Reset()| to release everything.
23 ProcessInfoSnapshot::~ProcessInfoSnapshot() {
24 Reset();
27 const size_t ProcessInfoSnapshot::kMaxPidListSize = 1000;
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;
37 if (len == 0) {
38 // If the process isn't found then sysctl returns a length of 0.
39 return false;
42 return true;
45 static bool GetExecutableNameForProcessID(
46 pid_t pid,
47 std::string* executable_name) {
48 if (!executable_name) {
49 NOTREACHED();
50 return false;
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";
61 if (s_arg_max == 0)
62 return false;
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;
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;
80 executable_name->resize(end_pos);
81 return true;
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;
117 uint64_t scale = 1;
118 for (int i = 0; i < shift; i++)
119 scale *= 1024;
120 *out_scale = scale;
122 return true;
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);
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)));
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;
151 std::istringstream in(output, std::istringstream::in);
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;
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;
177 if (!proc_info.pid || ! proc_info.vsize) {
178 LOG(WARNING) << "Invalid data from " << kProgram.value() << ".";
179 return false;
182 // Record the process information.
183 proc_info_entries[proc_info.pid] = proc_info;
186 return true;
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);
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");
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;
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);
224 // Try to read the PID.
225 pid_t pid;
226 in >> pid;
227 if (in.fail())
228 continue;
230 // Make sure that caller is interested in this process.
231 if (proc_info_entries.find(pid) == proc_info_entries.end())
232 continue;
234 // Skip the - or + sign that top puts after the pid.
235 in.get();
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;
248 if (unit.empty())
249 break;
251 uint64_t scale;
252 if (!ConvertByteUnitToScale(unit[0], &scale))
253 break;
254 values[i] *= scale;
256 if (i != arraysize(values))
257 continue;
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;
268 return true;
271 bool ProcessInfoSnapshot::Sample(std::vector<base::ProcessId> pid_list) {
272 Reset();
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;
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;
289 kinfo_proc kinfo;
290 if (!GetKInfoForProcessID(*it, &kinfo))
291 return false;
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;
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;
312 // Get memory information using top.
313 bool memory_info_success = GetProcessMemoryInfoUsingTop(proc_info_entries_);
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_);
321 return memory_info_success;
324 // Clear all the stored information.
325 void ProcessInfoSnapshot::Reset() {
326 proc_info_entries_.clear();
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) {
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;
346 *proc_info = it->second;
347 return true;
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;
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;
368 usage->priv = proc_info.vsize / 1024;
369 usage->mapped = 0;
370 usage->image = 0;
371 return true;
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;
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;
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;