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 "base/process_util.h"
13 #include <sys/types.h>
15 #include <sys/param.h>
16 #include <sys/sysctl.h>
21 #include "base/file_util.h"
22 #include "base/logging.h"
23 #include "base/string_number_conversions.h"
24 #include "base/string_split.h"
25 #include "base/string_tokenizer.h"
26 #include "base/string_util.h"
27 #include "base/sys_info.h"
28 #include "base/threading/thread_restrictions.h"
32 ProcessId
GetParentProcessId(ProcessHandle process
) {
33 struct kinfo_proc info
;
35 int mib
[] = { CTL_KERN
, KERN_PROC
, KERN_PROC_PID
, process
,
36 sizeof(struct kinfo_proc
), 0 };
38 if (sysctl(mib
, arraysize(mib
), NULL
, &length
, NULL
, 0) < 0)
41 mib
[5] = (length
/ sizeof(struct kinfo_proc
));
43 if (sysctl(mib
, arraysize(mib
), &info
, &length
, NULL
, 0) < 0)
49 FilePath
GetProcessExecutablePath(ProcessHandle process
) {
52 int mib
[] = { CTL_KERN
, KERN_PROC
, KERN_PROC_PID
, process
,
53 sizeof(struct kinfo_proc
), 0 };
55 if (sysctl(mib
, arraysize(mib
), NULL
, &len
, NULL
, 0) == -1)
57 mib
[5] = (len
/ sizeof(struct kinfo_proc
));
58 if (sysctl(mib
, arraysize(mib
), &kp
, &len
, NULL
, 0) < 0)
60 if ((kp
.p_flag
& P_SYSTEM
) != 0)
62 if (strcmp(kp
.p_comm
, "chrome") == 0)
63 return FilePath(kp
.p_comm
);
68 ProcessIterator::ProcessIterator(const ProcessFilter
* filter
)
69 : index_of_kinfo_proc_(),
72 int mib
[] = { CTL_KERN
, KERN_PROC
, KERN_PROC_UID
, getuid(),
73 sizeof(struct kinfo_proc
), 0 };
77 const int max_tries
= 10;
81 if (sysctl(mib
, arraysize(mib
), NULL
, &len
, NULL
, 0) < 0) {
82 DLOG(ERROR
) << "failed to get the size needed for the process list";
83 kinfo_procs_
.resize(0);
86 size_t num_of_kinfo_proc
= len
/ sizeof(struct kinfo_proc
);
87 // Leave some spare room for process table growth (more could show up
88 // between when we check and now)
89 num_of_kinfo_proc
+= 16;
90 kinfo_procs_
.resize(num_of_kinfo_proc
);
91 len
= num_of_kinfo_proc
* sizeof(struct kinfo_proc
);
92 if (sysctl(mib
, arraysize(mib
), &kinfo_procs_
[0], &len
, NULL
, 0) < 0) {
93 // If we get a mem error, it just means we need a bigger buffer, so
94 // loop around again. Anything else is a real error and give up.
95 if (errno
!= ENOMEM
) {
96 DLOG(ERROR
) << "failed to get the process list";
97 kinfo_procs_
.resize(0);
101 // Got the list, just make sure we're sized exactly right
102 size_t num_of_kinfo_proc
= len
/ sizeof(struct kinfo_proc
);
103 kinfo_procs_
.resize(num_of_kinfo_proc
);
107 } while (!done
&& (try_num
++ < max_tries
));
110 DDLOG(ERROR
) << "failed to collect the process list in a few tries";
111 kinfo_procs_
.resize(0);
115 ProcessIterator::~ProcessIterator() {
118 bool ProcessIterator::CheckForNextProcess() {
120 for (; index_of_kinfo_proc_
< kinfo_procs_
.size(); ++index_of_kinfo_proc_
) {
121 kinfo_proc
& kinfo
= kinfo_procs_
[index_of_kinfo_proc_
];
123 // Skip processes just awaiting collection
124 if ((kinfo
.p_pid
> 0) && (kinfo
.p_stat
== SZOMB
))
127 int mib
[] = { CTL_KERN
, KERN_PROC_ARGS
, kinfo
.p_pid
};
129 // Find out what size buffer we need.
131 if (sysctl(mib
, arraysize(mib
), NULL
, &data_len
, NULL
, 0) < 0) {
132 DVPLOG(1) << "failed to figure out the buffer size for a commandline";
136 data
.resize(data_len
);
137 if (sysctl(mib
, arraysize(mib
), &data
[0], &data_len
, NULL
, 0) < 0) {
138 DVPLOG(1) << "failed to fetch a commandline";
142 // |data| contains all the command line parameters of the process, separated
143 // by blocks of one or more null characters. We tokenize |data| into a
144 // vector of strings using '\0' as a delimiter and populate
145 // |entry_.cmd_line_args_|.
146 std::string delimiters
;
147 delimiters
.push_back('\0');
148 Tokenize(data
, delimiters
, &entry_
.cmd_line_args_
);
150 // |data| starts with the full executable path followed by a null character.
151 // We search for the first instance of '\0' and extract everything before it
152 // to populate |entry_.exe_file_|.
153 size_t exec_name_end
= data
.find('\0');
154 if (exec_name_end
== std::string::npos
) {
155 DLOG(ERROR
) << "command line data didn't match expected format";
159 entry_
.pid_
= kinfo
.p_pid
;
160 entry_
.ppid_
= kinfo
.p_ppid
;
161 entry_
.gid_
= kinfo
.p__pgid
;
162 size_t last_slash
= data
.rfind('/', exec_name_end
);
163 if (last_slash
== std::string::npos
)
164 entry_
.exe_file_
.assign(data
, 0, exec_name_end
);
166 entry_
.exe_file_
.assign(data
, last_slash
+ 1,
167 exec_name_end
- last_slash
- 1);
168 // Start w/ the next entry next time through
169 ++index_of_kinfo_proc_
;
176 bool NamedProcessIterator::IncludeEntry() {
177 return (executable_name_
== entry().exe_file() &&
178 ProcessIterator::IncludeEntry());
182 ProcessMetrics::ProcessMetrics(ProcessHandle process
)
185 last_system_time_(0),
188 processor_count_
= base::SysInfo::NumberOfProcessors();
192 ProcessMetrics
* ProcessMetrics::CreateProcessMetrics(ProcessHandle process
) {
193 return new ProcessMetrics(process
);
196 size_t ProcessMetrics::GetPagefileUsage() const {
197 struct kinfo_proc info
;
199 int mib
[] = { CTL_KERN
, KERN_PROC
, KERN_PROC_PID
, process_
,
200 sizeof(struct kinfo_proc
), 0 };
202 if (sysctl(mib
, arraysize(mib
), NULL
, &length
, NULL
, 0) < 0)
205 mib
[5] = (length
/ sizeof(struct kinfo_proc
));
207 if (sysctl(mib
, arraysize(mib
), &info
, &length
, NULL
, 0) < 0)
210 return (info
.p_vm_tsize
+ info
.p_vm_dsize
+ info
.p_vm_ssize
);
213 size_t ProcessMetrics::GetPeakPagefileUsage() const {
218 size_t ProcessMetrics::GetWorkingSetSize() const {
219 struct kinfo_proc info
;
221 int mib
[] = { CTL_KERN
, KERN_PROC
, KERN_PROC_PID
, process_
,
222 sizeof(struct kinfo_proc
), 0 };
224 if (sysctl(mib
, arraysize(mib
), NULL
, &length
, NULL
, 0) < 0)
227 mib
[5] = (length
/ sizeof(struct kinfo_proc
));
229 if (sysctl(mib
, arraysize(mib
), &info
, &length
, NULL
, 0) < 0)
232 return info
.p_vm_rssize
* getpagesize();
235 size_t ProcessMetrics::GetPeakWorkingSetSize() const {
240 bool ProcessMetrics::GetMemoryBytes(size_t* private_bytes
,
241 size_t* shared_bytes
) {
242 WorkingSetKBytes ws_usage
;
244 if (!GetWorkingSetKBytes(&ws_usage
))
248 *private_bytes
= ws_usage
.priv
<< 10;
251 *shared_bytes
= ws_usage
.shared
* 1024;
256 bool ProcessMetrics::GetWorkingSetKBytes(WorkingSetKBytes
* ws_usage
) const {
257 // TODO(bapt) be sure we can't be precise
258 size_t priv
= GetWorkingSetSize();
261 ws_usage
->priv
= priv
/ 1024;
262 ws_usage
->shareable
= 0;
263 ws_usage
->shared
= 0;
268 bool ProcessMetrics::GetIOCounters(IoCounters
* io_counters
) const {
272 static int GetProcessCPU(pid_t pid
) {
273 struct kinfo_proc info
;
275 int mib
[] = { CTL_KERN
, KERN_PROC
, KERN_PROC_PID
, pid
,
276 sizeof(struct kinfo_proc
), 0 };
278 if (sysctl(mib
, arraysize(mib
), NULL
, &length
, NULL
, 0) < 0)
281 mib
[5] = (length
/ sizeof(struct kinfo_proc
));
283 if (sysctl(mib
, arraysize(mib
), &info
, &length
, NULL
, 0) < 0)
286 return info
.p_pctcpu
;
289 double ProcessMetrics::GetCPUUsage() {
292 int retval
= gettimeofday(&now
, NULL
);
296 int64 time
= TimeValToMicroseconds(now
);
298 if (last_time_
== 0) {
299 // First call, just set the last values.
301 last_cpu_
= GetProcessCPU(process_
);
305 int64 time_delta
= time
- last_time_
;
306 DCHECK_NE(time_delta
, 0);
311 int cpu
= GetProcessCPU(process_
);
316 double percentage
= static_cast<double>((cpu
* 100.0) / FSCALE
);
321 size_t GetSystemCommitCharge() {
322 int mib
[] = { CTL_VM
, VM_METER
};
324 struct vmtotal vmtotal
;
325 unsigned long mem_total
, mem_free
, mem_inactive
;
326 size_t len
= sizeof(vmtotal
);
328 if (sysctl(mib
, arraysize(mib
), &vmtotal
, &len
, NULL
, 0) < 0)
331 mem_total
= vmtotal
.t_vm
;
332 mem_free
= vmtotal
.t_free
;
333 mem_inactive
= vmtotal
.t_vm
- vmtotal
.t_avm
;
335 pagesize
= getpagesize();
337 return mem_total
- (mem_free
*pagesize
) - (mem_inactive
*pagesize
);
340 void EnableTerminationOnOutOfMemory() {
343 void EnableTerminationOnHeapCorruption() {