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

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: . 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 <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 #import <objc/runtime.h>
13 14
14 #include <algorithm> 15 #include <algorithm>
15 16
16 #include "base/debug/stack_trace.h" 17 #include "base/debug/stack_trace.h"
17 #include "base/lazy_instance.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"
(...skipping 22 matching lines...) Expand all
45 46
46 // The depth of backtrace to store with zombies. This directly influences 47 // 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 48 // 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 49 // small as is useful. Unfortunately, too small and it won't poke through
49 // deep autorelease and event loop stacks. 50 // deep autorelease and event loop stacks.
50 // NOTE(shess): Breakpad currently restricts values to 255 bytes. The 51 // NOTE(shess): Breakpad currently restricts values to 255 bytes. The
51 // trace is hex-encoded with "0x" prefix and " " separators, meaning 52 // trace is hex-encoded with "0x" prefix and " " separators, meaning
52 // the maximum number of 32-bit items which can be encoded is 23. 53 // the maximum number of 32-bit items which can be encoded is 23.
53 const size_t kBacktraceDepth = 20; 54 const size_t kBacktraceDepth = 20;
54 55
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]|. 56 // The original implementation for |-[NSObject dealloc]|.
66 IMP g_originalDeallocIMP = NULL; 57 IMP g_originalDeallocIMP = NULL;
67 58
68 // Classes which freed objects become. |g_fatZombieSize| is the 59 // Classes which freed objects become. |g_fatZombieSize| is the
69 // minimum object size which can be made into a fat zombie (which can 60 // 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 61 // remember which class it was before free, even after falling off the
71 // treadmill). 62 // treadmill).
72 Class g_zombieClass = Nil; // cached [CrZombie class] 63 Class g_zombieClass = Nil; // cached [CrZombie class]
73 Class g_fatZombieClass = Nil; // cached [CrFatZombie class] 64 Class g_fatZombieClass = Nil; // cached [CrFatZombie class]
74 size_t g_fatZombieSize = 0; 65 size_t g_fatZombieSize = 0;
(...skipping 23 matching lines...) Expand all
98 const void* addr = reinterpret_cast<void*>(&object_getClass); 89 const void* addr = reinterpret_cast<void*>(&object_getClass);
99 Dl_info info; 90 Dl_info info;
100 91
101 // |dladdr()| doesn't document how long |info| will stay valid... 92 // |dladdr()| doesn't document how long |info| will stay valid...
102 if (dladdr(addr, &info)) 93 if (dladdr(addr, &info))
103 return info.dli_fname; 94 return info.dli_fname;
104 95
105 return NULL; 96 return NULL;
106 } 97 }
107 98
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 99 // Replacement |-dealloc| which turns objects into zombies and places
163 // them into |g_zombies| to be freed later. 100 // them into |g_zombies| to be freed later.
164 void ZombieDealloc(id self, SEL _cmd) { 101 void ZombieDealloc(id self, SEL _cmd) {
165 // This code should only be called when it is implementing |-dealloc|. 102 // This code should only be called when it is implementing |-dealloc|.
166 DCHECK_EQ(_cmd, @selector(dealloc)); 103 DCHECK_EQ(_cmd, @selector(dealloc));
167 104
168 // Use the original |-dealloc| if the object doesn't wish to be 105 // Use the original |-dealloc| if the object doesn't wish to be
169 // zombied. 106 // zombied.
170 if (!g_zombieAllObjects && ![self shouldBecomeCrZombie]) { 107 if (!g_zombieAllObjects && ![self shouldBecomeCrZombie]) {
171 g_originalDeallocIMP(self, _cmd); 108 g_originalDeallocIMP(self, _cmd);
172 return; 109 return;
173 } 110 }
174 111
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); 112 Class wasa = object_getClass(self);
186 const size_t size = class_getInstanceSize(wasa); 113 const size_t size = class_getInstanceSize(wasa);
187 114
188 // Destroy the instance by calling C++ destructors and clearing it 115 // Destroy the instance by calling C++ destructors and clearing it
189 // to something unlikely to work well if someone references it. 116 // to something unlikely to work well if someone references it.
190 // NOTE(shess): |object_dispose()| will call this again when the 117 // NOTE(shess): |object_dispose()| will call this again when the
191 // zombie falls off the treadmill! But by then |isa| will be a 118 // zombie falls off the treadmill! But by then |isa| will be a
192 // class without C++ destructors or associative references, so it 119 // class without C++ destructors or associative references, so it
193 // won't hurt anything. 120 // won't hurt anything.
194 (*g_objectDestruct)(self); 121 objc_destructInstance(self);
Scott Hess - ex-Googler 2012/08/08 04:26:14 Sweet!
195 memset(self, '!', size); 122 memset(self, '!', size);
196 123
197 // If the instance is big enough, make it into a fat zombie and have 124 // 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. 125 // it remember the old |isa|. Otherwise make it a regular zombie.
199 // Setting |isa| rather than using |object_setClass()| because that 126 // Setting |isa| rather than using |object_setClass()| because that
200 // function is implemented with a memory barrier. The runtime's 127 // function is implemented with a memory barrier. The runtime's
201 // |_internal_object_dispose()| (in objc-class.m) does this, so it 128 // |_internal_object_dispose()| (in objc-class.m) does this, so it
202 // should be safe (messaging free'd objects shouldn't be expected to 129 // should be safe (messaging free'd objects shouldn't be expected to
203 // be thread-safe in the first place). 130 // be thread-safe in the first place).
204 #pragma clang diagnostic push // clang warns about direct access to isa. 131 #pragma clang diagnostic push // clang warns about direct access to isa.
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after
306 } 233 }
307 DLOG(FATAL) << [aString UTF8String]; 234 DLOG(FATAL) << [aString UTF8String];
308 235
309 // This is how about:crash is implemented. Using instead of 236 // This is how about:crash is implemented. Using instead of
310 // |base::debug::BreakDebugger()| or |LOG(FATAL)| to make the top of 237 // |base::debug::BreakDebugger()| or |LOG(FATAL)| to make the top of
311 // stack more immediately obvious in crash dumps. 238 // stack more immediately obvious in crash dumps.
312 int* zero = NULL; 239 int* zero = NULL;
313 *zero = 0; 240 *zero = 0;
314 } 241 }
315 242
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. 243 // Initialize our globals, returning YES on success.
330 BOOL ZombieInit() { 244 BOOL ZombieInit() {
331 static BOOL initialized = NO; 245 static BOOL initialized = NO;
332 if (initialized) 246 if (initialized)
333 return YES; 247 return YES;
334 248
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]; 249 Class rootClass = [NSObject class];
365 g_originalDeallocIMP = 250 g_originalDeallocIMP =
366 class_getMethodImplementation(rootClass, @selector(dealloc)); 251 class_getMethodImplementation(rootClass, @selector(dealloc));
367 // objc_getClass() so CrZombie doesn't need +class. 252 // objc_getClass() so CrZombie doesn't need +class.
368 g_zombieClass = objc_getClass("CrZombie"); 253 g_zombieClass = objc_getClass("CrZombie");
369 g_fatZombieClass = objc_getClass("CrFatZombie"); 254 g_fatZombieClass = objc_getClass("CrFatZombie");
370 g_fatZombieSize = class_getInstanceSize(g_fatZombieClass); 255 g_fatZombieSize = class_getInstanceSize(g_fatZombieClass);
371 256
372 if (!g_objectDestruct || !g_originalDeallocIMP || 257 if (!g_originalDeallocIMP || !g_zombieClass || !g_fatZombieClass)
373 !g_zombieClass || !g_fatZombieClass)
374 return NO; 258 return NO;
375 259
376 initialized = YES; 260 initialized = YES;
377 return YES; 261 return YES;
378 } 262 }
379 263
380 } // namespace 264 } // namespace
381 265
382 @implementation CrZombie 266 @implementation CrZombie
383 267
(...skipping 163 matching lines...) Expand 10 before | Expand all | Expand 10 after
547 if (oldZombies) { 431 if (oldZombies) {
548 for (size_t i = 0; i < oldCount; ++i) { 432 for (size_t i = 0; i < oldCount; ++i) {
549 if (oldZombies[i].object) 433 if (oldZombies[i].object)
550 object_dispose(oldZombies[i].object); 434 object_dispose(oldZombies[i].object);
551 } 435 }
552 free(oldZombies); 436 free(oldZombies);
553 } 437 }
554 } 438 }
555 439
556 } // namespace ObjcEvilDoers 440 } // namespace ObjcEvilDoers
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698