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>
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() {
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";
38 // If the process isn't found then sysctl returns a length of 0.
45 static bool GetExecutableNameForProcessID(
47 std::string
* executable_name
) {
48 if (!executable_name
) {
53 static int 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";
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.
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
) {
80 executable_name
->resize(end_pos
);
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
) {
118 for (int i
= 0; i
< shift
; i
++)
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
)));
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.";
151 std::istringstream
in(output
, std::istringstream::in
);
153 // Process lines until done.
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
164 ProcessInfoSnapshot::ProcInfoEntry proc_info
= proc_info_entries
[pid
];
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.
173 LOG(ERROR
) << "Error parsing output from " << kProgram
.value() << ".";
177 if (!proc_info
.pid
|| ! proc_info
.vsize
) {
178 LOG(WARNING
) << "Invalid data from " << kProgram
.value() << ".";
182 // Record the process information.
183 proc_info_entries
[proc_info
.pid
] = proc_info
;
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");
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.";
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
);
221 while (std::getline(top_in
, line
)) {
222 std::istringstream
in(line
, std::istringstream::in
);
224 // Try to read the PID.
230 // Make sure that caller is interested in this process.
231 if (proc_info_entries
.find(pid
) == proc_info_entries
.end())
234 // Skip the - or + sign that top puts after the pid.
239 for (i
= 0; i
< arraysize(values
); i
++) {
252 if (!ConvertByteUnitToScale(unit
[0], &scale
))
256 if (i
!= arraysize(values
))
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
;
271 bool ProcessInfoSnapshot::Sample(std::vector
<base::ProcessId
> pid_list
) {
274 // Nothing to do if no PIDs given.
275 if (pid_list
.empty())
277 if (pid_list
.size() > kMaxPidListSize
) {
278 // The spec says |pid_list| *must* not have more than this many entries.
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
;
290 if (!GetKInfoForProcessID(*it
, &kinfo
))
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
,
321 return memory_info_success
;
324 // Clear all the stored information.
325 void ProcessInfoSnapshot::Reset() {
326 proc_info_entries_
.clear();
329 ProcessInfoSnapshot::ProcInfoEntry::ProcInfoEntry()
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())
346 *proc_info
= it
->second
;
350 bool ProcessInfoSnapshot::GetCommittedKBytesOfPID(
352 base::CommittedKBytes
* usage
) const {
353 // Try to avoid crashing on a bug; stats aren't usually so crucial.
359 // Failure of |GetProcInfo()| is "normal", due to racing.
360 ProcInfoEntry proc_info
;
361 if (!GetProcInfo(pid
, &proc_info
)) {
368 usage
->priv
= proc_info
.vsize
/ 1024;
374 bool ProcessInfoSnapshot::GetWorkingSetKBytesOfPID(
376 base::WorkingSetKBytes
* ws_usage
) const {
377 // Try to avoid crashing on a bug; stats aren't usually so crucial.
383 // Failure of |GetProcInfo()| is "normal", due to racing.
384 ProcInfoEntry proc_info
;
385 if (!GetProcInfo(pid
, &proc_info
)) {
387 ws_usage
->shareable
= 0;
388 ws_usage
->shared
= 0;
392 ws_usage
->priv
= proc_info
.rprvt
/ 1024;
393 ws_usage
->shareable
= proc_info
.rss
/ 1024;
394 ws_usage
->shared
= proc_info
.rshrd
/ 1024;