summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorshess@chromium.org <shess@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-10-19 19:28:21 +0000
committershess@chromium.org <shess@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-10-19 19:28:21 +0000
commitabc15163399fc64f116c341687c0064068fef243 (patch)
tree9588d9ccf4f54ff433299c66e41a254acb4a69bf
parent74e85f5dada2979f1b1aac72c2e3a03654dbda2b (diff)
downloadchromium_src-abc15163399fc64f116c341687c0064068fef243.zip
chromium_src-abc15163399fc64f116c341687c0064068fef243.tar.gz
chromium_src-abc15163399fc64f116c341687c0064068fef243.tar.bz2
[Mac] Remove stale objc_zombie.mm.
Looks like this survived cleaning. BUG=none TEST=none Review URL: http://codereview.chromium.org/8342020 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@106344 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/ui/cocoa/objc_zombie.mm561
1 files changed, 0 insertions, 561 deletions
diff --git a/chrome/browser/ui/cocoa/objc_zombie.mm b/chrome/browser/ui/cocoa/objc_zombie.mm
deleted file mode 100644
index 0918fd7..0000000
--- a/chrome/browser/ui/cocoa/objc_zombie.mm
+++ /dev/null
@@ -1,561 +0,0 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "chrome/browser/ui/cocoa/objc_zombie.h"
-
-#include <dlfcn.h>
-#include <execinfo.h>
-#include <mach-o/dyld.h>
-#include <mach-o/nlist.h>
-
-#import <objc/objc-class.h>
-
-#include <algorithm>
-#include <iostream>
-
-#include "base/debug/stack_trace.h"
-#include "base/logging.h"
-#include "base/mac/crash_logging.h"
-#include "base/mac/mac_util.h"
-#include "base/metrics/histogram.h"
-#include "base/synchronization/lock.h"
-#import "chrome/common/mac/objc_method_swizzle.h"
-
-// Deallocated objects are re-classed as |CrZombie|. No superclass
-// because then the class would have to override many/most of the
-// inherited methods (|NSObject| is like a category magnet!).
-@interface CrZombie {
- Class isa;
-}
-@end
-
-// Objects with enough space are made into "fat" zombies, which
-// directly remember which class they were until reallocated.
-@interface CrFatZombie : CrZombie {
- @public
- Class wasa;
-}
-@end
-
-namespace {
-
-// The depth of backtrace to store with zombies. This directly influences
-// the amount of memory required to track zombies, so should be kept as
-// small as is useful. Unfortunately, too small and it won't poke through
-// deep autorelease and event loop stacks.
-// NOTE(shess): Breakpad currently restricts values to 255 bytes. The
-// trace is hex-encoded with "0x" prefix and " " separators, meaning
-// the maximum number of 32-bit items which can be encoded is 23.
-const size_t kBacktraceDepth = 20;
-
-// Function which destroys the contents of an object without freeing
-// the object. On 10.5 this is |object_cxxDestruct()|, which
-// traverses the class hierarchy to run the C++ destructors. On 10.6
-// this is |objc_destructInstance()| which calls
-// |object_cxxDestruct()| and removes associative references.
-// |objc_destructInstance()| returns |void*| but pretending it has no
-// return value makes the code simpler.
-typedef void DestructFn(id obj);
-DestructFn* g_objectDestruct = NULL;
-
-// The original implementation for |-[NSObject dealloc]|.
-IMP g_originalDeallocIMP = NULL;
-
-// Classes which freed objects become. |g_fatZombieSize| is the
-// minimum object size which can be made into a fat zombie (which can
-// remember which class it was before free, even after falling off the
-// treadmill).
-Class g_zombieClass = Nil; // cached [CrZombie class]
-Class g_fatZombieClass = Nil; // cached [CrFatZombie class]
-size_t g_fatZombieSize = 0;
-
-// Whether to zombie all freed objects, or only those which return YES
-// from |-shouldBecomeCrZombie|.
-BOOL g_zombieAllObjects = NO;
-
-// Protects |g_zombieCount|, |g_zombieIndex|, and |g_zombies|.
-base::Lock lock_;
-
-// How many zombies to keep before freeing, and the current head of
-// the circular buffer.
-size_t g_zombieCount = 0;
-size_t g_zombieIndex = 0;
-
-typedef struct {
- id object; // The zombied object.
- Class wasa; // Value of |object->isa| before we replaced it.
- void* trace[kBacktraceDepth]; // Backtrace at point of deallocation.
- size_t traceDepth; // Actual depth of trace[].
-} ZombieRecord;
-
-ZombieRecord* g_zombies = NULL;
-
-const char* LookupObjcRuntimePath() {
- const void* addr = reinterpret_cast<void*>(&object_getClass);
- Dl_info info;
-
- // |dladdr()| doesn't document how long |info| will stay valid...
- if (dladdr(addr, &info))
- return info.dli_fname;
-
- return NULL;
-}
-
-// Lookup |objc_destructInstance()| dynamically because it isn't
-// available on 10.5, but we link with the 10.5 SDK.
-DestructFn* LookupObjectDestruct_10_6() {
- const char* objc_runtime_path = LookupObjcRuntimePath();
- if (!objc_runtime_path)
- return NULL;
-
- void* handle = dlopen(objc_runtime_path, RTLD_LAZY | RTLD_LOCAL);
- if (!handle)
- return NULL;
-
- void* fn = dlsym(handle, "objc_destructInstance");
-
- // |fn| would normally be expected to become invalid after this
- // |dlclose()|, but since the |dlopen()| was on a library
- // containing an already-mapped symbol, it will remain valid.
- dlclose(handle);
- return reinterpret_cast<DestructFn*>(fn);
-}
-
-// Under 10.5 |object_cxxDestruct()| is used but unfortunately it is
-// |__private_extern__| in the runtime, meaning |dlsym()| cannot reach it.
-DestructFn* LookupObjectDestruct_10_5() {
- // |nlist()| is only present for 32-bit.
-#if ARCH_CPU_32_BITS
- const char* objc_runtime_path = LookupObjcRuntimePath();
- if (!objc_runtime_path)
- return NULL;
-
- struct nlist nl[3];
- bzero(&nl, sizeof(nl));
-
- nl[0].n_un.n_name = const_cast<char*>("_object_cxxDestruct");
-
- // My ability to calculate the base for offsets is apparently poor.
- // Use |class_addIvar| as a known reference point.
- nl[1].n_un.n_name = const_cast<char*>("_class_addIvar");
-
- if (nlist(objc_runtime_path, nl) < 0 ||
- nl[0].n_type == N_UNDF || nl[1].n_type == N_UNDF)
- return NULL;
-
- // Back out offset to |class_addIvar()| to get the baseline, then
- // add back offset to |object_cxxDestruct()|.
- uintptr_t reference_addr = reinterpret_cast<uintptr_t>(&class_addIvar);
- reference_addr -= nl[1].n_value;
- reference_addr += nl[0].n_value;
-
- return reinterpret_cast<DestructFn*>(reference_addr);
-#endif
-
- return NULL;
-}
-
-// Replacement |-dealloc| which turns objects into zombies and places
-// them into |g_zombies| to be freed later.
-void ZombieDealloc(id self, SEL _cmd) {
- // This code should only be called when it is implementing |-dealloc|.
- DCHECK_EQ(_cmd, @selector(dealloc));
-
- // Use the original |-dealloc| if the object doesn't wish to be
- // zombied.
- if (!g_zombieAllObjects && ![self shouldBecomeCrZombie]) {
- g_originalDeallocIMP(self, _cmd);
- return;
- }
-
- // Use the original |-dealloc| if |g_objectDestruct| was never
- // initialized, because otherwise C++ destructors won't be called.
- // This case should be impossible, but doing it wrong would cause
- // terrible problems.
- DCHECK(g_objectDestruct);
- if (!g_objectDestruct) {
- g_originalDeallocIMP(self, _cmd);
- return;
- }
-
- Class wasa = object_getClass(self);
- const size_t size = class_getInstanceSize(wasa);
-
- // Destroy the instance by calling C++ destructors and clearing it
- // to something unlikely to work well if someone references it.
- // NOTE(shess): |object_dispose()| will call this again when the
- // zombie falls off the treadmill! But by then |isa| will be a
- // class without C++ destructors or associative references, so it
- // won't hurt anything.
- (*g_objectDestruct)(self);
- memset(self, '!', size);
-
- // If the instance is big enough, make it into a fat zombie and have
- // it remember the old |isa|. Otherwise make it a regular zombie.
- // Setting |isa| rather than using |object_setClass()| because that
- // function is implemented with a memory barrier. The runtime's
- // |_internal_object_dispose()| (in objc-class.m) does this, so it
- // should be safe (messaging free'd objects shouldn't be expected to
- // be thread-safe in the first place).
- if (size >= g_fatZombieSize) {
- self->isa = g_fatZombieClass;
- static_cast<CrFatZombie*>(self)->wasa = wasa;
- } else {
- self->isa = g_zombieClass;
- }
-
- // The new record to swap into |g_zombies|. If |g_zombieCount| is
- // zero, then |self| will be freed immediately.
- ZombieRecord zombieToFree = {self, wasa};
- zombieToFree.traceDepth =
- std::max(backtrace(zombieToFree.trace, kBacktraceDepth), 0);
-
- // Don't involve the lock when creating zombies without a treadmill.
- if (g_zombieCount > 0) {
- base::AutoLock pin(lock_);
-
- // Check the count again in a thread-safe manner.
- if (g_zombieCount > 0) {
- // Put the current object on the treadmill and keep the previous
- // occupant.
- std::swap(zombieToFree, g_zombies[g_zombieIndex]);
-
- // Bump the index forward.
- g_zombieIndex = (g_zombieIndex + 1) % g_zombieCount;
- }
- }
-
- // Do the free out here to prevent any chance of deadlock.
- if (zombieToFree.object)
- object_dispose(zombieToFree.object);
-}
-
-// Search the treadmill for |object| and fill in |*record| if found.
-// Returns YES if found.
-BOOL GetZombieRecord(id object, ZombieRecord* record) {
- // Holding the lock is reasonable because this should be fast, and
- // the process is going to crash presently anyhow.
- base::AutoLock pin(lock_);
- for (size_t i = 0; i < g_zombieCount; ++i) {
- if (g_zombies[i].object == object) {
- *record = g_zombies[i];
- return YES;
- }
- }
- return NO;
-}
-
-// Dump the symbols. This is pulled out into a function to make it
-// easy to use DCHECK to dump only in debug builds.
-BOOL DumpDeallocTrace(const void* const* array, int size) {
- // |cerr| because that's where PrintBacktrace() sends output.
- std::cerr << "Backtrace from -dealloc:\n";
- base::debug::StackTrace(array, size).PrintBacktrace();
-
- return YES;
-}
-
-// Log a message to a freed object. |wasa| is the object's original
-// class. |aSelector| is the selector which the calling code was
-// attempting to send. |viaSelector| is the selector of the
-// dispatch-related method which is being invoked to send |aSelector|
-// (for instance, -respondsToSelector:).
-void ZombieObjectCrash(id object, SEL aSelector, SEL viaSelector) {
- ZombieRecord record;
- BOOL found = GetZombieRecord(object, &record);
-
- // The object's class can be in the zombie record, but if that is
- // not available it can also be in the object itself (in most cases).
- Class wasa = Nil;
- if (found) {
- wasa = record.wasa;
- } else if (object_getClass(object) == g_fatZombieClass) {
- wasa = static_cast<CrFatZombie*>(object)->wasa;
- }
- const char* wasaName = (wasa ? class_getName(wasa) : "<unknown>");
-
- NSString* aString =
- [NSString stringWithFormat:@"Zombie <%s: %p> received -%s",
- wasaName, object, sel_getName(aSelector)];
- if (viaSelector != NULL) {
- const char* viaName = sel_getName(viaSelector);
- aString = [aString stringByAppendingFormat:@" (via -%s)", viaName];
- }
-
- // Set a value for breakpad to report.
- base::mac::SetCrashKeyValue(@"zombie", aString);
-
- // Hex-encode the backtrace and tuck it into a breakpad key.
- NSString* deallocTrace = @"<unknown>";
- if (found && record.traceDepth) {
- NSMutableArray* hexBacktrace =
- [NSMutableArray arrayWithCapacity:record.traceDepth];
- for (size_t i = 0; i < record.traceDepth; ++i) {
- NSString* s = [NSString stringWithFormat:@"%p", record.trace[i]];
- [hexBacktrace addObject:s];
- }
- deallocTrace = [hexBacktrace componentsJoinedByString:@" "];
-
- // Warn someone if this exceeds the breakpad limits.
- DCHECK_LE(strlen([deallocTrace UTF8String]), 255U);
- }
- base::mac::SetCrashKeyValue(@"zombie_dealloc_bt", deallocTrace);
-
- // Log -dealloc backtrace in debug builds then crash with a useful
- // stack trace.
- if (found && record.traceDepth) {
- DCHECK(DumpDeallocTrace(record.trace, record.traceDepth));
- } else {
- DLOG(INFO) << "Unable to generate backtrace from -dealloc.";
- }
- DLOG(FATAL) << [aString UTF8String];
-
- // This is how about:crash is implemented. Using instead of
- // |base::debug::BreakDebugger()| or |LOG(FATAL)| to make the top of
- // stack more immediately obvious in crash dumps.
- int* zero = NULL;
- *zero = 0;
-}
-
-// For monitoring failures in |ZombieInit()|.
-enum ZombieFailure {
- FAILED_10_5,
- FAILED_10_6,
-
- // Add new versions before here.
- FAILED_MAX,
-};
-
-void RecordZombieFailure(ZombieFailure failure) {
- UMA_HISTOGRAM_ENUMERATION("OSX.ZombieInitFailure", failure, FAILED_MAX);
-}
-
-// Initialize our globals, returning YES on success.
-BOOL ZombieInit() {
- static BOOL initialized = NO;
- if (initialized)
- return YES;
-
- // Whitelist releases that are compatible with objc zombies.
- if (base::mac::IsOSLeopard()) {
- g_objectDestruct = LookupObjectDestruct_10_5();
- if (!g_objectDestruct) {
- RecordZombieFailure(FAILED_10_5);
- return NO;
- }
- } else if (base::mac::IsOSSnowLeopard()) {
- g_objectDestruct = LookupObjectDestruct_10_6();
- if (!g_objectDestruct) {
- RecordZombieFailure(FAILED_10_6);
- return NO;
- }
- } else if (base::mac::IsOSLionOrLater()) {
- // Assume the future looks like the present.
- g_objectDestruct = LookupObjectDestruct_10_6();
-
- // Put all future failures into the MAX bin. New OS releases come
- // out infrequently enough that this should always correspond to
- // "Next release", and once the next release happens that bin will
- // get an official name.
- if (!g_objectDestruct) {
- RecordZombieFailure(FAILED_MAX);
- return NO;
- }
- } else {
- return NO;
- }
-
- Class rootClass = [NSObject class];
- g_originalDeallocIMP =
- class_getMethodImplementation(rootClass, @selector(dealloc));
- // objc_getClass() so CrZombie doesn't need +class.
- g_zombieClass = objc_getClass("CrZombie");
- g_fatZombieClass = objc_getClass("CrFatZombie");
- g_fatZombieSize = class_getInstanceSize(g_fatZombieClass);
-
- if (!g_objectDestruct || !g_originalDeallocIMP ||
- !g_zombieClass || !g_fatZombieClass)
- return NO;
-
- initialized = YES;
- return YES;
-}
-
-} // namespace
-
-@implementation CrZombie
-
-// The Objective-C runtime needs to be able to call this successfully.
-+ (void)initialize {
-}
-
-// Any method not explicitly defined will end up here, forcing a
-// crash.
-- (id)forwardingTargetForSelector:(SEL)aSelector {
- ZombieObjectCrash(self, aSelector, NULL);
- return nil;
-}
-
-// Override a few methods often used for dynamic dispatch to log the
-// message the caller is attempting to send, rather than the utility
-// method being used to send it.
-- (BOOL)respondsToSelector:(SEL)aSelector {
- ZombieObjectCrash(self, aSelector, _cmd);
- return NO;
-}
-
-- (id)performSelector:(SEL)aSelector {
- ZombieObjectCrash(self, aSelector, _cmd);
- return nil;
-}
-
-- (id)performSelector:(SEL)aSelector withObject:(id)anObject {
- ZombieObjectCrash(self, aSelector, _cmd);
- return nil;
-}
-
-- (id)performSelector:(SEL)aSelector
- withObject:(id)anObject
- withObject:(id)anotherObject {
- ZombieObjectCrash(self, aSelector, _cmd);
- return nil;
-}
-
-- (void)performSelector:(SEL)aSelector
- withObject:(id)anArgument
- afterDelay:(NSTimeInterval)delay {
- ZombieObjectCrash(self, aSelector, _cmd);
-}
-
-@end
-
-@implementation CrFatZombie
-
-// This implementation intentionally left empty.
-
-@end
-
-@implementation NSObject (CrZombie)
-
-- (BOOL)shouldBecomeCrZombie {
- return NO;
-}
-
-@end
-
-namespace ObjcEvilDoers {
-
-BOOL ZombieEnable(BOOL zombieAllObjects,
- size_t zombieCount) {
- // Only allow enable/disable on the main thread, just to keep things
- // simple.
- CHECK([NSThread isMainThread]);
-
- if (!ZombieInit())
- return NO;
-
- g_zombieAllObjects = zombieAllObjects;
-
- // Replace the implementation of -[NSObject dealloc].
- Method m = class_getInstanceMethod([NSObject class], @selector(dealloc));
- if (!m)
- return NO;
-
- const IMP prevDeallocIMP = method_setImplementation(m, (IMP)ZombieDealloc);
- DCHECK(prevDeallocIMP == g_originalDeallocIMP ||
- prevDeallocIMP == (IMP)ZombieDealloc);
-
- // Grab the current set of zombies. This is thread-safe because
- // only the main thread can change these.
- const size_t oldCount = g_zombieCount;
- ZombieRecord* oldZombies = g_zombies;
-
- {
- base::AutoLock pin(lock_);
-
- // Save the old index in case zombies need to be transferred.
- size_t oldIndex = g_zombieIndex;
-
- // Create the new zombie treadmill, disabling zombies in case of
- // failure.
- g_zombieIndex = 0;
- g_zombieCount = zombieCount;
- g_zombies = NULL;
- if (g_zombieCount) {
- g_zombies =
- static_cast<ZombieRecord*>(calloc(g_zombieCount, sizeof(*g_zombies)));
- if (!g_zombies) {
- NOTREACHED();
- g_zombies = oldZombies;
- g_zombieCount = oldCount;
- g_zombieIndex = oldIndex;
- ZombieDisable();
- return NO;
- }
- }
-
- // If the count is changing, allow some of the zombies to continue
- // shambling forward.
- const size_t sharedCount = std::min(oldCount, zombieCount);
- if (sharedCount) {
- // Get index of the first shared zombie.
- oldIndex = (oldIndex + oldCount - sharedCount) % oldCount;
-
- for (; g_zombieIndex < sharedCount; ++ g_zombieIndex) {
- DCHECK_LT(g_zombieIndex, g_zombieCount);
- DCHECK_LT(oldIndex, oldCount);
- std::swap(g_zombies[g_zombieIndex], oldZombies[oldIndex]);
- oldIndex = (oldIndex + 1) % oldCount;
- }
- g_zombieIndex %= g_zombieCount;
- }
- }
-
- // Free the old treadmill and any remaining zombies.
- if (oldZombies) {
- for (size_t i = 0; i < oldCount; ++i) {
- if (oldZombies[i].object)
- object_dispose(oldZombies[i].object);
- }
- free(oldZombies);
- }
-
- return YES;
-}
-
-void ZombieDisable() {
- // Only allow enable/disable on the main thread, just to keep things
- // simple.
- CHECK([NSThread isMainThread]);
-
- // |ZombieInit()| was never called.
- if (!g_originalDeallocIMP)
- return;
-
- // Put back the original implementation of -[NSObject dealloc].
- Method m = class_getInstanceMethod([NSObject class], @selector(dealloc));
- CHECK(m);
- method_setImplementation(m, g_originalDeallocIMP);
-
- // Can safely grab this because it only happens on the main thread.
- const size_t oldCount = g_zombieCount;
- ZombieRecord* oldZombies = g_zombies;
-
- {
- base::AutoLock pin(lock_); // In case any |-dealloc| are in-progress.
- g_zombieCount = 0;
- g_zombies = NULL;
- }
-
- // Free any remaining zombies.
- if (oldZombies) {
- for (size_t i = 0; i < oldCount; ++i) {
- if (oldZombies[i].object)
- object_dispose(oldZombies[i].object);
- }
- free(oldZombies);
- }
-}
-
-} // namespace ObjcEvilDoers