blob: 6d055815b975c5676829b354debcf4a004a990e6 [file] [log] [blame]
[email protected]9c73c772008-10-27 20:49:551// Copyright (c) 2008 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.
4
5
6#include "base/process_util.h"
7
8#import <Cocoa/Cocoa.h>
[email protected]6c1495c2009-02-03 17:45:289#include <crt_externs.h>
[email protected]6cfa3392010-07-01 20:11:4310#include <dlfcn.h>
[email protected]ed26d942009-11-09 06:57:2811#include <mach/mach.h>
[email protected]e9a8c19f2009-09-03 21:27:3612#include <mach/mach_init.h>
[email protected]c81f5782010-12-21 20:04:5613#include <mach/mach_vm.h>
14#include <mach/shared_region.h>
[email protected]e9a8c19f2009-09-03 21:27:3615#include <mach/task.h>
[email protected]cccb21212009-11-12 20:39:5616#include <malloc/malloc.h>
[email protected]9f958442010-03-19 18:42:4117#import <objc/runtime.h>
[email protected]9c73c772008-10-27 20:49:5518#include <spawn.h>
[email protected]d21a2ac2010-02-01 20:27:1819#include <sys/mman.h>
[email protected]962dd312009-02-05 21:44:1320#include <sys/sysctl.h>
[email protected]9c73c772008-10-27 20:49:5521#include <sys/types.h>
[email protected]10128a6d2010-08-24 19:22:3622#include <sys/utsname.h>
[email protected]9c73c772008-10-27 20:49:5523#include <sys/wait.h>
24
[email protected]9f958442010-03-19 18:42:4125#include <new>
[email protected]962dd312009-02-05 21:44:1326#include <string>
27
[email protected]58580352010-10-26 04:07:5028#include "base/debug/debugger.h"
[email protected]157c61b2009-05-01 21:37:3129#include "base/eintr_wrapper.h"
[email protected]c81f5782010-12-21 20:04:5630#include "base/hash_tables.h"
[email protected]962dd312009-02-05 21:44:1331#include "base/logging.h"
32#include "base/string_util.h"
[email protected]d21a2ac2010-02-01 20:27:1833#include "base/sys_info.h"
[email protected]047a03f2009-10-07 02:10:2034#include "base/sys_string_conversions.h"
[email protected]962dd312009-02-05 21:44:1335#include "base/time.h"
[email protected]67ec3c62010-10-18 21:55:3836#include "third_party/apple_apsl/CFBase.h"
37#include "third_party/apple_apsl/malloc.h"
[email protected]962dd312009-02-05 21:44:1338
[email protected]9963aaa2008-11-14 04:01:3539namespace base {
[email protected]9c73c772008-10-27 20:49:5540
[email protected]e9a8c19f2009-09-03 21:27:3641void RestoreDefaultExceptionHandler() {
42 // This function is tailored to remove the Breakpad exception handler.
43 // exception_mask matches s_exception_mask in
44 // breakpad/src/client/mac/handler/exception_handler.cc
45 const exception_mask_t exception_mask = EXC_MASK_BAD_ACCESS |
46 EXC_MASK_BAD_INSTRUCTION |
47 EXC_MASK_ARITHMETIC |
48 EXC_MASK_BREAKPOINT;
49
50 // Setting the exception port to MACH_PORT_NULL may not be entirely
51 // kosher to restore the default exception handler, but in practice,
52 // it results in the exception port being set to Apple Crash Reporter,
53 // the desired behavior.
54 task_set_exception_ports(mach_task_self(), exception_mask, MACH_PORT_NULL,
55 EXCEPTION_DEFAULT, THREAD_STATE_NONE);
56}
57
[email protected]b6128aa2010-04-29 17:44:4258ProcessIterator::ProcessIterator(const ProcessFilter* filter)
59 : index_of_kinfo_proc_(0),
[email protected]047a03f2009-10-07 02:10:2060 filter_(filter) {
[email protected]607fa452009-02-06 03:57:1761 // Get a snapshot of all of my processes (yes, as we loop it can go stale, but
62 // but trying to find where we were in a constantly changing list is basically
63 // impossible.
64
65 int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_UID, geteuid() };
[email protected]52a261f2009-03-03 15:01:1266
[email protected]607fa452009-02-06 03:57:1767 // Since more processes could start between when we get the size and when
68 // we get the list, we do a loop to keep trying until we get it.
69 bool done = false;
70 int try_num = 1;
71 const int max_tries = 10;
72 do {
73 // Get the size of the buffer
74 size_t len = 0;
75 if (sysctl(mib, arraysize(mib), NULL, &len, NULL, 0) < 0) {
76 LOG(ERROR) << "failed to get the size needed for the process list";
77 kinfo_procs_.resize(0);
78 done = true;
79 } else {
80 size_t num_of_kinfo_proc = len / sizeof(struct kinfo_proc);
81 // Leave some spare room for process table growth (more could show up
82 // between when we check and now)
[email protected]b6128aa2010-04-29 17:44:4283 num_of_kinfo_proc += 16;
[email protected]607fa452009-02-06 03:57:1784 kinfo_procs_.resize(num_of_kinfo_proc);
85 len = num_of_kinfo_proc * sizeof(struct kinfo_proc);
86 // Load the list of processes
87 if (sysctl(mib, arraysize(mib), &kinfo_procs_[0], &len, NULL, 0) < 0) {
88 // If we get a mem error, it just means we need a bigger buffer, so
89 // loop around again. Anything else is a real error and give up.
90 if (errno != ENOMEM) {
91 LOG(ERROR) << "failed to get the process list";
92 kinfo_procs_.resize(0);
93 done = true;
94 }
95 } else {
96 // Got the list, just make sure we're sized exactly right
97 size_t num_of_kinfo_proc = len / sizeof(struct kinfo_proc);
98 kinfo_procs_.resize(num_of_kinfo_proc);
99 done = true;
100 }
101 }
102 } while (!done && (try_num++ < max_tries));
[email protected]52a261f2009-03-03 15:01:12103
[email protected]607fa452009-02-06 03:57:17104 if (!done) {
105 LOG(ERROR) << "failed to collect the process list in a few tries";
106 kinfo_procs_.resize(0);
107 }
[email protected]962dd312009-02-05 21:44:13108}
109
[email protected]b6128aa2010-04-29 17:44:42110ProcessIterator::~ProcessIterator() {
[email protected]962dd312009-02-05 21:44:13111}
112
[email protected]b6128aa2010-04-29 17:44:42113bool ProcessIterator::CheckForNextProcess() {
[email protected]607fa452009-02-06 03:57:17114 std::string data;
[email protected]607fa452009-02-06 03:57:17115 for (; index_of_kinfo_proc_ < kinfo_procs_.size(); ++index_of_kinfo_proc_) {
[email protected]b6128aa2010-04-29 17:44:42116 kinfo_proc& kinfo = kinfo_procs_[index_of_kinfo_proc_];
[email protected]962dd312009-02-05 21:44:13117
[email protected]607fa452009-02-06 03:57:17118 // Skip processes just awaiting collection
[email protected]b6128aa2010-04-29 17:44:42119 if ((kinfo.kp_proc.p_pid > 0) && (kinfo.kp_proc.p_stat == SZOMB))
[email protected]607fa452009-02-06 03:57:17120 continue;
[email protected]f0a51fb52009-03-05 12:46:38121
[email protected]b6128aa2010-04-29 17:44:42122 int mib[] = { CTL_KERN, KERN_PROCARGS, kinfo.kp_proc.p_pid };
[email protected]962dd312009-02-05 21:44:13123
[email protected]b6128aa2010-04-29 17:44:42124 // Find out what size buffer we need.
[email protected]607fa452009-02-06 03:57:17125 size_t data_len = 0;
126 if (sysctl(mib, arraysize(mib), NULL, &data_len, NULL, 0) < 0) {
127 LOG(ERROR) << "failed to figure out the buffer size for a commandline";
128 continue;
129 }
[email protected]f0a51fb52009-03-05 12:46:38130
[email protected]607fa452009-02-06 03:57:17131 data.resize(data_len);
132 if (sysctl(mib, arraysize(mib), &data[0], &data_len, NULL, 0) < 0) {
133 LOG(ERROR) << "failed to fetch a commandline";
134 continue;
135 }
[email protected]f0a51fb52009-03-05 12:46:38136
[email protected]c47d81d2010-10-05 23:41:04137 // |data| contains all the command line parameters of the process, separated
138 // by blocks of one or more null characters. We tokenize |data| into a
139 // vector of strings using '\0' as a delimiter and populate
140 // |entry_.cmd_line_args_|.
141 std::string delimiters;
142 delimiters.push_back('\0');
143 Tokenize(data, delimiters, &entry_.cmd_line_args_);
[email protected]607fa452009-02-06 03:57:17144
[email protected]c47d81d2010-10-05 23:41:04145 // |data| starts with the full executable path followed by a null character.
146 // We search for the first instance of '\0' and extract everything before it
147 // to populate |entry_.exe_file_|.
[email protected]607fa452009-02-06 03:57:17148 size_t exec_name_end = data.find('\0');
149 if (exec_name_end == std::string::npos) {
150 LOG(ERROR) << "command line data didn't match expected format";
151 continue;
152 }
[email protected]c47d81d2010-10-05 23:41:04153
[email protected]b6128aa2010-04-29 17:44:42154 entry_.pid_ = kinfo.kp_proc.p_pid;
155 entry_.ppid_ = kinfo.kp_eproc.e_ppid;
156 entry_.gid_ = kinfo.kp_eproc.e_pgid;
[email protected]607fa452009-02-06 03:57:17157 size_t last_slash = data.rfind('/', exec_name_end);
158 if (last_slash == std::string::npos)
[email protected]b6128aa2010-04-29 17:44:42159 entry_.exe_file_.assign(data, 0, exec_name_end);
[email protected]607fa452009-02-06 03:57:17160 else
[email protected]b6128aa2010-04-29 17:44:42161 entry_.exe_file_.assign(data, last_slash + 1,
162 exec_name_end - last_slash - 1);
163 // Start w/ the next entry next time through
164 ++index_of_kinfo_proc_;
165 // Done
166 return true;
[email protected]607fa452009-02-06 03:57:17167 }
168 return false;
[email protected]962dd312009-02-05 21:44:13169}
170
171bool NamedProcessIterator::IncludeEntry() {
[email protected]4f260d02010-12-23 18:35:42172 return (executable_name_ == entry().exe_file() &&
[email protected]b6128aa2010-04-29 17:44:42173 ProcessIterator::IncludeEntry());
[email protected]962dd312009-02-05 21:44:13174}
175
[email protected]821ad22c2009-06-12 14:48:30176
177// ------------------------------------------------------------------------
178// NOTE: about ProcessMetrics
179//
[email protected]3740cb9b52009-12-19 04:50:04180// Getting a mach task from a pid for another process requires permissions in
181// general, so there doesn't really seem to be a way to do these (and spinning
182// up ps to fetch each stats seems dangerous to put in a base api for anyone to
183// call). Child processes ipc their port, so return something if available,
184// otherwise return 0.
[email protected]821ad22c2009-06-12 14:48:30185//
[email protected]b6128aa2010-04-29 17:44:42186
187ProcessMetrics::ProcessMetrics(ProcessHandle process,
188 ProcessMetrics::PortProvider* port_provider)
189 : process_(process),
190 last_time_(0),
191 last_system_time_(0),
192 port_provider_(port_provider) {
[email protected]58580352010-10-26 04:07:50193 processor_count_ = SysInfo::NumberOfProcessors();
[email protected]b6128aa2010-04-29 17:44:42194}
195
196// static
197ProcessMetrics* ProcessMetrics::CreateProcessMetrics(
198 ProcessHandle process,
199 ProcessMetrics::PortProvider* port_provider) {
200 return new ProcessMetrics(process, port_provider);
201}
202
[email protected]d043c2cc2009-03-25 18:30:45203bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) const {
[email protected]7a0bb4bf2008-11-19 21:41:48204 return false;
205}
[email protected]3740cb9b52009-12-19 04:50:04206
207static bool GetTaskInfo(mach_port_t task, task_basic_info_64* task_info_data) {
[email protected]b2e8e082009-12-21 17:44:20208 if (task == MACH_PORT_NULL)
[email protected]3740cb9b52009-12-19 04:50:04209 return false;
210 mach_msg_type_number_t count = TASK_BASIC_INFO_64_COUNT;
211 kern_return_t kr = task_info(task,
212 TASK_BASIC_INFO_64,
213 reinterpret_cast<task_info_t>(task_info_data),
214 &count);
215 // Most likely cause for failure: |task| is a zombie.
216 return kr == KERN_SUCCESS;
[email protected]14620b392009-12-18 22:48:19217}
[email protected]3740cb9b52009-12-19 04:50:04218
219size_t ProcessMetrics::GetPagefileUsage() const {
220 task_basic_info_64 task_info_data;
221 if (!GetTaskInfo(TaskForPid(process_), &task_info_data))
222 return 0;
223 return task_info_data.virtual_size;
224}
225
[email protected]0c557f12009-05-11 23:35:52226size_t ProcessMetrics::GetPeakPagefileUsage() const {
[email protected]0c557f12009-05-11 23:35:52227 return 0;
228}
[email protected]3740cb9b52009-12-19 04:50:04229
[email protected]0c557f12009-05-11 23:35:52230size_t ProcessMetrics::GetWorkingSetSize() const {
[email protected]3740cb9b52009-12-19 04:50:04231 task_basic_info_64 task_info_data;
232 if (!GetTaskInfo(TaskForPid(process_), &task_info_data))
233 return 0;
234 return task_info_data.resident_size;
[email protected]821ad22c2009-06-12 14:48:30235}
[email protected]3740cb9b52009-12-19 04:50:04236
[email protected]821ad22c2009-06-12 14:48:30237size_t ProcessMetrics::GetPeakWorkingSetSize() const {
[email protected]796da7c2009-06-11 12:45:45238 return 0;
[email protected]0c557f12009-05-11 23:35:52239}
240
[email protected]c81f5782010-12-21 20:04:56241static bool GetCPUTypeForProcess(pid_t pid, cpu_type_t* cpu_type) {
242 size_t len = sizeof(*cpu_type);
243 int result = sysctlbyname("sysctl.proc_cputype",
244 cpu_type,
245 &len,
246 NULL,
247 0);
248 if (result != 0) {
249 PLOG(ERROR) << "sysctlbyname(""sysctl.proc_cputype"")";
250 return false;
251 }
252
253 return true;
254}
255
256static bool IsAddressInSharedRegion(mach_vm_address_t addr, cpu_type_t type) {
257 if (type == CPU_TYPE_I386)
258 return addr >= SHARED_REGION_BASE_I386 &&
259 addr < (SHARED_REGION_BASE_I386 + SHARED_REGION_SIZE_I386);
260 else if (type == CPU_TYPE_X86_64)
261 return addr >= SHARED_REGION_BASE_X86_64 &&
262 addr < (SHARED_REGION_BASE_X86_64 + SHARED_REGION_SIZE_X86_64);
263 else
264 return false;
265}
266
267// This is a rough approximation of the algorithm that libtop uses.
268// private_bytes is the size of private resident memory.
269// shared_bytes is the size of shared resident memory.
[email protected]98947a02010-05-11 17:46:08270bool ProcessMetrics::GetMemoryBytes(size_t* private_bytes,
271 size_t* shared_bytes) {
[email protected]c81f5782010-12-21 20:04:56272 kern_return_t kr;
273 size_t private_pages_count = 0;
274 size_t shared_pages_count = 0;
275
276 if (!private_bytes && !shared_bytes)
277 return true;
278
279 mach_port_t task = TaskForPid(process_);
280 if (task == MACH_PORT_NULL) {
281 LOG(ERROR) << "Invalid process";
282 return false;
283 }
284
285 cpu_type_t cpu_type;
286 if (!GetCPUTypeForProcess(process_, &cpu_type))
287 return false;
288
289 // The same region can be referenced multiple times. To avoid double counting
290 // we need to keep track of which regions we've already counted.
291 base::hash_set<int> seen_objects;
292
293 // We iterate through each VM region in the task's address map. For shared
294 // memory we add up all the pages that are marked as shared. Like libtop we
295 // try to avoid counting pages that are also referenced by other tasks. Since
296 // we don't have access to the VM regions of other tasks the only hint we have
297 // is if the address is in the shared region area.
298 //
299 // Private memory is much simpler. We simply count the pages that are marked
300 // as private or copy on write (COW).
301 //
302 // See libtop_update_vm_regions in
303 // http://www.opensource.apple.com/source/top/top-67/libtop.c
304 mach_vm_size_t size = 0;
305 for (mach_vm_address_t address = MACH_VM_MIN_ADDRESS;; address += size) {
306 vm_region_top_info_data_t info;
307 mach_msg_type_number_t info_count = VM_REGION_TOP_INFO_COUNT;
308 mach_port_t object_name;
309 kr = mach_vm_region(task,
310 &address,
311 &size,
312 VM_REGION_TOP_INFO,
313 (vm_region_info_t)&info,
314 &info_count,
315 &object_name);
316 if (kr == KERN_INVALID_ADDRESS) {
317 // We're at the end of the address space.
318 break;
319 } else if (kr != KERN_SUCCESS) {
320 LOG(ERROR) << "Calling mach_vm_region failed with error: "
321 << mach_error_string(kr);
322 return false;
323 }
324
325 if (IsAddressInSharedRegion(address, cpu_type) &&
326 info.share_mode != SM_PRIVATE)
327 continue;
328
329 if (info.share_mode == SM_COW && info.ref_count == 1)
330 info.share_mode = SM_PRIVATE;
331
332 switch (info.share_mode) {
333 case SM_PRIVATE:
334 private_pages_count += info.private_pages_resident;
335 private_pages_count += info.shared_pages_resident;
336 break;
337 case SM_COW:
338 private_pages_count += info.private_pages_resident;
339 // Fall through
340 case SM_SHARED:
341 if (seen_objects.count(info.obj_id) == 0) {
342 // Only count the first reference to this region.
343 seen_objects.insert(info.obj_id);
344 shared_pages_count += info.shared_pages_resident;
345 }
346 break;
347 default:
348 break;
349 }
350 }
351
352 vm_size_t page_size;
353 kr = host_page_size(task, &page_size);
354 if (kr != KERN_SUCCESS) {
355 LOG(ERROR) << "Failed to fetch host page size, error: "
356 << mach_error_string(kr);
357 return false;
358 }
359
[email protected]98947a02010-05-11 17:46:08360 if (private_bytes)
[email protected]c81f5782010-12-21 20:04:56361 *private_bytes = private_pages_count * page_size;
[email protected]98947a02010-05-11 17:46:08362 if (shared_bytes)
[email protected]c81f5782010-12-21 20:04:56363 *shared_bytes = shared_pages_count * page_size;
364
[email protected]98947a02010-05-11 17:46:08365 return true;
[email protected]cd073bdf2009-09-15 22:15:08366}
[email protected]61cd1ab2010-05-19 16:51:26367
[email protected]cd073bdf2009-09-15 22:15:08368void ProcessMetrics::GetCommittedKBytes(CommittedKBytes* usage) const {
369}
370
371bool ProcessMetrics::GetWorkingSetKBytes(WorkingSetKBytes* ws_usage) const {
[email protected]3740cb9b52009-12-19 04:50:04372 size_t priv = GetWorkingSetSize();
373 if (!priv)
374 return false;
375 ws_usage->priv = priv / 1024;
376 ws_usage->shareable = 0;
377 ws_usage->shared = 0;
378 return true;
379}
380
381#define TIME_VALUE_TO_TIMEVAL(a, r) do { \
382 (r)->tv_sec = (a)->seconds; \
383 (r)->tv_usec = (a)->microseconds; \
384} while (0)
385
[email protected]022eab62010-01-13 04:55:06386double ProcessMetrics::GetCPUUsage() {
[email protected]3740cb9b52009-12-19 04:50:04387 mach_port_t task = TaskForPid(process_);
[email protected]b2e8e082009-12-21 17:44:20388 if (task == MACH_PORT_NULL)
[email protected]3740cb9b52009-12-19 04:50:04389 return 0;
390
391 kern_return_t kr;
392
[email protected]37ef15c2010-01-25 16:28:50393 // Libtop explicitly loops over the threads (libtop_pinfo_update_cpu_usage()
394 // in libtop.c), but this is more concise and gives the same results:
[email protected]3740cb9b52009-12-19 04:50:04395 task_thread_times_info thread_info_data;
396 mach_msg_type_number_t thread_info_count = TASK_THREAD_TIMES_INFO_COUNT;
397 kr = task_info(task,
398 TASK_THREAD_TIMES_INFO,
399 reinterpret_cast<task_info_t>(&thread_info_data),
400 &thread_info_count);
401 if (kr != KERN_SUCCESS) {
402 // Most likely cause: |task| is a zombie.
403 return 0;
404 }
405
406 task_basic_info_64 task_info_data;
407 if (!GetTaskInfo(task, &task_info_data))
408 return 0;
409
410 /* Set total_time. */
411 // thread info contains live time...
412 struct timeval user_timeval, system_timeval, task_timeval;
413 TIME_VALUE_TO_TIMEVAL(&thread_info_data.user_time, &user_timeval);
414 TIME_VALUE_TO_TIMEVAL(&thread_info_data.system_time, &system_timeval);
415 timeradd(&user_timeval, &system_timeval, &task_timeval);
416
417 // ... task info contains terminated time.
418 TIME_VALUE_TO_TIMEVAL(&task_info_data.user_time, &user_timeval);
419 TIME_VALUE_TO_TIMEVAL(&task_info_data.system_time, &system_timeval);
420 timeradd(&user_timeval, &task_timeval, &task_timeval);
421 timeradd(&system_timeval, &task_timeval, &task_timeval);
422
423 struct timeval now;
424 int retval = gettimeofday(&now, NULL);
425 if (retval)
426 return 0;
427
428 int64 time = TimeValToMicroseconds(now);
429 int64 task_time = TimeValToMicroseconds(task_timeval);
430
431 if ((last_system_time_ == 0) || (last_time_ == 0)) {
432 // First call, just set the last values.
433 last_system_time_ = task_time;
434 last_time_ = time;
435 return 0;
436 }
437
438 int64 system_time_delta = task_time - last_system_time_;
439 int64 time_delta = time - last_time_;
440 DCHECK(time_delta != 0);
441 if (time_delta == 0)
442 return 0;
443
444 // We add time_delta / 2 so the result is rounded.
[email protected]37ef15c2010-01-25 16:28:50445 double cpu = static_cast<double>((system_time_delta * 100.0) / time_delta);
[email protected]3740cb9b52009-12-19 04:50:04446
447 last_system_time_ = task_time;
448 last_time_ = time;
449
450 return cpu;
451}
452
453mach_port_t ProcessMetrics::TaskForPid(ProcessHandle process) const {
[email protected]b2e8e082009-12-21 17:44:20454 mach_port_t task = MACH_PORT_NULL;
[email protected]3740cb9b52009-12-19 04:50:04455 if (port_provider_)
456 task = port_provider_->TaskForPid(process_);
[email protected]b2e8e082009-12-21 17:44:20457 if (task == MACH_PORT_NULL && process_ == getpid())
[email protected]3740cb9b52009-12-19 04:50:04458 task = mach_task_self();
459 return task;
[email protected]cd073bdf2009-09-15 22:15:08460}
461
[email protected]821ad22c2009-06-12 14:48:30462// ------------------------------------------------------------------------
[email protected]0c557f12009-05-11 23:35:52463
[email protected]ed26d942009-11-09 06:57:28464// Bytes committed by the system.
465size_t GetSystemCommitCharge() {
466 host_name_port_t host = mach_host_self();
467 mach_msg_type_number_t count = HOST_VM_INFO_COUNT;
468 vm_statistics_data_t data;
469 kern_return_t kr = host_statistics(host, HOST_VM_INFO,
470 reinterpret_cast<host_info_t>(&data),
471 &count);
[email protected]66d0a942009-11-13 22:55:29472 if (kr) {
473 LOG(WARNING) << "Failed to fetch host statistics.";
[email protected]ed26d942009-11-09 06:57:28474 return 0;
[email protected]66d0a942009-11-13 22:55:29475 }
[email protected]ed26d942009-11-09 06:57:28476
477 vm_size_t page_size;
478 kr = host_page_size(host, &page_size);
[email protected]66d0a942009-11-13 22:55:29479 if (kr) {
[email protected]ed26d942009-11-09 06:57:28480 LOG(ERROR) << "Failed to fetch host page size.";
481 return 0;
[email protected]66d0a942009-11-13 22:55:29482 }
[email protected]ed26d942009-11-09 06:57:28483
484 return (data.active_count * page_size) / 1024;
485}
486
[email protected]cccb21212009-11-12 20:39:56487// ------------------------------------------------------------------------
488
489namespace {
490
[email protected]9f958442010-03-19 18:42:41491bool g_oom_killer_enabled;
492
[email protected]a1a82d52010-05-19 20:23:46493// === C malloc/calloc/valloc/realloc/posix_memalign ===
494
[email protected]cccb21212009-11-12 20:39:56495typedef void* (*malloc_type)(struct _malloc_zone_t* zone,
496 size_t size);
497typedef void* (*calloc_type)(struct _malloc_zone_t* zone,
498 size_t num_items,
499 size_t size);
500typedef void* (*valloc_type)(struct _malloc_zone_t* zone,
501 size_t size);
502typedef void* (*realloc_type)(struct _malloc_zone_t* zone,
503 void* ptr,
504 size_t size);
[email protected]a1a82d52010-05-19 20:23:46505typedef void* (*memalign_type)(struct _malloc_zone_t* zone,
506 size_t alignment,
507 size_t size);
[email protected]cccb21212009-11-12 20:39:56508
509malloc_type g_old_malloc;
510calloc_type g_old_calloc;
511valloc_type g_old_valloc;
512realloc_type g_old_realloc;
[email protected]a1a82d52010-05-19 20:23:46513memalign_type g_old_memalign;
[email protected]cccb21212009-11-12 20:39:56514
[email protected]6cfa3392010-07-01 20:11:43515malloc_type g_old_malloc_purgeable;
516calloc_type g_old_calloc_purgeable;
517valloc_type g_old_valloc_purgeable;
518realloc_type g_old_realloc_purgeable;
519memalign_type g_old_memalign_purgeable;
520
[email protected]cccb21212009-11-12 20:39:56521void* oom_killer_malloc(struct _malloc_zone_t* zone,
522 size_t size) {
523 void* result = g_old_malloc(zone, size);
[email protected]a1a82d52010-05-19 20:23:46524 if (!result && size)
[email protected]58580352010-10-26 04:07:50525 debug::BreakDebugger();
[email protected]cccb21212009-11-12 20:39:56526 return result;
527}
528
529void* oom_killer_calloc(struct _malloc_zone_t* zone,
530 size_t num_items,
531 size_t size) {
532 void* result = g_old_calloc(zone, num_items, size);
[email protected]a1a82d52010-05-19 20:23:46533 if (!result && num_items && size)
[email protected]58580352010-10-26 04:07:50534 debug::BreakDebugger();
[email protected]cccb21212009-11-12 20:39:56535 return result;
536}
537
538void* oom_killer_valloc(struct _malloc_zone_t* zone,
539 size_t size) {
540 void* result = g_old_valloc(zone, size);
[email protected]a1a82d52010-05-19 20:23:46541 if (!result && size)
[email protected]58580352010-10-26 04:07:50542 debug::BreakDebugger();
[email protected]cccb21212009-11-12 20:39:56543 return result;
544}
545
546void* oom_killer_realloc(struct _malloc_zone_t* zone,
547 void* ptr,
548 size_t size) {
549 void* result = g_old_realloc(zone, ptr, size);
[email protected]a1a82d52010-05-19 20:23:46550 if (!result && size)
[email protected]58580352010-10-26 04:07:50551 debug::BreakDebugger();
[email protected]cccb21212009-11-12 20:39:56552 return result;
553}
554
[email protected]a1a82d52010-05-19 20:23:46555void* oom_killer_memalign(struct _malloc_zone_t* zone,
556 size_t alignment,
557 size_t size) {
558 void* result = g_old_memalign(zone, alignment, size);
559 // Only die if posix_memalign would have returned ENOMEM, since there are
560 // other reasons why NULL might be returned (see
561 // http://opensource.apple.com/source/Libc/Libc-583/gen/malloc.c ).
562 if (!result && size && alignment >= sizeof(void*)
563 && (alignment & (alignment - 1)) == 0) {
[email protected]58580352010-10-26 04:07:50564 debug::BreakDebugger();
[email protected]a1a82d52010-05-19 20:23:46565 }
566 return result;
567}
568
[email protected]6cfa3392010-07-01 20:11:43569void* oom_killer_malloc_purgeable(struct _malloc_zone_t* zone,
570 size_t size) {
571 void* result = g_old_malloc_purgeable(zone, size);
572 if (!result && size)
[email protected]58580352010-10-26 04:07:50573 debug::BreakDebugger();
[email protected]6cfa3392010-07-01 20:11:43574 return result;
575}
576
577void* oom_killer_calloc_purgeable(struct _malloc_zone_t* zone,
578 size_t num_items,
579 size_t size) {
580 void* result = g_old_calloc_purgeable(zone, num_items, size);
581 if (!result && num_items && size)
[email protected]58580352010-10-26 04:07:50582 debug::BreakDebugger();
[email protected]6cfa3392010-07-01 20:11:43583 return result;
584}
585
586void* oom_killer_valloc_purgeable(struct _malloc_zone_t* zone,
587 size_t size) {
588 void* result = g_old_valloc_purgeable(zone, size);
589 if (!result && size)
[email protected]58580352010-10-26 04:07:50590 debug::BreakDebugger();
[email protected]6cfa3392010-07-01 20:11:43591 return result;
592}
593
594void* oom_killer_realloc_purgeable(struct _malloc_zone_t* zone,
595 void* ptr,
596 size_t size) {
597 void* result = g_old_realloc_purgeable(zone, ptr, size);
598 if (!result && size)
[email protected]58580352010-10-26 04:07:50599 debug::BreakDebugger();
[email protected]6cfa3392010-07-01 20:11:43600 return result;
601}
602
603void* oom_killer_memalign_purgeable(struct _malloc_zone_t* zone,
604 size_t alignment,
605 size_t size) {
606 void* result = g_old_memalign_purgeable(zone, alignment, size);
607 // Only die if posix_memalign would have returned ENOMEM, since there are
608 // other reasons why NULL might be returned (see
609 // http://opensource.apple.com/source/Libc/Libc-583/gen/malloc.c ).
610 if (!result && size && alignment >= sizeof(void*)
611 && (alignment & (alignment - 1)) == 0) {
[email protected]58580352010-10-26 04:07:50612 debug::BreakDebugger();
[email protected]6cfa3392010-07-01 20:11:43613 }
614 return result;
615}
616
[email protected]9f958442010-03-19 18:42:41617// === C++ operator new ===
618
619void oom_killer_new() {
[email protected]58580352010-10-26 04:07:50620 debug::BreakDebugger();
[email protected]9f958442010-03-19 18:42:41621}
622
623// === Core Foundation CFAllocators ===
624
[email protected]9f958442010-03-19 18:42:41625typedef ChromeCFAllocator* ChromeCFAllocatorRef;
626
627CFAllocatorAllocateCallBack g_old_cfallocator_system_default;
628CFAllocatorAllocateCallBack g_old_cfallocator_malloc;
629CFAllocatorAllocateCallBack g_old_cfallocator_malloc_zone;
630
631void* oom_killer_cfallocator_system_default(CFIndex alloc_size,
632 CFOptionFlags hint,
633 void* info) {
634 void* result = g_old_cfallocator_system_default(alloc_size, hint, info);
635 if (!result)
[email protected]58580352010-10-26 04:07:50636 debug::BreakDebugger();
[email protected]9f958442010-03-19 18:42:41637 return result;
638}
639
640void* oom_killer_cfallocator_malloc(CFIndex alloc_size,
641 CFOptionFlags hint,
642 void* info) {
643 void* result = g_old_cfallocator_malloc(alloc_size, hint, info);
644 if (!result)
[email protected]58580352010-10-26 04:07:50645 debug::BreakDebugger();
[email protected]9f958442010-03-19 18:42:41646 return result;
647}
648
649void* oom_killer_cfallocator_malloc_zone(CFIndex alloc_size,
650 CFOptionFlags hint,
651 void* info) {
652 void* result = g_old_cfallocator_malloc_zone(alloc_size, hint, info);
653 if (!result)
[email protected]58580352010-10-26 04:07:50654 debug::BreakDebugger();
[email protected]9f958442010-03-19 18:42:41655 return result;
656}
657
658// === Cocoa NSObject allocation ===
659
660typedef id (*allocWithZone_t)(id, SEL, NSZone*);
661allocWithZone_t g_old_allocWithZone;
662
663id oom_killer_allocWithZone(id self, SEL _cmd, NSZone* zone)
664{
665 id result = g_old_allocWithZone(self, _cmd, zone);
666 if (!result)
[email protected]58580352010-10-26 04:07:50667 debug::BreakDebugger();
[email protected]9f958442010-03-19 18:42:41668 return result;
669}
670
[email protected]cccb21212009-11-12 20:39:56671} // namespace
672
[email protected]6cfa3392010-07-01 20:11:43673malloc_zone_t* GetPurgeableZone() {
674 // malloc_default_purgeable_zone only exists on >= 10.6. Use dlsym to grab it
675 // at runtime because it may not be present in the SDK used for compilation.
676 typedef malloc_zone_t* (*malloc_default_purgeable_zone_t)(void);
677 malloc_default_purgeable_zone_t malloc_purgeable_zone =
678 reinterpret_cast<malloc_default_purgeable_zone_t>(
679 dlsym(RTLD_DEFAULT, "malloc_default_purgeable_zone"));
680 if (malloc_purgeable_zone)
681 return malloc_purgeable_zone();
682 return NULL;
683}
684
[email protected]73a964f02010-03-17 13:51:52685void EnableTerminationOnOutOfMemory() {
[email protected]9f958442010-03-19 18:42:41686 if (g_oom_killer_enabled)
687 return;
[email protected]73a964f02010-03-17 13:51:52688
[email protected]9f958442010-03-19 18:42:41689 g_oom_killer_enabled = true;
690
[email protected]10128a6d2010-08-24 19:22:36691 // Not SysInfo::OperatingSystemVersionNumbers as that calls through to Gestalt
692 // which ends up (on > 10.6) spawning threads.
693 struct utsname machine_info;
694 if (uname(&machine_info)) {
695 return;
696 }
697
698 // The string machine_info.release is the xnu/Darwin version number, "9.xxx"
699 // on Mac OS X 10.5, and "10.xxx" on Mac OS X 10.6. See
700 // http://en.wikipedia.org/wiki/Darwin_(operating_system) .
701 long darwin_version = strtol(machine_info.release, NULL, 10);
[email protected]c05b1f72010-06-02 19:06:39702
[email protected]a1a82d52010-05-19 20:23:46703 // === C malloc/calloc/valloc/realloc/posix_memalign ===
[email protected]9f958442010-03-19 18:42:41704
705 // This approach is not perfect, as requests for amounts of memory larger than
706 // MALLOC_ABSOLUTE_MAX_SIZE (currently SIZE_T_MAX - (2 * PAGE_SIZE)) will
707 // still fail with a NULL rather than dying (see
708 // http://opensource.apple.com/source/Libc/Libc-583/gen/malloc.c for details).
709 // Unfortunately, it's the best we can do. Also note that this does not affect
710 // allocations from non-default zones.
711
[email protected]a1a82d52010-05-19 20:23:46712 CHECK(!g_old_malloc && !g_old_calloc && !g_old_valloc && !g_old_realloc &&
713 !g_old_memalign) << "Old allocators unexpectedly non-null";
[email protected]cccb21212009-11-12 20:39:56714
[email protected]6cfa3392010-07-01 20:11:43715 CHECK(!g_old_malloc_purgeable && !g_old_calloc_purgeable &&
716 !g_old_valloc_purgeable && !g_old_realloc_purgeable &&
717 !g_old_memalign_purgeable) << "Old allocators unexpectedly non-null";
718
[email protected]89b90e152010-12-18 03:40:54719 // See http://trac.webkit.org/changeset/53362/trunk/Tools/DumpRenderTree/mac
[email protected]10128a6d2010-08-24 19:22:36720 bool zone_allocators_protected = darwin_version > 10;
[email protected]d21a2ac2010-02-01 20:27:18721
[email protected]a1a82d52010-05-19 20:23:46722 ChromeMallocZone* default_zone =
723 reinterpret_cast<ChromeMallocZone*>(malloc_default_zone());
[email protected]6cfa3392010-07-01 20:11:43724 ChromeMallocZone* purgeable_zone =
725 reinterpret_cast<ChromeMallocZone*>(GetPurgeableZone());
[email protected]d21a2ac2010-02-01 20:27:18726
[email protected]6cfa3392010-07-01 20:11:43727 vm_address_t page_start_default = NULL;
728 vm_address_t page_start_purgeable = NULL;
729 vm_size_t len_default = 0;
730 vm_size_t len_purgeable = 0;
[email protected]d21a2ac2010-02-01 20:27:18731 if (zone_allocators_protected) {
[email protected]6cfa3392010-07-01 20:11:43732 page_start_default = reinterpret_cast<vm_address_t>(default_zone) &
[email protected]d21a2ac2010-02-01 20:27:18733 static_cast<vm_size_t>(~(getpagesize() - 1));
[email protected]6cfa3392010-07-01 20:11:43734 len_default = reinterpret_cast<vm_address_t>(default_zone) -
735 page_start_default + sizeof(ChromeMallocZone);
736 mprotect(reinterpret_cast<void*>(page_start_default), len_default,
737 PROT_READ | PROT_WRITE);
738
739 if (purgeable_zone) {
740 page_start_purgeable = reinterpret_cast<vm_address_t>(purgeable_zone) &
741 static_cast<vm_size_t>(~(getpagesize() - 1));
742 len_purgeable = reinterpret_cast<vm_address_t>(purgeable_zone) -
743 page_start_purgeable + sizeof(ChromeMallocZone);
744 mprotect(reinterpret_cast<void*>(page_start_purgeable), len_purgeable,
745 PROT_READ | PROT_WRITE);
746 }
[email protected]d21a2ac2010-02-01 20:27:18747 }
748
[email protected]6cfa3392010-07-01 20:11:43749 // Default zone
750
[email protected]cccb21212009-11-12 20:39:56751 g_old_malloc = default_zone->malloc;
752 g_old_calloc = default_zone->calloc;
753 g_old_valloc = default_zone->valloc;
754 g_old_realloc = default_zone->realloc;
755 CHECK(g_old_malloc && g_old_calloc && g_old_valloc && g_old_realloc)
756 << "Failed to get system allocation functions.";
757
758 default_zone->malloc = oom_killer_malloc;
759 default_zone->calloc = oom_killer_calloc;
760 default_zone->valloc = oom_killer_valloc;
761 default_zone->realloc = oom_killer_realloc;
[email protected]d21a2ac2010-02-01 20:27:18762
[email protected]a1a82d52010-05-19 20:23:46763 if (default_zone->version >= 5) {
764 g_old_memalign = default_zone->memalign;
765 if (g_old_memalign)
766 default_zone->memalign = oom_killer_memalign;
767 }
768
[email protected]6cfa3392010-07-01 20:11:43769 // Purgeable zone (if it exists)
770
771 if (purgeable_zone) {
772 g_old_malloc_purgeable = purgeable_zone->malloc;
773 g_old_calloc_purgeable = purgeable_zone->calloc;
774 g_old_valloc_purgeable = purgeable_zone->valloc;
775 g_old_realloc_purgeable = purgeable_zone->realloc;
776 CHECK(g_old_malloc_purgeable && g_old_calloc_purgeable &&
777 g_old_valloc_purgeable && g_old_realloc_purgeable)
778 << "Failed to get system allocation functions.";
779
780 purgeable_zone->malloc = oom_killer_malloc_purgeable;
781 purgeable_zone->calloc = oom_killer_calloc_purgeable;
782 purgeable_zone->valloc = oom_killer_valloc_purgeable;
783 purgeable_zone->realloc = oom_killer_realloc_purgeable;
784
785 if (purgeable_zone->version >= 5) {
786 g_old_memalign_purgeable = purgeable_zone->memalign;
787 if (g_old_memalign_purgeable)
788 purgeable_zone->memalign = oom_killer_memalign_purgeable;
789 }
790 }
791
[email protected]d21a2ac2010-02-01 20:27:18792 if (zone_allocators_protected) {
[email protected]6cfa3392010-07-01 20:11:43793 mprotect(reinterpret_cast<void*>(page_start_default), len_default,
794 PROT_READ);
795 if (purgeable_zone) {
796 mprotect(reinterpret_cast<void*>(page_start_purgeable), len_purgeable,
797 PROT_READ);
798 }
[email protected]d21a2ac2010-02-01 20:27:18799 }
[email protected]9f958442010-03-19 18:42:41800
[email protected]a1a82d52010-05-19 20:23:46801 // === C malloc_zone_batch_malloc ===
802
803 // batch_malloc is omitted because the default malloc zone's implementation
804 // only supports batch_malloc for "tiny" allocations from the free list. It
805 // will fail for allocations larger than "tiny", and will only allocate as
806 // many blocks as it's able to from the free list. These factors mean that it
807 // can return less than the requested memory even in a non-out-of-memory
808 // situation. There's no good way to detect whether a batch_malloc failure is
809 // due to these other factors, or due to genuine memory or address space
810 // exhaustion. The fact that it only allocates space from the "tiny" free list
811 // means that it's likely that a failure will not be due to memory exhaustion.
812 // Similarly, these constraints on batch_malloc mean that callers must always
813 // be expecting to receive less memory than was requested, even in situations
814 // where memory pressure is not a concern. Finally, the only public interface
815 // to batch_malloc is malloc_zone_batch_malloc, which is specific to the
816 // system's malloc implementation. It's unlikely that anyone's even heard of
817 // it.
818
[email protected]9f958442010-03-19 18:42:41819 // === C++ operator new ===
820
821 // Yes, operator new does call through to malloc, but this will catch failures
822 // that our imperfect handling of malloc cannot.
823
824 std::set_new_handler(oom_killer_new);
825
826 // === Core Foundation CFAllocators ===
827
828 // This will not catch allocation done by custom allocators, but will catch
829 // all allocation done by system-provided ones.
830
831 CHECK(!g_old_cfallocator_system_default && !g_old_cfallocator_malloc &&
832 !g_old_cfallocator_malloc_zone)
833 << "Old allocators unexpectedly non-null";
834
[email protected]c05b1f72010-06-02 19:06:39835 bool cf_allocator_internals_known =
[email protected]10128a6d2010-08-24 19:22:36836 darwin_version == 9 || darwin_version == 10;
[email protected]9f958442010-03-19 18:42:41837
[email protected]c05b1f72010-06-02 19:06:39838 if (cf_allocator_internals_known) {
839 ChromeCFAllocatorRef allocator = const_cast<ChromeCFAllocatorRef>(
840 reinterpret_cast<const ChromeCFAllocator*>(kCFAllocatorSystemDefault));
[email protected]67ec3c62010-10-18 21:55:38841 g_old_cfallocator_system_default = allocator->_context.allocate;
[email protected]c05b1f72010-06-02 19:06:39842 CHECK(g_old_cfallocator_system_default)
843 << "Failed to get kCFAllocatorSystemDefault allocation function.";
[email protected]67ec3c62010-10-18 21:55:38844 allocator->_context.allocate = oom_killer_cfallocator_system_default;
[email protected]9f958442010-03-19 18:42:41845
[email protected]c05b1f72010-06-02 19:06:39846 allocator = const_cast<ChromeCFAllocatorRef>(
847 reinterpret_cast<const ChromeCFAllocator*>(kCFAllocatorMalloc));
[email protected]67ec3c62010-10-18 21:55:38848 g_old_cfallocator_malloc = allocator->_context.allocate;
[email protected]c05b1f72010-06-02 19:06:39849 CHECK(g_old_cfallocator_malloc)
850 << "Failed to get kCFAllocatorMalloc allocation function.";
[email protected]67ec3c62010-10-18 21:55:38851 allocator->_context.allocate = oom_killer_cfallocator_malloc;
[email protected]c05b1f72010-06-02 19:06:39852
853 allocator = const_cast<ChromeCFAllocatorRef>(
854 reinterpret_cast<const ChromeCFAllocator*>(kCFAllocatorMallocZone));
[email protected]67ec3c62010-10-18 21:55:38855 g_old_cfallocator_malloc_zone = allocator->_context.allocate;
[email protected]c05b1f72010-06-02 19:06:39856 CHECK(g_old_cfallocator_malloc_zone)
857 << "Failed to get kCFAllocatorMallocZone allocation function.";
[email protected]67ec3c62010-10-18 21:55:38858 allocator->_context.allocate = oom_killer_cfallocator_malloc_zone;
[email protected]c05b1f72010-06-02 19:06:39859 } else {
860 NSLog(@"Internals of CFAllocator not known; out-of-memory failures via "
861 "CFAllocator will not result in termination. http://crbug.com/45650");
862 }
[email protected]9f958442010-03-19 18:42:41863
864 // === Cocoa NSObject allocation ===
865
866 // Note that both +[NSObject new] and +[NSObject alloc] call through to
867 // +[NSObject allocWithZone:].
868
869 CHECK(!g_old_allocWithZone)
870 << "Old allocator unexpectedly non-null";
871
872 Class nsobject_class = [NSObject class];
873 Method orig_method = class_getClassMethod(nsobject_class,
874 @selector(allocWithZone:));
875 g_old_allocWithZone = reinterpret_cast<allocWithZone_t>(
876 method_getImplementation(orig_method));
877 CHECK(g_old_allocWithZone)
878 << "Failed to get allocWithZone allocation function.";
879 method_setImplementation(orig_method,
880 reinterpret_cast<IMP>(oom_killer_allocWithZone));
[email protected]cccb21212009-11-12 20:39:56881}
882
[email protected]56f0f262011-02-24 17:14:36883ProcessId GetParentProcessId(ProcessHandle process) {
884 struct kinfo_proc info;
885 size_t length = sizeof(struct kinfo_proc);
886 int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, process };
887 if (sysctl(mib, 4, &info, &length, NULL, 0) < 0) {
888 PLOG(ERROR) << "sysctl";
889 return -1;
890 }
891 if (length == 0)
892 return -1;
893 return info.kp_eproc.e_ppid;
894}
895
[email protected]9963aaa2008-11-14 04:01:35896} // namespace base