Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(315)

Side by Side Diff: chrome/common/mac/objc_zombie.mm

Issue 10837158: mac: Delete more 10.5-only code (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: avi Created 8 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 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 <AvailabilityMacros.h>
8
7 #include <dlfcn.h> 9 #include <dlfcn.h>
8 #include <execinfo.h> 10 #include <execinfo.h>
9 #include <mach-o/dyld.h> 11 #include <mach-o/dyld.h>
10 #include <mach-o/nlist.h> 12 #include <mach-o/nlist.h>
11 13
12 #import <objc/objc-class.h> 14 #import <objc/objc-class.h>
15 #import <objc/runtime.h>
13 16
14 #include <algorithm> 17 #include <algorithm>
15 18
16 #include "base/debug/stack_trace.h" 19 #include "base/debug/stack_trace.h"
17 #include "base/lazy_instance.h" 20 #include "base/lazy_instance.h"
18 #include "base/logging.h" 21 #include "base/logging.h"
19 #include "base/mac/crash_logging.h" 22 #include "base/mac/crash_logging.h"
20 #include "base/mac/mac_util.h" 23 #include "base/mac/mac_util.h"
21 #include "base/metrics/histogram.h" 24 #include "base/metrics/histogram.h"
22 #include "base/synchronization/lock.h" 25 #include "base/synchronization/lock.h"
23 #import "chrome/common/mac/objc_method_swizzle.h" 26 #import "chrome/common/mac/objc_method_swizzle.h"
24 27
28 #if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_6
29 // Apparently objc/runtime.h doesn't define this with the 10.6 SDK yet.
30 // The docs say it exists since 10.6 however.
31 OBJC_EXPORT void *objc_destructInstance(id obj);
32 #endif
33
25 // Deallocated objects are re-classed as |CrZombie|. No superclass 34 // Deallocated objects are re-classed as |CrZombie|. No superclass
26 // because then the class would have to override many/most of the 35 // because then the class would have to override many/most of the
27 // inherited methods (|NSObject| is like a category magnet!). 36 // inherited methods (|NSObject| is like a category magnet!).
28 // Without the __attribute__, clang's -Wobjc-root-class warns on the missing 37 // Without the __attribute__, clang's -Wobjc-root-class warns on the missing
29 // superclass. 38 // superclass.
30 __attribute__((objc_root_class)) 39 __attribute__((objc_root_class))
31 @interface CrZombie { 40 @interface CrZombie {
32 Class isa; 41 Class isa;
33 } 42 }
34 @end 43 @end
(...skipping 10 matching lines...) Expand all
45 54
46 // The depth of backtrace to store with zombies. This directly influences 55 // The depth of backtrace to store with zombies. This directly influences
47 // the amount of memory required to track zombies, so should be kept as 56 // the amount of memory required to track zombies, so should be kept as
48 // small as is useful. Unfortunately, too small and it won't poke through 57 // small as is useful. Unfortunately, too small and it won't poke through
49 // deep autorelease and event loop stacks. 58 // deep autorelease and event loop stacks.
50 // NOTE(shess): Breakpad currently restricts values to 255 bytes. The 59 // NOTE(shess): Breakpad currently restricts values to 255 bytes. The
51 // trace is hex-encoded with "0x" prefix and " " separators, meaning 60 // trace is hex-encoded with "0x" prefix and " " separators, meaning
52 // the maximum number of 32-bit items which can be encoded is 23. 61 // the maximum number of 32-bit items which can be encoded is 23.
53 const size_t kBacktraceDepth = 20; 62 const size_t kBacktraceDepth = 20;
54 63
55 // Function which destroys the contents of an object without freeing
56 // the object. On 10.5 this is |object_cxxDestruct()|, which
57 // traverses the class hierarchy to run the C++ destructors. On 10.6
58 // this is |objc_destructInstance()| which calls
59 // |object_cxxDestruct()| and removes associative references.
60 // |objc_destructInstance()| returns |void*| but pretending it has no
61 // return value makes the code simpler.
62 typedef void DestructFn(id obj);
63 DestructFn* g_objectDestruct = NULL;
64
65 // The original implementation for |-[NSObject dealloc]|. 64 // The original implementation for |-[NSObject dealloc]|.
66 IMP g_originalDeallocIMP = NULL; 65 IMP g_originalDeallocIMP = NULL;
67 66
68 // Classes which freed objects become. |g_fatZombieSize| is the 67 // Classes which freed objects become. |g_fatZombieSize| is the
69 // minimum object size which can be made into a fat zombie (which can 68 // minimum object size which can be made into a fat zombie (which can
70 // remember which class it was before free, even after falling off the 69 // remember which class it was before free, even after falling off the
71 // treadmill). 70 // treadmill).
72 Class g_zombieClass = Nil; // cached [CrZombie class] 71 Class g_zombieClass = Nil; // cached [CrZombie class]
73 Class g_fatZombieClass = Nil; // cached [CrFatZombie class] 72 Class g_fatZombieClass = Nil; // cached [CrFatZombie class]
74 size_t g_fatZombieSize = 0; 73 size_t g_fatZombieSize = 0;
(...skipping 23 matching lines...) Expand all
98 const void* addr = reinterpret_cast<void*>(&object_getClass); 97 const void* addr = reinterpret_cast<void*>(&object_getClass);
99 Dl_info info; 98 Dl_info info;
100 99
101 // |dladdr()| doesn't document how long |info| will stay valid... 100 // |dladdr()| doesn't document how long |info| will stay valid...
102 if (dladdr(addr, &info)) 101 if (dladdr(addr, &info))
103 return info.dli_fname; 102 return info.dli_fname;
104 103
105 return NULL; 104 return NULL;
106 } 105 }
107 106
108 // Lookup |objc_destructInstance()| dynamically because it isn't
109 // available on 10.5, but we link with the 10.5 SDK.
110 DestructFn* LookupObjectDestruct_10_6() {
111 const char* objc_runtime_path = LookupObjcRuntimePath();
112 if (!objc_runtime_path)
113 return NULL;
114
115 void* handle = dlopen(objc_runtime_path, RTLD_LAZY | RTLD_LOCAL);
116 if (!handle)
117 return NULL;
118
119 void* fn = dlsym(handle, "objc_destructInstance");
120
121 // |fn| would normally be expected to become invalid after this
122 // |dlclose()|, but since the |dlopen()| was on a library
123 // containing an already-mapped symbol, it will remain valid.
124 dlclose(handle);
125 return reinterpret_cast<DestructFn*>(fn);
126 }
127
128 // Under 10.5 |object_cxxDestruct()| is used but unfortunately it is
129 // |__private_extern__| in the runtime, meaning |dlsym()| cannot reach it.
130 DestructFn* LookupObjectDestruct_10_5() {
131 // |nlist()| is only present for 32-bit.
132 #if ARCH_CPU_32_BITS
133 const char* objc_runtime_path = LookupObjcRuntimePath();
134 if (!objc_runtime_path)
135 return NULL;
136
137 struct nlist nl[3];
138 bzero(&nl, sizeof(nl));
139
140 nl[0].n_un.n_name = const_cast<char*>("_object_cxxDestruct");
141
142 // My ability to calculate the base for offsets is apparently poor.
143 // Use |class_addIvar| as a known reference point.
144 nl[1].n_un.n_name = const_cast<char*>("_class_addIvar");
145
146 if (nlist(objc_runtime_path, nl) < 0 ||
147 nl[0].n_type == N_UNDF || nl[1].n_type == N_UNDF)
148 return NULL;
149
150 // Back out offset to |class_addIvar()| to get the baseline, then
151 // add back offset to |object_cxxDestruct()|.
152 uintptr_t reference_addr = reinterpret_cast<uintptr_t>(&class_addIvar);
153 reference_addr -= nl[1].n_value;
154 reference_addr += nl[0].n_value;
155
156 return reinterpret_cast<DestructFn*>(reference_addr);
157 #endif
158
159 return NULL;
160 }
161
162 // Replacement |-dealloc| which turns objects into zombies and places 107 // Replacement |-dealloc| which turns objects into zombies and places
163 // them into |g_zombies| to be freed later. 108 // them into |g_zombies| to be freed later.
164 void ZombieDealloc(id self, SEL _cmd) { 109 void ZombieDealloc(id self, SEL _cmd) {
165 // This code should only be called when it is implementing |-dealloc|. 110 // This code should only be called when it is implementing |-dealloc|.
166 DCHECK_EQ(_cmd, @selector(dealloc)); 111 DCHECK_EQ(_cmd, @selector(dealloc));
167 112
168 // Use the original |-dealloc| if the object doesn't wish to be 113 // Use the original |-dealloc| if the object doesn't wish to be
169 // zombied. 114 // zombied.
170 if (!g_zombieAllObjects && ![self shouldBecomeCrZombie]) { 115 if (!g_zombieAllObjects && ![self shouldBecomeCrZombie]) {
171 g_originalDeallocIMP(self, _cmd); 116 g_originalDeallocIMP(self, _cmd);
172 return; 117 return;
173 } 118 }
174 119
175 // Use the original |-dealloc| if |g_objectDestruct| was never
176 // initialized, because otherwise C++ destructors won't be called.
177 // This case should be impossible, but doing it wrong would cause
178 // terrible problems.
179 DCHECK(g_objectDestruct);
180 if (!g_objectDestruct) {
181 g_originalDeallocIMP(self, _cmd);
182 return;
183 }
184
185 Class wasa = object_getClass(self); 120 Class wasa = object_getClass(self);
186 const size_t size = class_getInstanceSize(wasa); 121 const size_t size = class_getInstanceSize(wasa);
187 122
188 // Destroy the instance by calling C++ destructors and clearing it 123 // Destroy the instance by calling C++ destructors and clearing it
189 // to something unlikely to work well if someone references it. 124 // to something unlikely to work well if someone references it.
190 // NOTE(shess): |object_dispose()| will call this again when the 125 // NOTE(shess): |object_dispose()| will call this again when the
191 // zombie falls off the treadmill! But by then |isa| will be a 126 // zombie falls off the treadmill! But by then |isa| will be a
192 // class without C++ destructors or associative references, so it 127 // class without C++ destructors or associative references, so it
193 // won't hurt anything. 128 // won't hurt anything.
194 (*g_objectDestruct)(self); 129 objc_destructInstance(self);
195 memset(self, '!', size); 130 memset(self, '!', size);
196 131
197 // If the instance is big enough, make it into a fat zombie and have 132 // If the instance is big enough, make it into a fat zombie and have
198 // it remember the old |isa|. Otherwise make it a regular zombie. 133 // it remember the old |isa|. Otherwise make it a regular zombie.
199 // Setting |isa| rather than using |object_setClass()| because that 134 // Setting |isa| rather than using |object_setClass()| because that
200 // function is implemented with a memory barrier. The runtime's 135 // function is implemented with a memory barrier. The runtime's
201 // |_internal_object_dispose()| (in objc-class.m) does this, so it 136 // |_internal_object_dispose()| (in objc-class.m) does this, so it
202 // should be safe (messaging free'd objects shouldn't be expected to 137 // should be safe (messaging free'd objects shouldn't be expected to
203 // be thread-safe in the first place). 138 // be thread-safe in the first place).
204 #pragma clang diagnostic push // clang warns about direct access to isa. 139 #pragma clang diagnostic push // clang warns about direct access to isa.
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after
306 } 241 }
307 DLOG(FATAL) << [aString UTF8String]; 242 DLOG(FATAL) << [aString UTF8String];
308 243
309 // This is how about:crash is implemented. Using instead of 244 // This is how about:crash is implemented. Using instead of
310 // |base::debug::BreakDebugger()| or |LOG(FATAL)| to make the top of 245 // |base::debug::BreakDebugger()| or |LOG(FATAL)| to make the top of
311 // stack more immediately obvious in crash dumps. 246 // stack more immediately obvious in crash dumps.
312 int* zero = NULL; 247 int* zero = NULL;
313 *zero = 0; 248 *zero = 0;
314 } 249 }
315 250
316 // For monitoring failures in |ZombieInit()|.
317 enum ZombieFailure {
318 FAILED_10_5,
319 FAILED_10_6,
320
321 // Add new versions before here.
322 FAILED_MAX,
323 };
324
325 void RecordZombieFailure(ZombieFailure failure) {
326 UMA_HISTOGRAM_ENUMERATION("OSX.ZombieInitFailure", failure, FAILED_MAX);
327 }
328
329 // Initialize our globals, returning YES on success. 251 // Initialize our globals, returning YES on success.
330 BOOL ZombieInit() { 252 BOOL ZombieInit() {
331 static BOOL initialized = NO; 253 static BOOL initialized = NO;
332 if (initialized) 254 if (initialized)
333 return YES; 255 return YES;
334 256
335 // Whitelist releases that are compatible with objc zombies.
336 if (base::mac::IsOSLeopard()) {
337 g_objectDestruct = LookupObjectDestruct_10_5();
338 if (!g_objectDestruct) {
339 RecordZombieFailure(FAILED_10_5);
340 return NO;
341 }
342 } else if (base::mac::IsOSSnowLeopard()) {
343 g_objectDestruct = LookupObjectDestruct_10_6();
344 if (!g_objectDestruct) {
345 RecordZombieFailure(FAILED_10_6);
346 return NO;
347 }
348 } else if (base::mac::IsOSLionOrLater()) {
349 // Assume the future looks like the present.
350 g_objectDestruct = LookupObjectDestruct_10_6();
351
352 // Put all future failures into the MAX bin. New OS releases come
353 // out infrequently enough that this should always correspond to
354 // "Next release", and once the next release happens that bin will
355 // get an official name.
356 if (!g_objectDestruct) {
357 RecordZombieFailure(FAILED_MAX);
358 return NO;
359 }
360 } else {
361 return NO;
362 }
363
364 Class rootClass = [NSObject class]; 257 Class rootClass = [NSObject class];
365 g_originalDeallocIMP = 258 g_originalDeallocIMP =
366 class_getMethodImplementation(rootClass, @selector(dealloc)); 259 class_getMethodImplementation(rootClass, @selector(dealloc));
367 // objc_getClass() so CrZombie doesn't need +class. 260 // objc_getClass() so CrZombie doesn't need +class.
368 g_zombieClass = objc_getClass("CrZombie"); 261 g_zombieClass = objc_getClass("CrZombie");
369 g_fatZombieClass = objc_getClass("CrFatZombie"); 262 g_fatZombieClass = objc_getClass("CrFatZombie");
370 g_fatZombieSize = class_getInstanceSize(g_fatZombieClass); 263 g_fatZombieSize = class_getInstanceSize(g_fatZombieClass);
371 264
372 if (!g_objectDestruct || !g_originalDeallocIMP || 265 if (!g_originalDeallocIMP || !g_zombieClass || !g_fatZombieClass)
373 !g_zombieClass || !g_fatZombieClass)
374 return NO; 266 return NO;
375 267
376 initialized = YES; 268 initialized = YES;
377 return YES; 269 return YES;
378 } 270 }
379 271
380 } // namespace 272 } // namespace
381 273
382 @implementation CrZombie 274 @implementation CrZombie
383 275
(...skipping 163 matching lines...) Expand 10 before | Expand all | Expand 10 after
547 if (oldZombies) { 439 if (oldZombies) {
548 for (size_t i = 0; i < oldCount; ++i) { 440 for (size_t i = 0; i < oldCount; ++i) {
549 if (oldZombies[i].object) 441 if (oldZombies[i].object)
550 object_dispose(oldZombies[i].object); 442 object_dispose(oldZombies[i].object);
551 } 443 }
552 free(oldZombies); 444 free(oldZombies);
553 } 445 }
554 } 446 }
555 447
556 } // namespace ObjcEvilDoers 448 } // namespace ObjcEvilDoers
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698