|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 // Undo the damage that exception_defines.h does. |
|
7 #undef try |
|
8 #undef catch |
|
9 |
|
10 #ifndef nsObjCExceptions_h_ |
|
11 #define nsObjCExceptions_h_ |
|
12 |
|
13 #import <Foundation/Foundation.h> |
|
14 |
|
15 #ifdef DEBUG |
|
16 #import <ExceptionHandling/NSExceptionHandler.h> |
|
17 #endif |
|
18 |
|
19 #if defined(MOZ_CRASHREPORTER) && defined(__cplusplus) |
|
20 #include "nsICrashReporter.h" |
|
21 #include "nsCOMPtr.h" |
|
22 #include "nsServiceManagerUtils.h" |
|
23 #endif |
|
24 |
|
25 #include <unistd.h> |
|
26 #include <signal.h> |
|
27 #include "nsError.h" |
|
28 |
|
29 // Undo the damage that exception_defines.h does. |
|
30 #undef try |
|
31 #undef catch |
|
32 |
|
33 /* NOTE: Macros that claim to abort no longer abort, see bug 486574. |
|
34 * If you actually want to log and abort, call "nsObjCExceptionLogAbort" |
|
35 * from an exception handler. At some point we will fix this by replacing |
|
36 * all macros in the tree with appropriately-named macros. |
|
37 */ |
|
38 |
|
39 // See Mozilla bug 163260. |
|
40 // This file can only be included in an Objective-C context. |
|
41 |
|
42 __attribute__((unused)) |
|
43 static void nsObjCExceptionLog(NSException* aException) |
|
44 { |
|
45 NSLog(@"Mozilla has caught an Obj-C exception [%@: %@]", |
|
46 [aException name], [aException reason]); |
|
47 |
|
48 #if defined(MOZ_CRASHREPORTER) && defined(__cplusplus) |
|
49 // Attach exception info to the crash report. |
|
50 nsCOMPtr<nsICrashReporter> crashReporter = |
|
51 do_GetService("@mozilla.org/toolkit/crash-reporter;1"); |
|
52 if (crashReporter) |
|
53 crashReporter->AppendObjCExceptionInfoToAppNotes(static_cast<void*>(aException)); |
|
54 #endif |
|
55 |
|
56 #ifdef DEBUG |
|
57 @try { |
|
58 // Try to get stack information out of the exception. 10.5 returns the stack |
|
59 // info with the callStackReturnAddresses selector. |
|
60 NSArray *stackTrace = nil; |
|
61 if ([aException respondsToSelector:@selector(callStackReturnAddresses)]) { |
|
62 NSArray* addresses = (NSArray*) |
|
63 [aException performSelector:@selector(callStackReturnAddresses)]; |
|
64 if ([addresses count]) |
|
65 stackTrace = addresses; |
|
66 } |
|
67 |
|
68 // 10.4 doesn't respond to callStackReturnAddresses so we'll try to pull the |
|
69 // stack info out of the userInfo. It might not be there, sadly :( |
|
70 if (!stackTrace) |
|
71 stackTrace = [[aException userInfo] objectForKey:NSStackTraceKey]; |
|
72 |
|
73 if (stackTrace) { |
|
74 // The command line should look like this: |
|
75 // /usr/bin/atos -p <pid> -printHeader <stack frame addresses> |
|
76 NSMutableArray *args = |
|
77 [NSMutableArray arrayWithCapacity:[stackTrace count] + 3]; |
|
78 |
|
79 [args addObject:@"-p"]; |
|
80 int pid = [[NSProcessInfo processInfo] processIdentifier]; |
|
81 [args addObject:[NSString stringWithFormat:@"%d", pid]]; |
|
82 |
|
83 [args addObject:@"-printHeader"]; |
|
84 |
|
85 unsigned int stackCount = [stackTrace count]; |
|
86 unsigned int stackIndex = 0; |
|
87 for (; stackIndex < stackCount; stackIndex++) { |
|
88 unsigned long address = |
|
89 [[stackTrace objectAtIndex:stackIndex] unsignedLongValue]; |
|
90 [args addObject:[NSString stringWithFormat:@"0x%lx", address]]; |
|
91 } |
|
92 |
|
93 NSPipe *outPipe = [NSPipe pipe]; |
|
94 |
|
95 NSTask *task = [[NSTask alloc] init]; |
|
96 [task setLaunchPath:@"/usr/bin/atos"]; |
|
97 [task setArguments:args]; |
|
98 [task setStandardOutput:outPipe]; |
|
99 [task setStandardError:outPipe]; |
|
100 |
|
101 NSLog(@"Generating stack trace for Obj-C exception..."); |
|
102 |
|
103 // This will throw an exception if the atos tool cannot be found, and in |
|
104 // that case we'll just hit our @catch block below. |
|
105 [task launch]; |
|
106 |
|
107 [task waitUntilExit]; |
|
108 [task release]; |
|
109 |
|
110 NSData *outData = |
|
111 [[outPipe fileHandleForReading] readDataToEndOfFile]; |
|
112 NSString *outString = |
|
113 [[NSString alloc] initWithData:outData encoding:NSUTF8StringEncoding]; |
|
114 |
|
115 NSLog(@"Stack trace:\n%@", outString); |
|
116 |
|
117 [outString release]; |
|
118 } |
|
119 else { |
|
120 NSLog(@"<No stack information available for Obj-C exception>"); |
|
121 } |
|
122 } |
|
123 @catch (NSException *exn) { |
|
124 NSLog(@"Failed to generate stack trace for Obj-C exception [%@: %@]", |
|
125 [exn name], [exn reason]); |
|
126 } |
|
127 #endif |
|
128 } |
|
129 |
|
130 __attribute__((unused)) |
|
131 static void nsObjCExceptionAbort() |
|
132 { |
|
133 // We need to raise a mach-o signal here, the Mozilla crash reporter on |
|
134 // Mac OS X does not respond to POSIX signals. Raising mach-o signals directly |
|
135 // is tricky so we do it by just derefing a null pointer. |
|
136 int* foo = nullptr; |
|
137 *foo = 1; |
|
138 } |
|
139 |
|
140 __attribute__((unused)) |
|
141 static void nsObjCExceptionLogAbort(NSException *e) |
|
142 { |
|
143 nsObjCExceptionLog(e); |
|
144 nsObjCExceptionAbort(); |
|
145 } |
|
146 |
|
147 #define NS_OBJC_TRY(_e, _fail) \ |
|
148 @try { _e; } \ |
|
149 @catch(NSException *_exn) { \ |
|
150 nsObjCExceptionLog(_exn); \ |
|
151 _fail; \ |
|
152 } |
|
153 |
|
154 #define NS_OBJC_TRY_EXPR(_e, _fail) \ |
|
155 ({ \ |
|
156 typeof(_e) _tmp; \ |
|
157 @try { _tmp = (_e); } \ |
|
158 @catch(NSException *_exn) { \ |
|
159 nsObjCExceptionLog(_exn); \ |
|
160 _fail; \ |
|
161 } \ |
|
162 _tmp; \ |
|
163 }) |
|
164 |
|
165 #define NS_OBJC_TRY_EXPR_NULL(_e) \ |
|
166 NS_OBJC_TRY_EXPR(_e, 0) |
|
167 |
|
168 #define NS_OBJC_TRY_IGNORE(_e) \ |
|
169 NS_OBJC_TRY(_e, ) |
|
170 |
|
171 // To reduce code size the abort versions do not reuse above macros. This allows |
|
172 // catch blocks to only contain one call. |
|
173 |
|
174 #define NS_OBJC_TRY_ABORT(_e) \ |
|
175 @try { _e; } \ |
|
176 @catch(NSException *_exn) { \ |
|
177 nsObjCExceptionLog(_exn); \ |
|
178 } |
|
179 |
|
180 #define NS_OBJC_TRY_EXPR_ABORT(_e) \ |
|
181 ({ \ |
|
182 typeof(_e) _tmp; \ |
|
183 @try { _tmp = (_e); } \ |
|
184 @catch(NSException *_exn) { \ |
|
185 nsObjCExceptionLog(_exn); \ |
|
186 } \ |
|
187 _tmp; \ |
|
188 }) |
|
189 |
|
190 // For wrapping blocks of Obj-C calls. Does not actually terminate. |
|
191 #define NS_OBJC_BEGIN_TRY_ABORT_BLOCK @try { |
|
192 #define NS_OBJC_END_TRY_ABORT_BLOCK } @catch(NSException *_exn) { \ |
|
193 nsObjCExceptionLog(_exn); \ |
|
194 } |
|
195 |
|
196 // Same as above ABORT_BLOCK but returns a value after the try/catch block to |
|
197 // suppress compiler warnings. This allows us to avoid having to refactor code |
|
198 // to get scoping right when wrapping an entire method. |
|
199 |
|
200 #define NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL @try { |
|
201 #define NS_OBJC_END_TRY_ABORT_BLOCK_NIL } @catch(NSException *_exn) { \ |
|
202 nsObjCExceptionLog(_exn); \ |
|
203 } \ |
|
204 return nil; |
|
205 |
|
206 #define NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSNULL @try { |
|
207 #define NS_OBJC_END_TRY_ABORT_BLOCK_NSNULL } @catch(NSException *_exn) { \ |
|
208 nsObjCExceptionLog(_exn); \ |
|
209 } \ |
|
210 return nullptr; |
|
211 |
|
212 #define NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT @try { |
|
213 #define NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT } @catch(NSException *_exn) { \ |
|
214 nsObjCExceptionLog(_exn); \ |
|
215 } \ |
|
216 return NS_ERROR_FAILURE; |
|
217 |
|
218 #define NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN @try { |
|
219 #define NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(_rv) } @catch(NSException *_exn) { \ |
|
220 nsObjCExceptionLog(_exn);\ |
|
221 } \ |
|
222 return _rv; |
|
223 |
|
224 #define NS_OBJC_BEGIN_TRY_LOGONLY_BLOCK @try { |
|
225 #define NS_OBJC_END_TRY_LOGONLY_BLOCK } @catch(NSException *_exn) { \ |
|
226 nsObjCExceptionLog(_exn); \ |
|
227 } |
|
228 |
|
229 #define NS_OBJC_BEGIN_TRY_LOGONLY_BLOCK_RETURN @try { |
|
230 #define NS_OBJC_END_TRY_LOGONLY_BLOCK_RETURN(_rv) } @catch(NSException *_exn) { \ |
|
231 nsObjCExceptionLog(_exn); \ |
|
232 } \ |
|
233 return _rv; |
|
234 |
|
235 #endif // nsObjCExceptions_h_ |