| 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/common/mac/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/lazy_instance.h" |
| 18 #include "base/logging.h" | 19 #include "base/logging.h" |
| 19 #include "base/mac/crash_logging.h" | 20 #include "base/mac/crash_logging.h" |
| 20 #include "base/mac/mac_util.h" | 21 #include "base/mac/mac_util.h" |
| 21 #include "base/metrics/histogram.h" | 22 #include "base/metrics/histogram.h" |
| 22 #include "base/synchronization/lock.h" | 23 #include "base/synchronization/lock.h" |
| 23 #import "chrome/common/mac/objc_method_swizzle.h" | 24 #import "chrome/common/mac/objc_method_swizzle.h" |
| 24 | 25 |
| 25 // Deallocated objects are re-classed as |CrZombie|. No superclass | 26 // Deallocated objects are re-classed as |CrZombie|. No superclass |
| 26 // because then the class would have to override many/most of the | 27 // because then the class would have to override many/most of the |
| 27 // inherited methods (|NSObject| is like a category magnet!). | 28 // inherited methods (|NSObject| is like a category magnet!). |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 68 // treadmill). | 69 // treadmill). |
| 69 Class g_zombieClass = Nil; // cached [CrZombie class] | 70 Class g_zombieClass = Nil; // cached [CrZombie class] |
| 70 Class g_fatZombieClass = Nil; // cached [CrFatZombie class] | 71 Class g_fatZombieClass = Nil; // cached [CrFatZombie class] |
| 71 size_t g_fatZombieSize = 0; | 72 size_t g_fatZombieSize = 0; |
| 72 | 73 |
| 73 // Whether to zombie all freed objects, or only those which return YES | 74 // Whether to zombie all freed objects, or only those which return YES |
| 74 // from |-shouldBecomeCrZombie|. | 75 // from |-shouldBecomeCrZombie|. |
| 75 BOOL g_zombieAllObjects = NO; | 76 BOOL g_zombieAllObjects = NO; |
| 76 | 77 |
| 77 // Protects |g_zombieCount|, |g_zombieIndex|, and |g_zombies|. | 78 // Protects |g_zombieCount|, |g_zombieIndex|, and |g_zombies|. |
| 78 base::Lock lock_; | 79 base::LazyInstance<base::Lock, base::LeakyLazyInstanceTraits<base::Lock> > |
| 80 g_lock = LAZY_INSTANCE_INITIALIZER; |
| 79 | 81 |
| 80 // How many zombies to keep before freeing, and the current head of | 82 // How many zombies to keep before freeing, and the current head of |
| 81 // the circular buffer. | 83 // the circular buffer. |
| 82 size_t g_zombieCount = 0; | 84 size_t g_zombieCount = 0; |
| 83 size_t g_zombieIndex = 0; | 85 size_t g_zombieIndex = 0; |
| 84 | 86 |
| 85 typedef struct { | 87 typedef struct { |
| 86 id object; // The zombied object. | 88 id object; // The zombied object. |
| 87 Class wasa; // Value of |object->isa| before we replaced it. | 89 Class wasa; // Value of |object->isa| before we replaced it. |
| 88 void* trace[kBacktraceDepth]; // Backtrace at point of deallocation. | 90 void* trace[kBacktraceDepth]; // Backtrace at point of deallocation. |
| (...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 206 } | 208 } |
| 207 | 209 |
| 208 // The new record to swap into |g_zombies|. If |g_zombieCount| is | 210 // The new record to swap into |g_zombies|. If |g_zombieCount| is |
| 209 // zero, then |self| will be freed immediately. | 211 // zero, then |self| will be freed immediately. |
| 210 ZombieRecord zombieToFree = {self, wasa}; | 212 ZombieRecord zombieToFree = {self, wasa}; |
| 211 zombieToFree.traceDepth = | 213 zombieToFree.traceDepth = |
| 212 std::max(backtrace(zombieToFree.trace, kBacktraceDepth), 0); | 214 std::max(backtrace(zombieToFree.trace, kBacktraceDepth), 0); |
| 213 | 215 |
| 214 // Don't involve the lock when creating zombies without a treadmill. | 216 // Don't involve the lock when creating zombies without a treadmill. |
| 215 if (g_zombieCount > 0) { | 217 if (g_zombieCount > 0) { |
| 216 base::AutoLock pin(lock_); | 218 base::AutoLock pin(g_lock.Get()); |
| 217 | 219 |
| 218 // Check the count again in a thread-safe manner. | 220 // Check the count again in a thread-safe manner. |
| 219 if (g_zombieCount > 0) { | 221 if (g_zombieCount > 0) { |
| 220 // Put the current object on the treadmill and keep the previous | 222 // Put the current object on the treadmill and keep the previous |
| 221 // occupant. | 223 // occupant. |
| 222 std::swap(zombieToFree, g_zombies[g_zombieIndex]); | 224 std::swap(zombieToFree, g_zombies[g_zombieIndex]); |
| 223 | 225 |
| 224 // Bump the index forward. | 226 // Bump the index forward. |
| 225 g_zombieIndex = (g_zombieIndex + 1) % g_zombieCount; | 227 g_zombieIndex = (g_zombieIndex + 1) % g_zombieCount; |
| 226 } | 228 } |
| 227 } | 229 } |
| 228 | 230 |
| 229 // Do the free out here to prevent any chance of deadlock. | 231 // Do the free out here to prevent any chance of deadlock. |
| 230 if (zombieToFree.object) | 232 if (zombieToFree.object) |
| 231 object_dispose(zombieToFree.object); | 233 object_dispose(zombieToFree.object); |
| 232 } | 234 } |
| 233 | 235 |
| 234 // Search the treadmill for |object| and fill in |*record| if found. | 236 // Search the treadmill for |object| and fill in |*record| if found. |
| 235 // Returns YES if found. | 237 // Returns YES if found. |
| 236 BOOL GetZombieRecord(id object, ZombieRecord* record) { | 238 BOOL GetZombieRecord(id object, ZombieRecord* record) { |
| 237 // Holding the lock is reasonable because this should be fast, and | 239 // Holding the lock is reasonable because this should be fast, and |
| 238 // the process is going to crash presently anyhow. | 240 // the process is going to crash presently anyhow. |
| 239 base::AutoLock pin(lock_); | 241 base::AutoLock pin(g_lock.Get()); |
| 240 for (size_t i = 0; i < g_zombieCount; ++i) { | 242 for (size_t i = 0; i < g_zombieCount; ++i) { |
| 241 if (g_zombies[i].object == object) { | 243 if (g_zombies[i].object == object) { |
| 242 *record = g_zombies[i]; | 244 *record = g_zombies[i]; |
| 243 return YES; | 245 return YES; |
| 244 } | 246 } |
| 245 } | 247 } |
| 246 return NO; | 248 return NO; |
| 247 } | 249 } |
| 248 | 250 |
| 249 // Dump the symbols. This is pulled out into a function to make it | 251 // Dump the symbols. This is pulled out into a function to make it |
| (...skipping 215 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 465 const IMP prevDeallocIMP = method_setImplementation(m, (IMP)ZombieDealloc); | 467 const IMP prevDeallocIMP = method_setImplementation(m, (IMP)ZombieDealloc); |
| 466 DCHECK(prevDeallocIMP == g_originalDeallocIMP || | 468 DCHECK(prevDeallocIMP == g_originalDeallocIMP || |
| 467 prevDeallocIMP == (IMP)ZombieDealloc); | 469 prevDeallocIMP == (IMP)ZombieDealloc); |
| 468 | 470 |
| 469 // Grab the current set of zombies. This is thread-safe because | 471 // Grab the current set of zombies. This is thread-safe because |
| 470 // only the main thread can change these. | 472 // only the main thread can change these. |
| 471 const size_t oldCount = g_zombieCount; | 473 const size_t oldCount = g_zombieCount; |
| 472 ZombieRecord* oldZombies = g_zombies; | 474 ZombieRecord* oldZombies = g_zombies; |
| 473 | 475 |
| 474 { | 476 { |
| 475 base::AutoLock pin(lock_); | 477 base::AutoLock pin(g_lock.Get()); |
| 476 | 478 |
| 477 // Save the old index in case zombies need to be transferred. | 479 // Save the old index in case zombies need to be transferred. |
| 478 size_t oldIndex = g_zombieIndex; | 480 size_t oldIndex = g_zombieIndex; |
| 479 | 481 |
| 480 // Create the new zombie treadmill, disabling zombies in case of | 482 // Create the new zombie treadmill, disabling zombies in case of |
| 481 // failure. | 483 // failure. |
| 482 g_zombieIndex = 0; | 484 g_zombieIndex = 0; |
| 483 g_zombieCount = zombieCount; | 485 g_zombieCount = zombieCount; |
| 484 g_zombies = NULL; | 486 g_zombies = NULL; |
| 485 if (g_zombieCount) { | 487 if (g_zombieCount) { |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 536 // Put back the original implementation of -[NSObject dealloc]. | 538 // Put back the original implementation of -[NSObject dealloc]. |
| 537 Method m = class_getInstanceMethod([NSObject class], @selector(dealloc)); | 539 Method m = class_getInstanceMethod([NSObject class], @selector(dealloc)); |
| 538 DCHECK(m); | 540 DCHECK(m); |
| 539 method_setImplementation(m, g_originalDeallocIMP); | 541 method_setImplementation(m, g_originalDeallocIMP); |
| 540 | 542 |
| 541 // Can safely grab this because it only happens on the main thread. | 543 // Can safely grab this because it only happens on the main thread. |
| 542 const size_t oldCount = g_zombieCount; | 544 const size_t oldCount = g_zombieCount; |
| 543 ZombieRecord* oldZombies = g_zombies; | 545 ZombieRecord* oldZombies = g_zombies; |
| 544 | 546 |
| 545 { | 547 { |
| 546 base::AutoLock pin(lock_); // In case any |-dealloc| are in-progress. | 548 base::AutoLock pin(g_lock.Get()); // In case any -dealloc are in progress. |
| 547 g_zombieCount = 0; | 549 g_zombieCount = 0; |
| 548 g_zombies = NULL; | 550 g_zombies = NULL; |
| 549 } | 551 } |
| 550 | 552 |
| 551 // Free any remaining zombies. | 553 // Free any remaining zombies. |
| 552 if (oldZombies) { | 554 if (oldZombies) { |
| 553 for (size_t i = 0; i < oldCount; ++i) { | 555 for (size_t i = 0; i < oldCount; ++i) { |
| 554 if (oldZombies[i].object) | 556 if (oldZombies[i].object) |
| 555 object_dispose(oldZombies[i].object); | 557 object_dispose(oldZombies[i].object); |
| 556 } | 558 } |
| 557 free(oldZombies); | 559 free(oldZombies); |
| 558 } | 560 } |
| 559 } | 561 } |
| 560 | 562 |
| 561 } // namespace ObjcEvilDoers | 563 } // namespace ObjcEvilDoers |
| OLD | NEW |