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 |