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/sysctl.h>
20 #include "base/file_util.h"
21 #include "base/logging.h"
22 #include "base/string_number_conversions.h"
23 #include "base/string_split.h"
24 #include "base/string_tokenizer.h"
25 #include "base/string_util.h"
26 #include "base/sys_info.h"
30 ProcessId
GetParentProcessId(ProcessHandle process
) {
31 struct kinfo_proc info
;
33 int mib
[] = { CTL_KERN
, KERN_PROC
, KERN_PROC_PID
, process
};
35 if (sysctl(mib
, arraysize(mib
), &info
, &length
, NULL
, 0) < 0)
41 FilePath
GetProcessExecutablePath(ProcessHandle process
) {
42 char pathname
[PATH_MAX
];
44 int mib
[] = { CTL_KERN
, KERN_PROC
, KERN_PROC_PATHNAME
, process
};
46 length
= sizeof(pathname
);
48 if (sysctl(mib
, arraysize(mib
), pathname
, &length
, NULL
, 0) < 0 ||
53 return FilePath(std::string(pathname
));
56 ProcessIterator::ProcessIterator(const ProcessFilter
* filter
)
57 : index_of_kinfo_proc_(),
60 int mib
[] = { CTL_KERN
, KERN_PROC
, KERN_PROC_UID
, getuid() };
64 const int max_tries
= 10;
68 if (sysctl(mib
, arraysize(mib
), NULL
, &len
, NULL
, 0) <0 ){
69 LOG(ERROR
) << "failed to get the size needed for the process list";
70 kinfo_procs_
.resize(0);
73 size_t num_of_kinfo_proc
= len
/ sizeof(struct kinfo_proc
);
74 // Leave some spare room for process table growth (more could show up
75 // between when we check and now)
76 num_of_kinfo_proc
+= 16;
77 kinfo_procs_
.resize(num_of_kinfo_proc
);
78 len
= num_of_kinfo_proc
* sizeof(struct kinfo_proc
);
79 if (sysctl(mib
, arraysize(mib
), &kinfo_procs_
[0], &len
, NULL
, 0) <0) {
80 // If we get a mem error, it just means we need a bigger buffer, so
81 // loop around again. Anything else is a real error and give up.
82 if (errno
!= ENOMEM
) {
83 LOG(ERROR
) << "failed to get the process list";
84 kinfo_procs_
.resize(0);
88 // Got the list, just make sure we're sized exactly right
89 size_t num_of_kinfo_proc
= len
/ sizeof(struct kinfo_proc
);
90 kinfo_procs_
.resize(num_of_kinfo_proc
);
94 } while (!done
&& (try_num
++ < max_tries
));
97 LOG(ERROR
) << "failed to collect the process list in a few tries";
98 kinfo_procs_
.resize(0);
102 ProcessIterator::~ProcessIterator() {
105 bool ProcessIterator::CheckForNextProcess() {
108 for (; index_of_kinfo_proc_
< kinfo_procs_
.size(); ++ index_of_kinfo_proc_
) {
110 struct kinfo_proc kinfo
= kinfo_procs_
[index_of_kinfo_proc_
];
111 int mib
[] = { CTL_KERN
, KERN_PROC_ARGS
, kinfo
.ki_pid
};
113 if ((kinfo
.ki_pid
> 0) && (kinfo
.ki_stat
== SZOMB
))
117 if (sysctl(mib
, arraysize(mib
), NULL
, &length
, NULL
, 0) < 0) {
118 LOG(ERROR
) << "failed to figure out the buffer size for a command line";
124 if (sysctl(mib
, arraysize(mib
), &data
[0], &length
, NULL
, 0) < 0) {
125 LOG(ERROR
) << "failed to fetch a commandline";
129 std::string delimiters
;
130 delimiters
.push_back('\0');
131 Tokenize(data
, delimiters
, &entry_
.cmd_line_args_
);
133 size_t exec_name_end
= data
.find('\0');
134 if (exec_name_end
== std::string::npos
) {
135 LOG(ERROR
) << "command line data didn't match expected format";
139 entry_
.pid_
= kinfo
.ki_pid
;
140 entry_
.ppid_
= kinfo
.ki_ppid
;
141 entry_
.gid_
= kinfo
.ki_pgid
;
143 size_t last_slash
= data
.rfind('/', exec_name_end
);
144 if (last_slash
== std::string::npos
) {
145 entry_
.exe_file_
.assign(data
, 0, exec_name_end
);
147 entry_
.exe_file_
.assign(data
, last_slash
+ 1,
148 exec_name_end
- last_slash
- 1);
151 // Start w/ the next entry next time through
152 ++index_of_kinfo_proc_
;
159 bool NamedProcessIterator::IncludeEntry() {
160 if(executable_name_
!= entry().exe_file())
163 return ProcessIterator::IncludeEntry();
167 ProcessMetrics::ProcessMetrics(ProcessHandle process
)
170 last_system_time_(0),
172 processor_count_
= base::SysInfo::NumberOfProcessors();
176 ProcessMetrics
* ProcessMetrics::CreateProcessMetrics(ProcessHandle process
) {
177 return new ProcessMetrics(process
);
180 size_t ProcessMetrics::GetPagefileUsage() const {
181 struct kinfo_proc info
;
182 int mib
[] = { CTL_KERN
, KERN_PROC
, KERN_PROC_PID
, process_
};
183 size_t length
= sizeof(info
);
185 if (sysctl(mib
, arraysize(mib
), &info
, &length
, NULL
, 0) < 0)
191 size_t ProcessMetrics::GetPeakPagefileUsage() const {
195 size_t ProcessMetrics::GetWorkingSetSize() const {
196 struct kinfo_proc info
;
197 int mib
[] = { CTL_KERN
, KERN_PROC
, KERN_PROC_PID
, process_
};
198 size_t length
= sizeof(info
);
200 if (sysctl(mib
, arraysize(mib
), &info
, &length
, NULL
, 0) < 0)
203 return info
.ki_rssize
* getpagesize();
206 size_t ProcessMetrics::GetPeakWorkingSetSize() const {
210 bool ProcessMetrics::GetMemoryBytes(size_t* private_bytes
,
211 size_t* shared_bytes
) {
212 WorkingSetKBytes ws_usage
;
213 if (!GetWorkingSetKBytes(&ws_usage
))
217 *private_bytes
= ws_usage
.priv
<< 10;
220 *shared_bytes
= ws_usage
.shared
* 1024;
225 bool ProcessMetrics::GetWorkingSetKBytes(WorkingSetKBytes
* ws_usage
) const {
226 // TODO(bapt) be sure we can't be precise
227 size_t priv
= GetWorkingSetSize();
230 ws_usage
->priv
= priv
/ 1024;
231 ws_usage
->shareable
= 0;
232 ws_usage
->shared
= 0;
237 bool ProcessMetrics::GetIOCounters(IoCounters
* io_counters
) const {
241 double ProcessMetrics::GetCPUUsage() {
242 struct kinfo_proc info
;
243 int mib
[] = { CTL_KERN
, KERN_PROC
, KERN_PROC_PID
, process_
};
244 size_t length
= sizeof(info
);
247 int retval
= gettimeofday(&now
, NULL
);
251 if (sysctl(mib
, arraysize(mib
), &info
, &length
, NULL
, 0) < 0)
254 return (info
.ki_pctcpu
/ FSCALE
) * 100.0;
257 size_t GetSystemCommitCharge() {
258 int mib
[2], pagesize
;
259 unsigned long mem_total
, mem_free
, mem_inactive
;
260 size_t length
= sizeof(mem_total
);
262 if (sysctl(mib
, arraysize(mib
), &mem_total
, &length
, NULL
, 0) < 0)
265 length
= sizeof(mem_free
);
266 if (sysctlbyname("vm.stats.vm.v_free_count", &mem_free
, &length
, NULL
, 0) < 0)
269 length
= sizeof(mem_inactive
);
270 if (sysctlbyname("vm.stats.vm.v_inactive_count", &mem_inactive
, &length
,
275 pagesize
= getpagesize();
277 return mem_total
- (mem_free
*pagesize
) - (mem_inactive
*pagesize
);
280 void EnableTerminationOnOutOfMemory() {
281 DLOG(WARNING
) << "Not feasible.";
284 void EnableTerminationOnHeapCorruption() {
288 bool AdjustOOMScore(ProcessId process
, int score
) {