Chromium Code Reviews| 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 | |
| 462 // === Cocoa NSObject allocation === | |
| 463 | |
| 464 typedef id (*allocWithZone_t)(id, SEL, NSZone*); | |
| 465 allocWithZone_t g_old_allocWithZone; | |
| 466 | |
| 467 id oom_killer_allocWithZone(id self, SEL _cmd, NSZone* zone) | |
| 468 { | |
| 469 id result = g_old_allocWithZone(self, _cmd, zone); | |
| 470 if (!result) | |
| 471 DebugUtil::BreakDebugger(); | |
| 472 return result; | |
| 473 } | |
| 474 | |
| 408 } // namespace | 475 } // namespace |
| 409 | 476 |
| 410 void EnableTerminationOnOutOfMemory() { | 477 void EnableTerminationOnOutOfMemory() { |
| 478 if (g_oom_killer_enabled) | |
| 479 return; | |
| 480 | |
| 481 g_oom_killer_enabled = true; | |
| 482 | |
| 483 // === C malloc/calloc/valloc/realloc === | |
| 484 | |
| 485 // This approach is not perfect, as requests for amounts of memory larger than | |
| 486 // MALLOC_ABSOLUTE_MAX_SIZE (currently SIZE_T_MAX - (2 * PAGE_SIZE)) will | |
| 487 // still fail with a NULL rather than dying (see | |
| 488 // http://opensource.apple.com/source/Libc/Libc-583/gen/malloc.c for details). | |
| 489 // Unfortunately, it's the best we can do. Also note that this does not affect | |
| 490 // allocations from non-default zones. | |
| 491 | |
| 411 CHECK(!g_old_malloc && !g_old_calloc && !g_old_valloc && !g_old_realloc) | 492 CHECK(!g_old_malloc && !g_old_calloc && !g_old_valloc && !g_old_realloc) |
| 412 << "EnableTerminationOnOutOfMemory() called twice!"; | 493 << "Old allocators unexpectedly non-null"; |
| 413 | |
| 414 // This approach is sub-optimal: | |
| 415 // - Requests for amounts of memory larger than MALLOC_ABSOLUTE_MAX_SIZE | |
| 416 // (currently SIZE_T_MAX - (2 * PAGE_SIZE)) will still fail with a NULL | |
| 417 // rather than dying (see | |
| 418 // http://opensource.apple.com/source/Libc/Libc-583/gen/malloc.c for | |
| 419 // details). | |
| 420 // - It is unclear whether allocations via the C++ operator new() are affected | |
| 421 // by this (although it is likely). | |
| 422 // - This does not affect allocations from non-default zones. | |
| 423 // - It is unclear whether allocations from CoreFoundation's | |
| 424 // kCFAllocatorDefault or +[NSObject alloc] are affected by this. | |
| 425 // Nevertheless this is better than nothing for now. | |
| 426 // TODO(avi):Do better. http://crbug.com/12673 | |
| 427 | 494 |
| 428 int32 major; | 495 int32 major; |
| 429 int32 minor; | 496 int32 minor; |
| 430 int32 bugfix; | 497 int32 bugfix; |
| 431 SysInfo::OperatingSystemVersionNumbers(&major, &minor, &bugfix); | 498 SysInfo::OperatingSystemVersionNumbers(&major, &minor, &bugfix); |
| 432 bool zone_allocators_protected = ((major == 10 && minor > 6) || major > 10); | 499 bool zone_allocators_protected = ((major == 10 && minor > 6) || major > 10); |
| 433 | 500 |
| 434 malloc_zone_t* default_zone = malloc_default_zone(); | 501 malloc_zone_t* default_zone = malloc_default_zone(); |
| 435 | 502 |
| 436 vm_address_t page_start = NULL; | 503 vm_address_t page_start = NULL; |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 452 << "Failed to get system allocation functions."; | 519 << "Failed to get system allocation functions."; |
| 453 | 520 |
| 454 default_zone->malloc = oom_killer_malloc; | 521 default_zone->malloc = oom_killer_malloc; |
| 455 default_zone->calloc = oom_killer_calloc; | 522 default_zone->calloc = oom_killer_calloc; |
| 456 default_zone->valloc = oom_killer_valloc; | 523 default_zone->valloc = oom_killer_valloc; |
| 457 default_zone->realloc = oom_killer_realloc; | 524 default_zone->realloc = oom_killer_realloc; |
| 458 | 525 |
| 459 if (zone_allocators_protected) { | 526 if (zone_allocators_protected) { |
| 460 mprotect(reinterpret_cast<void*>(page_start), len, PROT_READ); | 527 mprotect(reinterpret_cast<void*>(page_start), len, PROT_READ); |
| 461 } | 528 } |
| 529 | |
| 530 // === C++ operator new === | |
| 531 | |
| 532 // Yes, operator new does call through to malloc, but this will catch failures | |
| 533 // that our imperfect handling of malloc cannot. | |
| 534 | |
| 535 std::set_new_handler(oom_killer_new); | |
| 536 | |
| 537 // === Core Foundation CFAllocators === | |
| 538 | |
| 539 // This will not catch allocation done by custom allocators, but will catch | |
| 540 // all allocation done by system-provided ones. | |
| 541 | |
| 542 CHECK(!g_old_cfallocator_system_default && !g_old_cfallocator_malloc && | |
| 543 !g_old_cfallocator_malloc_zone) | |
| 544 << "Old allocators unexpectedly non-null"; | |
| 545 | |
| 546 ChromeCFAllocatorRef allocator = const_cast<ChromeCFAllocatorRef>( | |
| 547 reinterpret_cast<const ChromeCFAllocator*>(kCFAllocatorSystemDefault)); | |
| 548 g_old_cfallocator_system_default = allocator->context.allocate; | |
| 549 CHECK(g_old_cfallocator_system_default) | |
| 550 << "Failed to get kCFAllocatorSystemDefault allocation function."; | |
| 551 allocator->context.allocate = oom_killer_cfallocator_system_default; | |
| 552 | |
| 553 allocator = const_cast<ChromeCFAllocatorRef>( | |
| 554 reinterpret_cast<const ChromeCFAllocator*>(kCFAllocatorMalloc)); | |
| 555 g_old_cfallocator_malloc = allocator->context.allocate; | |
| 556 CHECK(g_old_cfallocator_malloc) | |
| 557 << "Failed to get kCFAllocatorMalloc allocation function."; | |
| 558 allocator->context.allocate = oom_killer_cfallocator_malloc; | |
| 559 | |
| 560 allocator = const_cast<ChromeCFAllocatorRef>( | |
| 561 reinterpret_cast<const ChromeCFAllocator*>(kCFAllocatorMallocZone)); | |
| 562 g_old_cfallocator_malloc_zone = allocator->context.allocate; | |
| 563 CHECK(g_old_cfallocator_malloc_zone) | |
| 564 << "Failed to get kCFAllocatorMallocZone allocation function."; | |
| 565 allocator->context.allocate = oom_killer_cfallocator_malloc_zone; | |
| 566 | |
| 567 // === Cocoa NSObject allocation === | |
| 568 | |
| 569 // Note that both +[NSObject new] and +[NSObject alloc] call through to | |
| 570 // +[NSObject allocWithZone:]. | |
| 571 | |
| 572 CHECK(!g_old_allocWithZone) | |
| 573 << "Old allocator unexpectedly non-null"; | |
| 574 | |
| 575 Class nsobject_class = [NSObject class]; | |
| 576 Method orig_method = class_getClassMethod(nsobject_class, | |
| 577 @selector(allocWithZone:)); | |
|
Mark Mentovai
2010/03/18 19:43:10
I'm still curious about what sel_getName(method_ge
| |
| 578 g_old_allocWithZone = reinterpret_cast<allocWithZone_t>( | |
| 579 method_getImplementation(orig_method)); | |
| 580 CHECK(g_old_allocWithZone) | |
| 581 << "Failed to get allocWithZone allocation function."; | |
| 582 method_setImplementation(orig_method, | |
| 583 reinterpret_cast<IMP>(oom_killer_allocWithZone)); | |
| 584 | |
|
Avi (use Gerrit)
2010/03/17 21:44:06
removed blank line here.
| |
| 462 } | 585 } |
| 463 | 586 |
| 464 } // namespace base | 587 } // namespace base |
| OLD | NEW |