OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 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 | 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 #import "chrome/browser/ui/cocoa/objc_zombie.h" | 5 #import "chrome/common/mac/objc_zombie.h" |
6 | 6 |
7 #include <dlfcn.h> | 7 #include <dlfcn.h> |
8 #include <execinfo.h> | 8 #include <execinfo.h> |
9 #include <mach-o/dyld.h> | 9 #include <mach-o/dyld.h> |
10 #include <mach-o/nlist.h> | 10 #include <mach-o/nlist.h> |
11 | 11 |
12 #import <objc/objc-class.h> | 12 #import <objc/objc-class.h> |
13 | 13 |
14 #include <algorithm> | 14 #include <algorithm> |
15 #include <iostream> | 15 #include <iostream> |
16 | 16 |
17 #include "base/debug/stack_trace.h" | 17 #include "base/debug/stack_trace.h" |
18 #include "base/logging.h" | 18 #include "base/logging.h" |
19 #include "base/mac/mac_util.h" | 19 #include "base/mac/mac_util.h" |
20 #include "base/metrics/histogram.h" | 20 #include "base/metrics/histogram.h" |
21 #include "base/synchronization/lock.h" | 21 #include "base/synchronization/lock.h" |
22 #import "chrome/app/breakpad_mac.h" | 22 #import "chrome/app/breakpad_mac.h" |
23 #import "chrome/browser/ui/cocoa/objc_method_swizzle.h" | 23 #import "chrome/common/mac/objc_method_swizzle.h" |
24 | 24 |
25 // Deallocated objects are re-classed as |CrZombie|. No superclass | 25 // Deallocated objects are re-classed as |CrZombie|. No superclass |
26 // because then the class would have to override many/most of the | 26 // because then the class would have to override many/most of the |
27 // inherited methods (|NSObject| is like a category magnet!). | 27 // inherited methods (|NSObject| is like a category magnet!). |
28 @interface CrZombie { | 28 @interface CrZombie { |
29 Class isa; | 29 Class isa; |
30 } | 30 } |
31 @end | 31 @end |
32 | 32 |
33 // Objects with enough space are made into "fat" zombies, which | 33 // Objects with enough space are made into "fat" zombies, which |
(...skipping 405 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
439 @implementation NSObject (CrZombie) | 439 @implementation NSObject (CrZombie) |
440 | 440 |
441 - (BOOL)shouldBecomeCrZombie { | 441 - (BOOL)shouldBecomeCrZombie { |
442 return NO; | 442 return NO; |
443 } | 443 } |
444 | 444 |
445 @end | 445 @end |
446 | 446 |
447 namespace ObjcEvilDoers { | 447 namespace ObjcEvilDoers { |
448 | 448 |
449 BOOL ZombieEnable(BOOL zombieAllObjects, | 449 bool ZombieEnable(bool zombieAllObjects, |
450 size_t zombieCount) { | 450 size_t zombieCount) { |
451 // Only allow enable/disable on the main thread, just to keep things | 451 // Only allow enable/disable on the main thread, just to keep things |
452 // simple. | 452 // simple. |
453 CHECK([NSThread isMainThread]); | 453 CHECK([NSThread isMainThread]); |
454 | 454 |
455 if (!ZombieInit()) | 455 if (!ZombieInit()) |
456 return NO; | 456 return false; |
457 | 457 |
458 g_zombieAllObjects = zombieAllObjects; | 458 g_zombieAllObjects = zombieAllObjects; |
459 | 459 |
460 // Replace the implementation of -[NSObject dealloc]. | 460 // Replace the implementation of -[NSObject dealloc]. |
461 Method m = class_getInstanceMethod([NSObject class], @selector(dealloc)); | 461 Method m = class_getInstanceMethod([NSObject class], @selector(dealloc)); |
462 if (!m) | 462 if (!m) |
463 return NO; | 463 return false; |
464 | 464 |
465 const IMP prevDeallocIMP = method_setImplementation(m, (IMP)ZombieDealloc); | 465 const IMP prevDeallocIMP = method_setImplementation(m, (IMP)ZombieDealloc); |
466 DCHECK(prevDeallocIMP == g_originalDeallocIMP || | 466 DCHECK(prevDeallocIMP == g_originalDeallocIMP || |
467 prevDeallocIMP == (IMP)ZombieDealloc); | 467 prevDeallocIMP == (IMP)ZombieDealloc); |
468 | 468 |
469 // Grab the current set of zombies. This is thread-safe because | 469 // Grab the current set of zombies. This is thread-safe because |
470 // only the main thread can change these. | 470 // only the main thread can change these. |
471 const size_t oldCount = g_zombieCount; | 471 const size_t oldCount = g_zombieCount; |
472 ZombieRecord* oldZombies = g_zombies; | 472 ZombieRecord* oldZombies = g_zombies; |
473 | 473 |
(...skipping 10 matching lines...) Expand all Loading... |
484 g_zombies = NULL; | 484 g_zombies = NULL; |
485 if (g_zombieCount) { | 485 if (g_zombieCount) { |
486 g_zombies = | 486 g_zombies = |
487 static_cast<ZombieRecord*>(calloc(g_zombieCount, sizeof(*g_zombies))); | 487 static_cast<ZombieRecord*>(calloc(g_zombieCount, sizeof(*g_zombies))); |
488 if (!g_zombies) { | 488 if (!g_zombies) { |
489 NOTREACHED(); | 489 NOTREACHED(); |
490 g_zombies = oldZombies; | 490 g_zombies = oldZombies; |
491 g_zombieCount = oldCount; | 491 g_zombieCount = oldCount; |
492 g_zombieIndex = oldIndex; | 492 g_zombieIndex = oldIndex; |
493 ZombieDisable(); | 493 ZombieDisable(); |
494 return NO; | 494 return false; |
495 } | 495 } |
496 } | 496 } |
497 | 497 |
498 // If the count is changing, allow some of the zombies to continue | 498 // If the count is changing, allow some of the zombies to continue |
499 // shambling forward. | 499 // shambling forward. |
500 const size_t sharedCount = std::min(oldCount, zombieCount); | 500 const size_t sharedCount = std::min(oldCount, zombieCount); |
501 if (sharedCount) { | 501 if (sharedCount) { |
502 // Get index of the first shared zombie. | 502 // Get index of the first shared zombie. |
503 oldIndex = (oldIndex + oldCount - sharedCount) % oldCount; | 503 oldIndex = (oldIndex + oldCount - sharedCount) % oldCount; |
504 | 504 |
505 for (; g_zombieIndex < sharedCount; ++ g_zombieIndex) { | 505 for (; g_zombieIndex < sharedCount; ++ g_zombieIndex) { |
506 DCHECK_LT(g_zombieIndex, g_zombieCount); | 506 DCHECK_LT(g_zombieIndex, g_zombieCount); |
507 DCHECK_LT(oldIndex, oldCount); | 507 DCHECK_LT(oldIndex, oldCount); |
508 std::swap(g_zombies[g_zombieIndex], oldZombies[oldIndex]); | 508 std::swap(g_zombies[g_zombieIndex], oldZombies[oldIndex]); |
509 oldIndex = (oldIndex + 1) % oldCount; | 509 oldIndex = (oldIndex + 1) % oldCount; |
510 } | 510 } |
511 g_zombieIndex %= g_zombieCount; | 511 g_zombieIndex %= g_zombieCount; |
512 } | 512 } |
513 } | 513 } |
514 | 514 |
515 // Free the old treadmill and any remaining zombies. | 515 // Free the old treadmill and any remaining zombies. |
516 if (oldZombies) { | 516 if (oldZombies) { |
517 for (size_t i = 0; i < oldCount; ++i) { | 517 for (size_t i = 0; i < oldCount; ++i) { |
518 if (oldZombies[i].object) | 518 if (oldZombies[i].object) |
519 object_dispose(oldZombies[i].object); | 519 object_dispose(oldZombies[i].object); |
520 } | 520 } |
521 free(oldZombies); | 521 free(oldZombies); |
522 } | 522 } |
523 | 523 |
524 return YES; | 524 return true; |
525 } | 525 } |
526 | 526 |
527 void ZombieDisable() { | 527 void ZombieDisable() { |
528 // Only allow enable/disable on the main thread, just to keep things | 528 // Only allow enable/disable on the main thread, just to keep things |
529 // simple. | 529 // simple. |
530 CHECK([NSThread isMainThread]); | 530 CHECK([NSThread isMainThread]); |
531 | 531 |
532 // |ZombieInit()| was never called. | 532 // |ZombieInit()| was never called. |
533 if (!g_originalDeallocIMP) | 533 if (!g_originalDeallocIMP) |
534 return; | 534 return; |
(...skipping 17 matching lines...) Expand all Loading... |
552 if (oldZombies) { | 552 if (oldZombies) { |
553 for (size_t i = 0; i < oldCount; ++i) { | 553 for (size_t i = 0; i < oldCount; ++i) { |
554 if (oldZombies[i].object) | 554 if (oldZombies[i].object) |
555 object_dispose(oldZombies[i].object); | 555 object_dispose(oldZombies[i].object); |
556 } | 556 } |
557 free(oldZombies); | 557 free(oldZombies); |
558 } | 558 } |
559 } | 559 } |
560 | 560 |
561 } // namespace ObjcEvilDoers | 561 } // namespace ObjcEvilDoers |
OLD | NEW |