| OLD | NEW |
| 1 // Copyright (c) 2008 The Chromium Authors. All rights reserved. | 1 // 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 | 5 |
| 6 #include "base/process_util.h" | 6 #include "base/process_util.h" |
| 7 | 7 |
| 8 #import <Cocoa/Cocoa.h> | 8 #import <Cocoa/Cocoa.h> |
| 9 #include <crt_externs.h> | 9 #include <crt_externs.h> |
| 10 #include <mach/mach.h> | 10 #include <mach/mach.h> |
| 11 #include <mach/mach_init.h> | 11 #include <mach/mach_init.h> |
| 12 #include <mach/task.h> | 12 #include <mach/task.h> |
| 13 #include <malloc/malloc.h> | 13 #include <malloc/malloc.h> |
| 14 #import <objc/runtime.h> |
| 14 #include <spawn.h> | 15 #include <spawn.h> |
| 15 #include <sys/mman.h> | 16 #include <sys/mman.h> |
| 16 #include <sys/sysctl.h> | 17 #include <sys/sysctl.h> |
| 17 #include <sys/types.h> | 18 #include <sys/types.h> |
| 18 #include <sys/wait.h> | 19 #include <sys/wait.h> |
| 19 | 20 |
| 21 #include <new> |
| 20 #include <string> | 22 #include <string> |
| 21 | 23 |
| 22 #include "base/debug_util.h" | 24 #include "base/debug_util.h" |
| 23 #include "base/eintr_wrapper.h" | 25 #include "base/eintr_wrapper.h" |
| 24 #include "base/logging.h" | 26 #include "base/logging.h" |
| 25 #include "base/string_util.h" | 27 #include "base/string_util.h" |
| 26 #include "base/sys_info.h" | 28 #include "base/sys_info.h" |
| 27 #include "base/sys_string_conversions.h" | 29 #include "base/sys_string_conversions.h" |
| 28 #include "base/time.h" | 30 #include "base/time.h" |
| 29 | 31 |
| (...skipping 318 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 348 return 0; | 350 return 0; |
| 349 } | 351 } |
| 350 | 352 |
| 351 return (data.active_count * page_size) / 1024; | 353 return (data.active_count * page_size) / 1024; |
| 352 } | 354 } |
| 353 | 355 |
| 354 // ------------------------------------------------------------------------ | 356 // ------------------------------------------------------------------------ |
| 355 | 357 |
| 356 namespace { | 358 namespace { |
| 357 | 359 |
| 360 bool g_oom_killer_enabled; |
| 361 |
| 362 // === C malloc/calloc/valloc/realloc === |
| 363 |
| 358 typedef void* (*malloc_type)(struct _malloc_zone_t* zone, | 364 typedef void* (*malloc_type)(struct _malloc_zone_t* zone, |
| 359 size_t size); | 365 size_t size); |
| 360 typedef void* (*calloc_type)(struct _malloc_zone_t* zone, | 366 typedef void* (*calloc_type)(struct _malloc_zone_t* zone, |
| 361 size_t num_items, | 367 size_t num_items, |
| 362 size_t size); | 368 size_t size); |
| 363 typedef void* (*valloc_type)(struct _malloc_zone_t* zone, | 369 typedef void* (*valloc_type)(struct _malloc_zone_t* zone, |
| 364 size_t size); | 370 size_t size); |
| 365 typedef void* (*realloc_type)(struct _malloc_zone_t* zone, | 371 typedef void* (*realloc_type)(struct _malloc_zone_t* zone, |
| 366 void* ptr, | 372 void* ptr, |
| 367 size_t size); | 373 size_t size); |
| (...skipping 30 matching lines...) Expand all Loading... |
| 398 | 404 |
| 399 void* oom_killer_realloc(struct _malloc_zone_t* zone, | 405 void* oom_killer_realloc(struct _malloc_zone_t* zone, |
| 400 void* ptr, | 406 void* ptr, |
| 401 size_t size) { | 407 size_t size) { |
| 402 void* result = g_old_realloc(zone, ptr, size); | 408 void* result = g_old_realloc(zone, ptr, size); |
| 403 if (size && !result) | 409 if (size && !result) |
| 404 DebugUtil::BreakDebugger(); | 410 DebugUtil::BreakDebugger(); |
| 405 return result; | 411 return result; |
| 406 } | 412 } |
| 407 | 413 |
| 414 // === C++ operator new === |
| 415 |
| 416 void oom_killer_new() { |
| 417 DebugUtil::BreakDebugger(); |
| 418 } |
| 419 |
| 420 // === Core Foundation CFAllocators === |
| 421 |
| 422 // This is the real structure of a CFAllocatorRef behind the scenes. See |
| 423 // http://opensource.apple.com/source/CF/CF-550/CFBase.c for details. |
| 424 struct ChromeCFAllocator { |
| 425 _malloc_zone_t fake_malloc_zone; |
| 426 void* allocator; |
| 427 CFAllocatorContext context; |
| 428 }; |
| 429 typedef ChromeCFAllocator* ChromeCFAllocatorRef; |
| 430 |
| 431 CFAllocatorAllocateCallBack g_old_cfallocator_system_default; |
| 432 CFAllocatorAllocateCallBack g_old_cfallocator_malloc; |
| 433 CFAllocatorAllocateCallBack g_old_cfallocator_malloc_zone; |
| 434 |
| 435 void* oom_killer_cfallocator_system_default(CFIndex alloc_size, |
| 436 CFOptionFlags hint, |
| 437 void* info) { |
| 438 void* result = g_old_cfallocator_system_default(alloc_size, hint, info); |
| 439 if (!result) |
| 440 DebugUtil::BreakDebugger(); |
| 441 return result; |
| 442 } |
| 443 |
| 444 void* oom_killer_cfallocator_malloc(CFIndex alloc_size, |
| 445 CFOptionFlags hint, |
| 446 void* info) { |
| 447 void* result = g_old_cfallocator_malloc(alloc_size, hint, info); |
| 448 if (!result) |
| 449 DebugUtil::BreakDebugger(); |
| 450 return result; |
| 451 } |
| 452 |
| 453 void* oom_killer_cfallocator_malloc_zone(CFIndex alloc_size, |
| 454 CFOptionFlags hint, |
| 455 void* info) { |
| 456 void* result = g_old_cfallocator_malloc_zone(alloc_size, hint, info); |
| 457 if (!result) |
| 458 DebugUtil::BreakDebugger(); |
| 459 return result; |
| 460 } |
| 461 |
| 408 } // namespace | 462 } // namespace |
| 409 | 463 |
| 464 } // namespace base |
| 465 |
| 466 // === Cocoa NSObject allocation === |
| 467 |
| 468 @interface NSObject (ChromeOOMKiller) |
| 469 + (id)OOMKiller_allocWithZone:(NSZone *)zone; |
| 470 @end |
| 471 |
| 472 @implementation NSObject (ChromeOOMKiller) |
| 473 |
| 474 + (id)OOMKiller_allocWithZone:(NSZone *)zone { |
| 475 id result = [self OOMKiller_allocWithZone:zone]; |
| 476 if (!result) |
| 477 DebugUtil::BreakDebugger(); |
| 478 return result; |
| 479 } |
| 480 |
| 481 @end |
| 482 |
| 483 namespace base { |
| 484 |
| 410 void EnableTerminationOnOutOfMemory() { | 485 void EnableTerminationOnOutOfMemory() { |
| 411 CHECK(!g_old_malloc && !g_old_calloc && !g_old_valloc && !g_old_realloc) | 486 if (g_oom_killer_enabled) |
| 412 << "EnableTerminationOnOutOfMemory() called twice!"; | 487 return; |
| 413 | 488 |
| 414 // This approach is sub-optimal: | 489 g_oom_killer_enabled = true; |
| 415 // - Requests for amounts of memory larger than MALLOC_ABSOLUTE_MAX_SIZE | 490 |
| 416 // (currently SIZE_T_MAX - (2 * PAGE_SIZE)) will still fail with a NULL | 491 // === C malloc/calloc/valloc/realloc === |
| 417 // rather than dying (see | 492 |
| 418 // http://opensource.apple.com/source/Libc/Libc-583/gen/malloc.c for | 493 // This approach is not perfect, as requests for amounts of memory larger than |
| 419 // details). | 494 // MALLOC_ABSOLUTE_MAX_SIZE (currently SIZE_T_MAX - (2 * PAGE_SIZE)) will |
| 420 // - It is unclear whether allocations via the C++ operator new() are affected | 495 // still fail with a NULL rather than dying (see |
| 421 // by this (although it is likely). | 496 // http://opensource.apple.com/source/Libc/Libc-583/gen/malloc.c for details). |
| 422 // - This does not affect allocations from non-default zones. | 497 // Unfortunately, it's the best we can do. Also note that this does not affect |
| 423 // - It is unclear whether allocations from CoreFoundation's | 498 // allocations from non-default zones. |
| 424 // kCFAllocatorDefault or +[NSObject alloc] are affected by this. | 499 |
| 425 // Nevertheless this is better than nothing for now. | 500 DCHECK(!g_old_malloc && !g_old_calloc && !g_old_valloc && !g_old_realloc) |
| 426 // TODO(avi):Do better. http://crbug.com/12673 | 501 << "Old allocators unexpectedly non-null"; |
| 427 | 502 |
| 428 int32 major; | 503 int32 major; |
| 429 int32 minor; | 504 int32 minor; |
| 430 int32 bugfix; | 505 int32 bugfix; |
| 431 SysInfo::OperatingSystemVersionNumbers(&major, &minor, &bugfix); | 506 SysInfo::OperatingSystemVersionNumbers(&major, &minor, &bugfix); |
| 432 bool zone_allocators_protected = ((major == 10 && minor > 6) || major > 10); | 507 bool zone_allocators_protected = ((major == 10 && minor > 6) || major > 10); |
| 433 | 508 |
| 434 malloc_zone_t* default_zone = malloc_default_zone(); | 509 malloc_zone_t* default_zone = malloc_default_zone(); |
| 435 | 510 |
| 436 vm_address_t page_start = NULL; | 511 vm_address_t page_start = NULL; |
| (...skipping 15 matching lines...) Expand all Loading... |
| 452 << "Failed to get system allocation functions."; | 527 << "Failed to get system allocation functions."; |
| 453 | 528 |
| 454 default_zone->malloc = oom_killer_malloc; | 529 default_zone->malloc = oom_killer_malloc; |
| 455 default_zone->calloc = oom_killer_calloc; | 530 default_zone->calloc = oom_killer_calloc; |
| 456 default_zone->valloc = oom_killer_valloc; | 531 default_zone->valloc = oom_killer_valloc; |
| 457 default_zone->realloc = oom_killer_realloc; | 532 default_zone->realloc = oom_killer_realloc; |
| 458 | 533 |
| 459 if (zone_allocators_protected) { | 534 if (zone_allocators_protected) { |
| 460 mprotect(reinterpret_cast<void*>(page_start), len, PROT_READ); | 535 mprotect(reinterpret_cast<void*>(page_start), len, PROT_READ); |
| 461 } | 536 } |
| 537 |
| 538 // === C++ operator new === |
| 539 |
| 540 // Yes, operator new does call through to malloc, but this will catch failures |
| 541 // that our imperfect handling of malloc cannot. |
| 542 |
| 543 std::set_new_handler(oom_killer_new); |
| 544 |
| 545 // === Core Foundation CFAllocators === |
| 546 |
| 547 // This will not catch allocation done by custom allocators, but will catch |
| 548 // all allocation done by system-provided ones. |
| 549 |
| 550 DCHECK(!g_old_cfallocator_system_default && !g_old_cfallocator_malloc && |
| 551 !g_old_cfallocator_malloc_zone) |
| 552 << "Old allocators unexpectedly non-null"; |
| 553 |
| 554 ChromeCFAllocatorRef allocator = const_cast<ChromeCFAllocatorRef>( |
| 555 reinterpret_cast<const ChromeCFAllocator*>(kCFAllocatorSystemDefault)); |
| 556 g_old_cfallocator_system_default = allocator->context.allocate; |
| 557 allocator->context.allocate = oom_killer_cfallocator_system_default; |
| 558 |
| 559 allocator = const_cast<ChromeCFAllocatorRef>( |
| 560 reinterpret_cast<const ChromeCFAllocator*>(kCFAllocatorMalloc)); |
| 561 g_old_cfallocator_malloc = allocator->context.allocate; |
| 562 allocator->context.allocate = oom_killer_cfallocator_malloc; |
| 563 |
| 564 allocator = const_cast<ChromeCFAllocatorRef>( |
| 565 reinterpret_cast<const ChromeCFAllocator*>(kCFAllocatorMallocZone)); |
| 566 g_old_cfallocator_malloc_zone = allocator->context.allocate; |
| 567 allocator->context.allocate = oom_killer_cfallocator_malloc_zone; |
| 568 |
| 569 // === Cocoa NSObject allocation === |
| 570 |
| 571 // Note that both +[NSObject new] and +[NSObject alloc] call through to |
| 572 // +[NSObject allocWithZone:]. |
| 573 |
| 574 Class nsobject_class = [NSObject class]; |
| 575 Method m1 = class_getClassMethod(nsobject_class, |
| 576 @selector(allocWithZone:)); |
| 577 CHECK(m1); |
| 578 Method m2 = class_getClassMethod(nsobject_class, |
| 579 @selector(OOMKiller_allocWithZone:)); |
| 580 CHECK(m2); |
| 581 method_exchangeImplementations(m1, m2); |
| 462 } | 582 } |
| 463 | 583 |
| 464 } // namespace base | 584 } // namespace base |
| OLD | NEW |