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/mac/mac_util.h"
15 #include "base/process/launch.h"
16 #include "base/strings/string_number_conversions.h"
17 #include "base/strings/string_util.h"
18 #include "base/threading/thread.h"
20 // Default constructor.
21 ProcessInfoSnapshot::ProcessInfoSnapshot() { }
23 // Destructor: just call |Reset()| to release everything.
24 ProcessInfoSnapshot::~ProcessInfoSnapshot() {
28 const size_t ProcessInfoSnapshot::kMaxPidListSize
= 1000;
30 static bool GetKInfoForProcessID(pid_t pid
, kinfo_proc
* kinfo
) {
31 int mib
[] = {CTL_KERN
, KERN_PROC
, KERN_PROC_PID
, pid
};
32 size_t len
= sizeof(*kinfo
);
33 if (sysctl(mib
, arraysize(mib
), kinfo
, &len
, NULL
, 0) != 0) {
34 PLOG(ERROR
) << "sysctl() for KERN_PROC";
39 // If the process isn't found then sysctl returns a length of 0.
46 static bool GetExecutableNameForProcessID(
48 std::string
* executable_name
) {
49 if (!executable_name
) {
54 static int s_arg_max
= 0;
56 int mib
[] = {CTL_KERN
, KERN_ARGMAX
};
57 size_t size
= sizeof(s_arg_max
);
58 if (sysctl(mib
, arraysize(mib
), &s_arg_max
, &size
, NULL
, 0) != 0)
59 PLOG(ERROR
) << "sysctl() for KERN_ARGMAX";
65 int mib
[] = {CTL_KERN
, KERN_PROCARGS
, pid
};
66 size_t size
= s_arg_max
;
67 executable_name
->resize(s_arg_max
+ 1);
68 if (sysctl(mib
, arraysize(mib
), &(*executable_name
)[0],
69 &size
, NULL
, 0) != 0) {
70 // Don't log the error since it's normal for this to fail.
74 // KERN_PROCARGS returns multiple NULL terminated strings. Truncate
75 // executable_name to just the first string.
76 size_t end_pos
= executable_name
->find('\0');
77 if (end_pos
== std::string::npos
) {
81 executable_name
->resize(end_pos
);
85 // Converts a byte unit such as 'K' or 'M' into the scale for the unit.
86 // The scale can then be used to calculate the number of bytes in a value.
87 // The units are based on humanize_number(). See:
88 // http://www.opensource.apple.com/source/libutil/libutil-21/humanize_number.c
89 static bool ConvertByteUnitToScale(char unit
, uint64_t* out_scale
) {
119 for (int i
= 0; i
< shift
; i
++)
126 // Capture the information by calling '/bin/ps'.
127 // Note: we ignore the "tsiz" (text size) display option of ps because it's
128 // always zero (tested on 10.5 and 10.6).
129 static bool GetProcessMemoryInfoUsingPS(
130 const std::vector
<base::ProcessId
>& pid_list
,
131 std::map
<int,ProcessInfoSnapshot::ProcInfoEntry
>& proc_info_entries
) {
132 const base::FilePath
kProgram("/bin/ps");
133 base::CommandLine
command_line(kProgram
);
135 // Get resident set size, virtual memory size.
136 command_line
.AppendArg("-o");
137 command_line
.AppendArg("pid=,rss=,vsz=");
138 // Only display the specified PIDs.
139 for (std::vector
<base::ProcessId
>::const_iterator it
= pid_list
.begin();
140 it
!= pid_list
.end(); ++it
) {
141 command_line
.AppendArg("-p");
142 command_line
.AppendArg(base::Int64ToString(static_cast<int64
>(*it
)));
146 // Limit output read to a megabyte for safety.
147 if (!base::GetAppOutputRestricted(command_line
, &output
, 1024 * 1024)) {
148 LOG(ERROR
) << "Failure running " << kProgram
.value() << " to acquire data.";
152 std::istringstream
in(output
, std::istringstream::in
);
154 // Process lines until done.
156 // The format is as specified above to ps (see ps(1)):
157 // "-o pid=,rss=,vsz=".
158 // Try to read the PID; if we get it, we should be able to get the rest of
165 ProcessInfoSnapshot::ProcInfoEntry proc_info
= proc_info_entries
[pid
];
168 in
>> proc_info
.vsize
;
169 proc_info
.rss
*= 1024; // Convert from kilobytes to bytes.
170 proc_info
.vsize
*= 1024;
172 // If the fail or bad bits were set, then there was an error reading input.
174 LOG(ERROR
) << "Error parsing output from " << kProgram
.value() << ".";
178 if (!proc_info
.pid
|| ! proc_info
.vsize
) {
179 LOG(WARNING
) << "Invalid data from " << kProgram
.value() << ".";
183 // Record the process information.
184 proc_info_entries
[proc_info
.pid
] = proc_info
;
186 // Ignore the rest of the line.
187 in
.ignore(std::numeric_limits
<std::streamsize
>::max(), '\n');
193 static bool GetProcessMemoryInfoUsingTop(
194 std::map
<int,ProcessInfoSnapshot::ProcInfoEntry
>& proc_info_entries
) {
195 const base::FilePath
kProgram("/usr/bin/top");
196 base::CommandLine
command_line(kProgram
);
198 // -stats tells top to print just the given fields as ordered.
199 command_line
.AppendArg("-stats");
200 command_line
.AppendArg("pid," // Process ID
201 "rsize," // Resident memory
202 "rshrd," // Resident shared memory
203 "rprvt," // Resident private memory
204 "vsize"); // Total virtual memory
205 // Run top in logging (non-interactive) mode.
206 command_line
.AppendArg("-l");
207 command_line
.AppendArg("1");
208 // Set the delay between updates to 0.
209 command_line
.AppendArg("-s");
210 command_line
.AppendArg("0");
213 // Limit output read to a megabyte for safety.
214 if (!base::GetAppOutputRestricted(command_line
, &output
, 1024 * 1024)) {
215 LOG(ERROR
) << "Failure running " << kProgram
.value() << " to acquire data.";
219 // Process lines until done. Lines should look something like this:
220 // PID RSIZE RSHRD RPRVT VSIZE
221 // 58539 1276K+ 336K+ 740K+ 2378M+
222 // 58485 1888K+ 592K+ 1332K+ 2383M+
223 std::istringstream
top_in(output
, std::istringstream::in
);
225 while (std::getline(top_in
, line
)) {
226 std::istringstream
in(line
, std::istringstream::in
);
228 // Try to read the PID.
234 // Make sure that caller is interested in this process.
235 if (proc_info_entries
.find(pid
) == proc_info_entries
.end())
238 // Skip the - or + sign that top puts after the pid.
243 for (i
= 0; i
< arraysize(values
); i
++) {
256 if (!ConvertByteUnitToScale(unit
[0], &scale
))
260 if (i
!= arraysize(values
))
263 ProcessInfoSnapshot::ProcInfoEntry proc_info
= proc_info_entries
[pid
];
264 proc_info
.rss
= values
[0];
265 proc_info
.rshrd
= values
[1];
266 proc_info
.rprvt
= values
[2];
267 proc_info
.vsize
= values
[3];
268 // Record the process information.
269 proc_info_entries
[proc_info
.pid
] = proc_info
;
275 bool ProcessInfoSnapshot::Sample(std::vector
<base::ProcessId
> pid_list
) {
278 // Nothing to do if no PIDs given.
279 if (pid_list
.empty())
281 if (pid_list
.size() > kMaxPidListSize
) {
282 // The spec says |pid_list| *must* not have more than this many entries.
287 // Get basic process info from KERN_PROC.
288 for (std::vector
<base::ProcessId
>::iterator it
= pid_list
.begin();
289 it
!= pid_list
.end(); ++it
) {
290 ProcInfoEntry proc_info
;
294 if (!GetKInfoForProcessID(*it
, &kinfo
))
297 proc_info
.ppid
= kinfo
.kp_eproc
.e_ppid
;
298 proc_info
.uid
= kinfo
.kp_eproc
.e_pcred
.p_ruid
;
299 proc_info
.euid
= kinfo
.kp_eproc
.e_ucred
.cr_uid
;
300 // Note, p_comm is truncated to 16 characters.
301 proc_info
.command
= kinfo
.kp_proc
.p_comm
;
302 proc_info_entries_
[*it
] = proc_info
;
305 // Use KERN_PROCARGS to get the full executable name. This may fail if this
306 // process doesn't have privileges to inspect the target process.
307 for (std::vector
<base::ProcessId
>::iterator it
= pid_list
.begin();
308 it
!= pid_list
.end(); ++it
) {
309 std::string exectuable_name
;
310 if (GetExecutableNameForProcessID(*it
, &exectuable_name
)) {
311 ProcInfoEntry proc_info
= proc_info_entries_
[*it
];
312 proc_info
.command
= exectuable_name
;
316 // In OSX 10.9+, top no longer returns any useful information. 'rshrd' is no
317 // longer supported, and 'rprvt' and 'vsize' return N/A. 'rsize' still works,
318 // but the information is also available from ps.
319 // http://crbug.com/383553
320 if (base::mac::IsOSMavericksOrLater())
321 return GetProcessMemoryInfoUsingPS(pid_list
, proc_info_entries_
);
323 // Get memory information using top.
324 bool memory_info_success
= GetProcessMemoryInfoUsingTop(proc_info_entries_
);
326 // If top didn't work then fall back to ps.
327 if (!memory_info_success
) {
328 memory_info_success
= GetProcessMemoryInfoUsingPS(pid_list
,
332 return memory_info_success
;
335 // Clear all the stored information.
336 void ProcessInfoSnapshot::Reset() {
337 proc_info_entries_
.clear();
340 ProcessInfoSnapshot::ProcInfoEntry::ProcInfoEntry()
351 bool ProcessInfoSnapshot::GetProcInfo(int pid
,
352 ProcInfoEntry
* proc_info
) const {
353 std::map
<int,ProcInfoEntry
>::const_iterator it
= proc_info_entries_
.find(pid
);
354 if (it
== proc_info_entries_
.end())
357 *proc_info
= it
->second
;
361 bool ProcessInfoSnapshot::GetCommittedKBytesOfPID(
363 base::CommittedKBytes
* usage
) const {
364 // Try to avoid crashing on a bug; stats aren't usually so crucial.
370 // Failure of |GetProcInfo()| is "normal", due to racing.
371 ProcInfoEntry proc_info
;
372 if (!GetProcInfo(pid
, &proc_info
)) {
379 usage
->priv
= proc_info
.vsize
/ 1024;
385 bool ProcessInfoSnapshot::GetWorkingSetKBytesOfPID(
387 base::WorkingSetKBytes
* ws_usage
) const {
388 // Try to avoid crashing on a bug; stats aren't usually so crucial.
394 // Failure of |GetProcInfo()| is "normal", due to racing.
395 ProcInfoEntry proc_info
;
396 if (!GetProcInfo(pid
, &proc_info
)) {
398 ws_usage
->shareable
= 0;
399 ws_usage
->shared
= 0;
403 ws_usage
->priv
= proc_info
.rprvt
/ 1024;
404 ws_usage
->shareable
= proc_info
.rss
/ 1024;
405 ws_usage
->shared
= proc_info
.rshrd
/ 1024;