Roll skia to r1241.
[chromium-blink-merge.git] / base / process_util_mac.mm
blob5c81b2a4bffbeeda80e5506c52f328e82b80c4b1
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.
6 #include "base/process_util.h"
8 #import <Cocoa/Cocoa.h>
9 #include <crt_externs.h>
10 #include <dlfcn.h>
11 #include <mach/mach.h>
12 #include <mach/mach_init.h>
13 #include <mach/mach_vm.h>
14 #include <mach/shared_region.h>
15 #include <mach/task.h>
16 #include <malloc/malloc.h>
17 #import <objc/runtime.h>
18 #include <spawn.h>
19 #include <sys/mman.h>
20 #include <sys/sysctl.h>
21 #include <sys/types.h>
22 #include <sys/utsname.h>
23 #include <sys/wait.h>
25 #include <new>
26 #include <string>
28 #include "base/debug/debugger.h"
29 #include "base/eintr_wrapper.h"
30 #include "base/hash_tables.h"
31 #include "base/logging.h"
32 #include "base/string_util.h"
33 #include "base/sys_info.h"
34 #include "base/sys_string_conversions.h"
35 #include "base/time.h"
36 #include "third_party/apple_apsl/CFBase.h"
37 #include "third_party/apple_apsl/malloc.h"
39 namespace base {
41 void 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;
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);
58 ProcessIterator::ProcessIterator(const ProcessFilter* filter)
59     : index_of_kinfo_proc_(0),
60       filter_(filter) {
61   // 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.
65   int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_UID, geteuid() };
67   // 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)
83       num_of_kinfo_proc += 16;
84       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));
104   if (!done) {
105     LOG(ERROR) << "failed to collect the process list in a few tries";
106     kinfo_procs_.resize(0);
107   }
110 ProcessIterator::~ProcessIterator() {
113 bool ProcessIterator::CheckForNextProcess() {
114   std::string data;
115   for (; index_of_kinfo_proc_ < kinfo_procs_.size(); ++index_of_kinfo_proc_) {
116     kinfo_proc& kinfo = kinfo_procs_[index_of_kinfo_proc_];
118     // Skip processes just awaiting collection
119     if ((kinfo.kp_proc.p_pid > 0) && (kinfo.kp_proc.p_stat == SZOMB))
120       continue;
122     int mib[] = { CTL_KERN, KERN_PROCARGS, kinfo.kp_proc.p_pid };
124     // Find out what size buffer we need.
125     size_t data_len = 0;
126     if (sysctl(mib, arraysize(mib), NULL, &data_len, NULL, 0) < 0) {
127       DVPLOG(1) << "failed to figure out the buffer size for a commandline";
128       continue;
129     }
131     data.resize(data_len);
132     if (sysctl(mib, arraysize(mib), &data[0], &data_len, NULL, 0) < 0) {
133       DVPLOG(1) << "failed to fetch a commandline";
134       continue;
135     }
137     // |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_);
145     // |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_|.
148     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     }
154     entry_.pid_ = kinfo.kp_proc.p_pid;
155     entry_.ppid_ = kinfo.kp_eproc.e_ppid;
156     entry_.gid_ = kinfo.kp_eproc.e_pgid;
157     size_t last_slash = data.rfind('/', exec_name_end);
158     if (last_slash == std::string::npos)
159       entry_.exe_file_.assign(data, 0, exec_name_end);
160     else
161       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;
167   }
168   return false;
171 bool NamedProcessIterator::IncludeEntry() {
172   return (executable_name_ == entry().exe_file() &&
173           ProcessIterator::IncludeEntry());
177 // ------------------------------------------------------------------------
178 // NOTE: about ProcessMetrics
180 // 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.
187 ProcessMetrics::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) {
193   processor_count_ = SysInfo::NumberOfProcessors();
196 // static
197 ProcessMetrics* ProcessMetrics::CreateProcessMetrics(
198     ProcessHandle process,
199     ProcessMetrics::PortProvider* port_provider) {
200   return new ProcessMetrics(process, port_provider);
203 bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) const {
204   return false;
207 static bool GetTaskInfo(mach_port_t task, task_basic_info_64* task_info_data) {
208   if (task == MACH_PORT_NULL)
209     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;
219 size_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;
226 size_t ProcessMetrics::GetPeakPagefileUsage() const {
227   return 0;
230 size_t ProcessMetrics::GetWorkingSetSize() const {
231   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;
237 size_t ProcessMetrics::GetPeakWorkingSetSize() const {
238   return 0;
241 static 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   }
253   return true;
256 static 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;
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.
270 bool ProcessMetrics::GetMemoryBytes(size_t* private_bytes,
271                                     size_t* shared_bytes) {
272   kern_return_t kr;
273   size_t private_pages_count = 0;
274   size_t shared_pages_count = 0;
276   if (!private_bytes && !shared_bytes)
277     return true;
279   mach_port_t task = TaskForPid(process_);
280   if (task == MACH_PORT_NULL) {
281     LOG(ERROR) << "Invalid process";
282     return false;
283   }
285   cpu_type_t cpu_type;
286   if (!GetCPUTypeForProcess(process_, &cpu_type))
287     return false;
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;
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     }
325     if (IsAddressInSharedRegion(address, cpu_type) &&
326         info.share_mode != SM_PRIVATE)
327       continue;
329     if (info.share_mode == SM_COW && info.ref_count == 1)
330       info.share_mode = SM_PRIVATE;
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   }
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   }
360   if (private_bytes)
361     *private_bytes = private_pages_count * page_size;
362   if (shared_bytes)
363     *shared_bytes = shared_pages_count * page_size;
365   return true;
368 void ProcessMetrics::GetCommittedKBytes(CommittedKBytes* usage) const {
371 bool ProcessMetrics::GetWorkingSetKBytes(WorkingSetKBytes* ws_usage) const {
372   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;
381 #define TIME_VALUE_TO_TIMEVAL(a, r) do {  \
382   (r)->tv_sec = (a)->seconds;             \
383   (r)->tv_usec = (a)->microseconds;       \
384 } while (0)
386 double ProcessMetrics::GetCPUUsage() {
387   mach_port_t task = TaskForPid(process_);
388   if (task == MACH_PORT_NULL)
389     return 0;
391   kern_return_t kr;
393   // 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:
395   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   }
406   task_basic_info_64 task_info_data;
407   if (!GetTaskInfo(task, &task_info_data))
408     return 0;
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);
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);
423   struct timeval now;
424   int retval = gettimeofday(&now, NULL);
425   if (retval)
426     return 0;
428   int64 time = TimeValToMicroseconds(now);
429   int64 task_time = TimeValToMicroseconds(task_timeval);
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   }
438   int64 system_time_delta = task_time - last_system_time_;
439   int64 time_delta = time - last_time_;
440   DCHECK_NE(0U, time_delta);
441   if (time_delta == 0)
442     return 0;
444   // We add time_delta / 2 so the result is rounded.
445   double cpu = static_cast<double>((system_time_delta * 100.0) / time_delta);
447   last_system_time_ = task_time;
448   last_time_ = time;
450   return cpu;
453 mach_port_t ProcessMetrics::TaskForPid(ProcessHandle process) const {
454   mach_port_t task = MACH_PORT_NULL;
455   if (port_provider_)
456     task = port_provider_->TaskForPid(process_);
457   if (task == MACH_PORT_NULL && process_ == getpid())
458     task = mach_task_self();
459   return task;
462 // ------------------------------------------------------------------------
464 // Bytes committed by the system.
465 size_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);
472   if (kr) {
473     LOG(WARNING) << "Failed to fetch host statistics.";
474     return 0;
475   }
477   vm_size_t page_size;
478   kr = host_page_size(host, &page_size);
479   if (kr) {
480     LOG(ERROR) << "Failed to fetch host page size.";
481     return 0;
482   }
484   return (data.active_count * page_size) / 1024;
487 // ------------------------------------------------------------------------
489 namespace {
491 bool g_oom_killer_enabled;
493 // === C malloc/calloc/valloc/realloc/posix_memalign ===
495 typedef void* (*malloc_type)(struct _malloc_zone_t* zone,
496                              size_t size);
497 typedef void* (*calloc_type)(struct _malloc_zone_t* zone,
498                              size_t num_items,
499                              size_t size);
500 typedef void* (*valloc_type)(struct _malloc_zone_t* zone,
501                              size_t size);
502 typedef void* (*realloc_type)(struct _malloc_zone_t* zone,
503                               void* ptr,
504                               size_t size);
505 typedef void* (*memalign_type)(struct _malloc_zone_t* zone,
506                                size_t alignment,
507                                size_t size);
509 malloc_type g_old_malloc;
510 calloc_type g_old_calloc;
511 valloc_type g_old_valloc;
512 realloc_type g_old_realloc;
513 memalign_type g_old_memalign;
515 malloc_type g_old_malloc_purgeable;
516 calloc_type g_old_calloc_purgeable;
517 valloc_type g_old_valloc_purgeable;
518 realloc_type g_old_realloc_purgeable;
519 memalign_type g_old_memalign_purgeable;
521 void* oom_killer_malloc(struct _malloc_zone_t* zone,
522                         size_t size) {
523   void* result = g_old_malloc(zone, size);
524   if (!result && size)
525     debug::BreakDebugger();
526   return result;
529 void* 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);
533   if (!result && num_items && size)
534     debug::BreakDebugger();
535   return result;
538 void* oom_killer_valloc(struct _malloc_zone_t* zone,
539                         size_t size) {
540   void* result = g_old_valloc(zone, size);
541   if (!result && size)
542     debug::BreakDebugger();
543   return result;
546 void* oom_killer_realloc(struct _malloc_zone_t* zone,
547                          void* ptr,
548                          size_t size) {
549   void* result = g_old_realloc(zone, ptr, size);
550   if (!result && size)
551     debug::BreakDebugger();
552   return result;
555 void* 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) {
564     debug::BreakDebugger();
565   }
566   return result;
569 void* 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)
573     debug::BreakDebugger();
574   return result;
577 void* 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)
582     debug::BreakDebugger();
583   return result;
586 void* 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)
590     debug::BreakDebugger();
591   return result;
594 void* 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)
599     debug::BreakDebugger();
600   return result;
603 void* 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) {
612     debug::BreakDebugger();
613   }
614   return result;
617 // === C++ operator new ===
619 void oom_killer_new() {
620   debug::BreakDebugger();
623 // === Core Foundation CFAllocators ===
625 bool CanGetContextForCFAllocator(long darwin_version) {
626   // TODO(avi): remove at final release; http://crbug.com/74589
627   if (darwin_version == 11) {
628     NSLog(@"Unsure about the internals of CFAllocator but going to patch them "
629            "anyway. Watch out for crashes inside of CFAllocatorAllocate.");
630   }
631   return darwin_version == 9 ||
632          darwin_version == 10 ||
633          darwin_version == 11;
636 CFAllocatorContext* ContextForCFAllocator(CFAllocatorRef allocator,
637                                           long darwin_version) {
638   if (darwin_version == 9 || darwin_version == 10) {
639     ChromeCFAllocator9and10* our_allocator =
640         const_cast<ChromeCFAllocator9and10*>(
641             reinterpret_cast<const ChromeCFAllocator9and10*>(allocator));
642     return &our_allocator->_context;
643   } else if (darwin_version == 11) {
644     ChromeCFAllocator11* our_allocator =
645         const_cast<ChromeCFAllocator11*>(
646             reinterpret_cast<const ChromeCFAllocator11*>(allocator));
647     return &our_allocator->_context;
648   } else {
649     return NULL;
650   }
653 CFAllocatorAllocateCallBack g_old_cfallocator_system_default;
654 CFAllocatorAllocateCallBack g_old_cfallocator_malloc;
655 CFAllocatorAllocateCallBack g_old_cfallocator_malloc_zone;
657 void* oom_killer_cfallocator_system_default(CFIndex alloc_size,
658                                             CFOptionFlags hint,
659                                             void* info) {
660   void* result = g_old_cfallocator_system_default(alloc_size, hint, info);
661   if (!result)
662     debug::BreakDebugger();
663   return result;
666 void* oom_killer_cfallocator_malloc(CFIndex alloc_size,
667                                     CFOptionFlags hint,
668                                     void* info) {
669   void* result = g_old_cfallocator_malloc(alloc_size, hint, info);
670   if (!result)
671     debug::BreakDebugger();
672   return result;
675 void* oom_killer_cfallocator_malloc_zone(CFIndex alloc_size,
676                                          CFOptionFlags hint,
677                                          void* info) {
678   void* result = g_old_cfallocator_malloc_zone(alloc_size, hint, info);
679   if (!result)
680     debug::BreakDebugger();
681   return result;
684 // === Cocoa NSObject allocation ===
686 typedef id (*allocWithZone_t)(id, SEL, NSZone*);
687 allocWithZone_t g_old_allocWithZone;
689 id oom_killer_allocWithZone(id self, SEL _cmd, NSZone* zone)
691   id result = g_old_allocWithZone(self, _cmd, zone);
692   if (!result)
693     debug::BreakDebugger();
694   return result;
697 }  // namespace
699 malloc_zone_t* GetPurgeableZone() {
700   // malloc_default_purgeable_zone only exists on >= 10.6. Use dlsym to grab it
701   // at runtime because it may not be present in the SDK used for compilation.
702   typedef malloc_zone_t* (*malloc_default_purgeable_zone_t)(void);
703   malloc_default_purgeable_zone_t malloc_purgeable_zone =
704       reinterpret_cast<malloc_default_purgeable_zone_t>(
705           dlsym(RTLD_DEFAULT, "malloc_default_purgeable_zone"));
706   if (malloc_purgeable_zone)
707     return malloc_purgeable_zone();
708   return NULL;
711 void EnableTerminationOnOutOfMemory() {
712   if (g_oom_killer_enabled)
713     return;
715   g_oom_killer_enabled = true;
717   // Not SysInfo::OperatingSystemVersionNumbers as that calls through to Gestalt
718   // which ends up (on > 10.6) spawning threads.
719   struct utsname machine_info;
720   if (uname(&machine_info)) {
721     return;
722   }
724   // The string machine_info.release is the xnu/Darwin version number, "9.xxx"
725   // on Mac OS X 10.5, and "10.xxx" on Mac OS X 10.6. See
726   // http://en.wikipedia.org/wiki/Darwin_(operating_system) .
727   long darwin_version = strtol(machine_info.release, NULL, 10);
729   // === C malloc/calloc/valloc/realloc/posix_memalign ===
731   // This approach is not perfect, as requests for amounts of memory larger than
732   // MALLOC_ABSOLUTE_MAX_SIZE (currently SIZE_T_MAX - (2 * PAGE_SIZE)) will
733   // still fail with a NULL rather than dying (see
734   // http://opensource.apple.com/source/Libc/Libc-583/gen/malloc.c for details).
735   // Unfortunately, it's the best we can do. Also note that this does not affect
736   // allocations from non-default zones.
738   CHECK(!g_old_malloc && !g_old_calloc && !g_old_valloc && !g_old_realloc &&
739         !g_old_memalign) << "Old allocators unexpectedly non-null";
741   CHECK(!g_old_malloc_purgeable && !g_old_calloc_purgeable &&
742         !g_old_valloc_purgeable && !g_old_realloc_purgeable &&
743         !g_old_memalign_purgeable) << "Old allocators unexpectedly non-null";
745   // See http://trac.webkit.org/changeset/53362/trunk/Tools/DumpRenderTree/mac
746   bool zone_allocators_protected = darwin_version > 10;
748   ChromeMallocZone* default_zone =
749       reinterpret_cast<ChromeMallocZone*>(malloc_default_zone());
750   ChromeMallocZone* purgeable_zone =
751       reinterpret_cast<ChromeMallocZone*>(GetPurgeableZone());
753   vm_address_t page_start_default = NULL;
754   vm_address_t page_start_purgeable = NULL;
755   vm_size_t len_default = 0;
756   vm_size_t len_purgeable = 0;
757   if (zone_allocators_protected) {
758     page_start_default = reinterpret_cast<vm_address_t>(default_zone) &
759         static_cast<vm_size_t>(~(getpagesize() - 1));
760     len_default = reinterpret_cast<vm_address_t>(default_zone) -
761         page_start_default + sizeof(ChromeMallocZone);
762     mprotect(reinterpret_cast<void*>(page_start_default), len_default,
763              PROT_READ | PROT_WRITE);
765     if (purgeable_zone) {
766       page_start_purgeable = reinterpret_cast<vm_address_t>(purgeable_zone) &
767           static_cast<vm_size_t>(~(getpagesize() - 1));
768       len_purgeable = reinterpret_cast<vm_address_t>(purgeable_zone) -
769           page_start_purgeable + sizeof(ChromeMallocZone);
770       mprotect(reinterpret_cast<void*>(page_start_purgeable), len_purgeable,
771                PROT_READ | PROT_WRITE);
772     }
773   }
775   // Default zone
777   g_old_malloc = default_zone->malloc;
778   g_old_calloc = default_zone->calloc;
779   g_old_valloc = default_zone->valloc;
780   g_old_realloc = default_zone->realloc;
781   CHECK(g_old_malloc && g_old_calloc && g_old_valloc && g_old_realloc)
782       << "Failed to get system allocation functions.";
784   default_zone->malloc = oom_killer_malloc;
785   default_zone->calloc = oom_killer_calloc;
786   default_zone->valloc = oom_killer_valloc;
787   default_zone->realloc = oom_killer_realloc;
789   if (default_zone->version >= 5) {
790     g_old_memalign = default_zone->memalign;
791     if (g_old_memalign)
792       default_zone->memalign = oom_killer_memalign;
793   }
795   // Purgeable zone (if it exists)
797   if (purgeable_zone) {
798     g_old_malloc_purgeable = purgeable_zone->malloc;
799     g_old_calloc_purgeable = purgeable_zone->calloc;
800     g_old_valloc_purgeable = purgeable_zone->valloc;
801     g_old_realloc_purgeable = purgeable_zone->realloc;
802     CHECK(g_old_malloc_purgeable && g_old_calloc_purgeable &&
803           g_old_valloc_purgeable && g_old_realloc_purgeable)
804         << "Failed to get system allocation functions.";
806     purgeable_zone->malloc = oom_killer_malloc_purgeable;
807     purgeable_zone->calloc = oom_killer_calloc_purgeable;
808     purgeable_zone->valloc = oom_killer_valloc_purgeable;
809     purgeable_zone->realloc = oom_killer_realloc_purgeable;
811     if (purgeable_zone->version >= 5) {
812       g_old_memalign_purgeable = purgeable_zone->memalign;
813       if (g_old_memalign_purgeable)
814         purgeable_zone->memalign = oom_killer_memalign_purgeable;
815     }
816   }
818   if (zone_allocators_protected) {
819     mprotect(reinterpret_cast<void*>(page_start_default), len_default,
820              PROT_READ);
821     if (purgeable_zone) {
822       mprotect(reinterpret_cast<void*>(page_start_purgeable), len_purgeable,
823                PROT_READ);
824     }
825   }
827   // === C malloc_zone_batch_malloc ===
829   // batch_malloc is omitted because the default malloc zone's implementation
830   // only supports batch_malloc for "tiny" allocations from the free list. It
831   // will fail for allocations larger than "tiny", and will only allocate as
832   // many blocks as it's able to from the free list. These factors mean that it
833   // can return less than the requested memory even in a non-out-of-memory
834   // situation. There's no good way to detect whether a batch_malloc failure is
835   // due to these other factors, or due to genuine memory or address space
836   // exhaustion. The fact that it only allocates space from the "tiny" free list
837   // means that it's likely that a failure will not be due to memory exhaustion.
838   // Similarly, these constraints on batch_malloc mean that callers must always
839   // be expecting to receive less memory than was requested, even in situations
840   // where memory pressure is not a concern. Finally, the only public interface
841   // to batch_malloc is malloc_zone_batch_malloc, which is specific to the
842   // system's malloc implementation. It's unlikely that anyone's even heard of
843   // it.
845   // === C++ operator new ===
847   // Yes, operator new does call through to malloc, but this will catch failures
848   // that our imperfect handling of malloc cannot.
850   std::set_new_handler(oom_killer_new);
852   // === Core Foundation CFAllocators ===
854   // This will not catch allocation done by custom allocators, but will catch
855   // all allocation done by system-provided ones.
857   CHECK(!g_old_cfallocator_system_default && !g_old_cfallocator_malloc &&
858         !g_old_cfallocator_malloc_zone)
859       << "Old allocators unexpectedly non-null";
861   bool cf_allocator_internals_known =
862       CanGetContextForCFAllocator(darwin_version);
864   if (cf_allocator_internals_known) {
865     CFAllocatorContext* context =
866         ContextForCFAllocator(kCFAllocatorSystemDefault, darwin_version);
867     CHECK(context) << "Failed to get context for kCFAllocatorSystemDefault.";
868     g_old_cfallocator_system_default = context->allocate;
869     CHECK(g_old_cfallocator_system_default)
870         << "Failed to get kCFAllocatorSystemDefault allocation function.";
871     context->allocate = oom_killer_cfallocator_system_default;
873     context = ContextForCFAllocator(kCFAllocatorMalloc, darwin_version);
874     CHECK(context) << "Failed to get context for kCFAllocatorMalloc.";
875     g_old_cfallocator_malloc = context->allocate;
876     CHECK(g_old_cfallocator_malloc)
877         << "Failed to get kCFAllocatorMalloc allocation function.";
878     context->allocate = oom_killer_cfallocator_malloc;
880     context = ContextForCFAllocator(kCFAllocatorMallocZone, darwin_version);
881     CHECK(context) << "Failed to get context for kCFAllocatorMallocZone.";
882     g_old_cfallocator_malloc_zone = context->allocate;
883     CHECK(g_old_cfallocator_malloc_zone)
884         << "Failed to get kCFAllocatorMallocZone allocation function.";
885     context->allocate = oom_killer_cfallocator_malloc_zone;
886   } else {
887     NSLog(@"Internals of CFAllocator not known; out-of-memory failures via "
888         "CFAllocator will not result in termination. http://crbug.com/45650");
889   }
891   // === Cocoa NSObject allocation ===
893   // Note that both +[NSObject new] and +[NSObject alloc] call through to
894   // +[NSObject allocWithZone:].
896   CHECK(!g_old_allocWithZone)
897       << "Old allocator unexpectedly non-null";
899   Class nsobject_class = [NSObject class];
900   Method orig_method = class_getClassMethod(nsobject_class,
901                                             @selector(allocWithZone:));
902   g_old_allocWithZone = reinterpret_cast<allocWithZone_t>(
903       method_getImplementation(orig_method));
904   CHECK(g_old_allocWithZone)
905       << "Failed to get allocWithZone allocation function.";
906   method_setImplementation(orig_method,
907                            reinterpret_cast<IMP>(oom_killer_allocWithZone));
910 ProcessId GetParentProcessId(ProcessHandle process) {
911   struct kinfo_proc info;
912   size_t length = sizeof(struct kinfo_proc);
913   int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, process };
914   if (sysctl(mib, 4, &info, &length, NULL, 0) < 0) {
915     PLOG(ERROR) << "sysctl";
916     return -1;
917   }
918   if (length == 0)
919     return -1;
920   return info.kp_eproc.e_ppid;
923 }  // namespace base