| 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 |