| OLD | NEW |
| 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 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/browser/ui/cocoa/objc_zombie.h" |
| 6 | 6 |
| 7 #include <dlfcn.h> | 7 #include <dlfcn.h> |
| 8 #include <mach-o/dyld.h> | 8 #include <mach-o/dyld.h> |
| 9 #include <mach-o/nlist.h> | 9 #include <mach-o/nlist.h> |
| 10 | 10 |
| 11 #import <objc/objc-class.h> | 11 #import <objc/objc-class.h> |
| 12 | 12 |
| 13 #include "base/lock.h" | |
| 14 #include "base/logging.h" | 13 #include "base/logging.h" |
| 14 #include "base/synchronization/lock.h" |
| 15 #import "chrome/app/breakpad_mac.h" | 15 #import "chrome/app/breakpad_mac.h" |
| 16 #import "chrome/browser/ui/cocoa/objc_method_swizzle.h" | 16 #import "chrome/browser/ui/cocoa/objc_method_swizzle.h" |
| 17 | 17 |
| 18 // Deallocated objects are re-classed as |CrZombie|. No superclass | 18 // Deallocated objects are re-classed as |CrZombie|. No superclass |
| 19 // because then the class would have to override many/most of the | 19 // because then the class would have to override many/most of the |
| 20 // inherited methods (|NSObject| is like a category magnet!). | 20 // inherited methods (|NSObject| is like a category magnet!). |
| 21 @interface CrZombie { | 21 @interface CrZombie { |
| 22 Class isa; | 22 Class isa; |
| 23 } | 23 } |
| 24 @end | 24 @end |
| (...skipping 24 matching lines...) Expand all Loading... |
| 49 // treadmill). | 49 // treadmill). |
| 50 Class g_zombieClass = Nil; // cached [CrZombie class] | 50 Class g_zombieClass = Nil; // cached [CrZombie class] |
| 51 Class g_fatZombieClass = Nil; // cached [CrFatZombie class] | 51 Class g_fatZombieClass = Nil; // cached [CrFatZombie class] |
| 52 size_t g_fatZombieSize = 0; | 52 size_t g_fatZombieSize = 0; |
| 53 | 53 |
| 54 // Whether to zombie all freed objects, or only those which return YES | 54 // Whether to zombie all freed objects, or only those which return YES |
| 55 // from |-shouldBecomeCrZombie|. | 55 // from |-shouldBecomeCrZombie|. |
| 56 BOOL g_zombieAllObjects = NO; | 56 BOOL g_zombieAllObjects = NO; |
| 57 | 57 |
| 58 // Protects |g_zombieCount|, |g_zombieIndex|, and |g_zombies|. | 58 // Protects |g_zombieCount|, |g_zombieIndex|, and |g_zombies|. |
| 59 Lock lock_; | 59 base::Lock lock_; |
| 60 | 60 |
| 61 // How many zombies to keep before freeing, and the current head of | 61 // How many zombies to keep before freeing, and the current head of |
| 62 // the circular buffer. | 62 // the circular buffer. |
| 63 size_t g_zombieCount = 0; | 63 size_t g_zombieCount = 0; |
| 64 size_t g_zombieIndex = 0; | 64 size_t g_zombieIndex = 0; |
| 65 | 65 |
| 66 typedef struct { | 66 typedef struct { |
| 67 id object; // The zombied object. | 67 id object; // The zombied object. |
| 68 Class wasa; // Value of |object->isa| before we replaced it. | 68 Class wasa; // Value of |object->isa| before we replaced it. |
| 69 } ZombieRecord; | 69 } ZombieRecord; |
| (...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 140 } else { | 140 } else { |
| 141 self->isa = g_zombieClass; | 141 self->isa = g_zombieClass; |
| 142 } | 142 } |
| 143 | 143 |
| 144 // The new record to swap into |g_zombies|. If |g_zombieCount| is | 144 // The new record to swap into |g_zombies|. If |g_zombieCount| is |
| 145 // zero, then |self| will be freed immediately. | 145 // zero, then |self| will be freed immediately. |
| 146 ZombieRecord zombieToFree = {self, wasa}; | 146 ZombieRecord zombieToFree = {self, wasa}; |
| 147 | 147 |
| 148 // Don't involve the lock when creating zombies without a treadmill. | 148 // Don't involve the lock when creating zombies without a treadmill. |
| 149 if (g_zombieCount > 0) { | 149 if (g_zombieCount > 0) { |
| 150 AutoLock pin(lock_); | 150 base::AutoLock pin(lock_); |
| 151 | 151 |
| 152 // Check the count again in a thread-safe manner. | 152 // Check the count again in a thread-safe manner. |
| 153 if (g_zombieCount > 0) { | 153 if (g_zombieCount > 0) { |
| 154 // Put the current object on the treadmill and keep the previous | 154 // Put the current object on the treadmill and keep the previous |
| 155 // occupant. | 155 // occupant. |
| 156 std::swap(zombieToFree, g_zombies[g_zombieIndex]); | 156 std::swap(zombieToFree, g_zombies[g_zombieIndex]); |
| 157 | 157 |
| 158 // Bump the index forward. | 158 // Bump the index forward. |
| 159 g_zombieIndex = (g_zombieIndex + 1) % g_zombieCount; | 159 g_zombieIndex = (g_zombieIndex + 1) % g_zombieCount; |
| 160 } | 160 } |
| 161 } | 161 } |
| 162 | 162 |
| 163 // Do the free out here to prevent any chance of deadlock. | 163 // Do the free out here to prevent any chance of deadlock. |
| 164 if (zombieToFree.object) | 164 if (zombieToFree.object) |
| 165 free(zombieToFree.object); | 165 free(zombieToFree.object); |
| 166 } | 166 } |
| 167 | 167 |
| 168 // Attempt to determine the original class of zombie |object|. | 168 // Attempt to determine the original class of zombie |object|. |
| 169 Class ZombieWasa(id object) { | 169 Class ZombieWasa(id object) { |
| 170 // Fat zombies can hold onto their |wasa| past the point where the | 170 // Fat zombies can hold onto their |wasa| past the point where the |
| 171 // object was actually freed. Note that to arrive here at all, | 171 // object was actually freed. Note that to arrive here at all, |
| 172 // |object|'s memory must still be accessible. | 172 // |object|'s memory must still be accessible. |
| 173 if (object_getClass(object) == g_fatZombieClass) | 173 if (object_getClass(object) == g_fatZombieClass) |
| 174 return static_cast<CrFatZombie*>(object)->wasa; | 174 return static_cast<CrFatZombie*>(object)->wasa; |
| 175 | 175 |
| 176 // For instances which weren't big enough to store |wasa|, check if | 176 // For instances which weren't big enough to store |wasa|, check if |
| 177 // the object is still on the treadmill. | 177 // the object is still on the treadmill. |
| 178 AutoLock pin(lock_); | 178 base::AutoLock pin(lock_); |
| 179 for (size_t i=0; i < g_zombieCount; ++i) { | 179 for (size_t i=0; i < g_zombieCount; ++i) { |
| 180 if (g_zombies[i].object == object) | 180 if (g_zombies[i].object == object) |
| 181 return g_zombies[i].wasa; | 181 return g_zombies[i].wasa; |
| 182 } | 182 } |
| 183 | 183 |
| 184 return Nil; | 184 return Nil; |
| 185 } | 185 } |
| 186 | 186 |
| 187 // Log a message to a freed object. |wasa| is the object's original | 187 // Log a message to a freed object. |wasa| is the object's original |
| 188 // class. |aSelector| is the selector which the calling code was | 188 // class. |aSelector| is the selector which the calling code was |
| (...skipping 129 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 318 const IMP prevDeallocIMP = method_setImplementation(m, (IMP)ZombieDealloc); | 318 const IMP prevDeallocIMP = method_setImplementation(m, (IMP)ZombieDealloc); |
| 319 DCHECK(prevDeallocIMP == g_originalDeallocIMP || | 319 DCHECK(prevDeallocIMP == g_originalDeallocIMP || |
| 320 prevDeallocIMP == (IMP)ZombieDealloc); | 320 prevDeallocIMP == (IMP)ZombieDealloc); |
| 321 | 321 |
| 322 // Grab the current set of zombies. This is thread-safe because | 322 // Grab the current set of zombies. This is thread-safe because |
| 323 // only the main thread can change these. | 323 // only the main thread can change these. |
| 324 const size_t oldCount = g_zombieCount; | 324 const size_t oldCount = g_zombieCount; |
| 325 ZombieRecord* oldZombies = g_zombies; | 325 ZombieRecord* oldZombies = g_zombies; |
| 326 | 326 |
| 327 { | 327 { |
| 328 AutoLock pin(lock_); | 328 base::AutoLock pin(lock_); |
| 329 | 329 |
| 330 // Save the old index in case zombies need to be transferred. | 330 // Save the old index in case zombies need to be transferred. |
| 331 size_t oldIndex = g_zombieIndex; | 331 size_t oldIndex = g_zombieIndex; |
| 332 | 332 |
| 333 // Create the new zombie treadmill, disabling zombies in case of | 333 // Create the new zombie treadmill, disabling zombies in case of |
| 334 // failure. | 334 // failure. |
| 335 g_zombieIndex = 0; | 335 g_zombieIndex = 0; |
| 336 g_zombieCount = zombieCount; | 336 g_zombieCount = zombieCount; |
| 337 g_zombies = NULL; | 337 g_zombies = NULL; |
| 338 if (g_zombieCount) { | 338 if (g_zombieCount) { |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 389 // Put back the original implementation of -[NSObject dealloc]. | 389 // Put back the original implementation of -[NSObject dealloc]. |
| 390 Method m = class_getInstanceMethod([NSObject class], @selector(dealloc)); | 390 Method m = class_getInstanceMethod([NSObject class], @selector(dealloc)); |
| 391 CHECK(m); | 391 CHECK(m); |
| 392 method_setImplementation(m, g_originalDeallocIMP); | 392 method_setImplementation(m, g_originalDeallocIMP); |
| 393 | 393 |
| 394 // Can safely grab this because it only happens on the main thread. | 394 // Can safely grab this because it only happens on the main thread. |
| 395 const size_t oldCount = g_zombieCount; | 395 const size_t oldCount = g_zombieCount; |
| 396 ZombieRecord* oldZombies = g_zombies; | 396 ZombieRecord* oldZombies = g_zombies; |
| 397 | 397 |
| 398 { | 398 { |
| 399 AutoLock pin(lock_); // In case any |-dealloc| are in-progress. | 399 base::AutoLock pin(lock_); // In case any |-dealloc| are in-progress. |
| 400 g_zombieCount = 0; | 400 g_zombieCount = 0; |
| 401 g_zombies = NULL; | 401 g_zombies = NULL; |
| 402 } | 402 } |
| 403 | 403 |
| 404 // Free any remaining zombies. | 404 // Free any remaining zombies. |
| 405 if (oldZombies) { | 405 if (oldZombies) { |
| 406 for (size_t i = 0; i < oldCount; ++i) { | 406 for (size_t i = 0; i < oldCount; ++i) { |
| 407 if (oldZombies[i].object) | 407 if (oldZombies[i].object) |
| 408 free(oldZombies[i].object); | 408 free(oldZombies[i].object); |
| 409 } | 409 } |
| 410 free(oldZombies); | 410 free(oldZombies); |
| 411 } | 411 } |
| 412 } | 412 } |
| 413 | 413 |
| 414 } // namespace ObjcEvilDoers | 414 } // namespace ObjcEvilDoers |
| OLD | NEW |