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/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() {
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";
36 // If the process isn't found then sysctl returns a length of 0.
43 static bool GetExecutableNameForProcessID(
45 std::string
* executable_name
) {
46 if (!executable_name
) {
51 static int 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";
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.
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
) {
78 executable_name
->resize(end_pos
);
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
) {
116 for (int i
= 0; i
< shift
; i
++)
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
)));
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.";
149 std::istringstream
in(output
, std::istringstream::in
);
152 // Process lines until done.
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
163 ProcessInfoSnapshot::ProcInfoEntry proc_info
= proc_info_entries
[pid
];
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.
172 LOG(ERROR
) << "Error parsing output from " << kProgram
.value() << ".";
176 if (!proc_info
.pid
|| ! proc_info
.vsize
) {
177 LOG(WARNING
) << "Invalid data from " << kProgram
.value() << ".";
181 // Record the process information.
182 proc_info_entries
[proc_info
.pid
] = proc_info
;
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");
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.";
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
);
220 while (std::getline(top_in
, line
)) {
221 std::istringstream
in(line
, std::istringstream::in
);
223 // Try to read the PID.
229 // Make sure that caller is interested in this process.
230 if (proc_info_entries
.find(pid
) == proc_info_entries
.end())
233 // Skip the - or + sign that top puts after the pid.
238 for (i
= 0; i
< arraysize(values
); i
++) {
251 if (!ConvertByteUnitToScale(unit
[0], &scale
))
255 if (i
!= arraysize(values
))
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
;
270 bool ProcessInfoSnapshot::Sample(std::vector
<base::ProcessId
> pid_list
) {
273 // Nothing to do if no PIDs given.
274 if (pid_list
.empty())
276 if (pid_list
.size() > kMaxPidListSize
) {
277 // The spec says |pid_list| *must* not have more than this many entries.
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
;
289 if (!GetKInfoForProcessID(*it
, &kinfo
))
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
,
320 return memory_info_success
;
323 // Clear all the stored information.
324 void ProcessInfoSnapshot::Reset() {
325 proc_info_entries_
.clear();
328 ProcessInfoSnapshot::ProcInfoEntry::ProcInfoEntry()
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())
345 *proc_info
= it
->second
;
349 bool ProcessInfoSnapshot::GetCommittedKBytesOfPID(
351 base::CommittedKBytes
* usage
) const {
352 // Try to avoid crashing on a bug; stats aren't usually so crucial.
358 // Failure of |GetProcInfo()| is "normal", due to racing.
359 ProcInfoEntry proc_info
;
360 if (!GetProcInfo(pid
, &proc_info
)) {
367 usage
->priv
= proc_info
.vsize
/ 1024;
373 bool ProcessInfoSnapshot::GetWorkingSetKBytesOfPID(
375 base::WorkingSetKBytes
* ws_usage
) const {
376 // Try to avoid crashing on a bug; stats aren't usually so crucial.
382 // Failure of |GetProcInfo()| is "normal", due to racing.
383 ProcInfoEntry proc_info
;
384 if (!GetProcInfo(pid
, &proc_info
)) {
386 ws_usage
->shareable
= 0;
387 ws_usage
->shared
= 0;
391 ws_usage
->priv
= proc_info
.rprvt
/ 1024;
392 ws_usage
->shareable
= proc_info
.rss
/ 1024;
393 ws_usage
->shared
= proc_info
.rshrd
/ 1024;