1 // Copyright (c) 2006, Google Inc.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 // * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above
11 // copyright notice, this list of conditions and the following disclaimer
12 // in the documentation and/or other materials provided with the
13 // distribution.
14 // * Neither the name of Google Inc. nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30 #include <map>
31 #include <pthread.h>
32
33 #include "client/mac/handler/exception_handler.h"
34 #include "client/mac/handler/minidump_generator.h"
35 #include "common/mac/macho_utilities.h"
36 #include "common/mac/scoped_task_suspend-inl.h"
37
38 #ifndef USE_PROTECTED_ALLOCATIONS
39 #define USE_PROTECTED_ALLOCATIONS 0
40 #endif
41
42 // If USE_PROTECTED_ALLOCATIONS is activated then the
43 // gBreakpadAllocator needs to be setup in other code
44 // ahead of time. Please see ProtectedMemoryAllocator.h
45 // for more details.
46 #if USE_PROTECTED_ALLOCATIONS
47 #include "protected_memory_allocator.h"
48 extern ProtectedMemoryAllocator *gBreakpadAllocator;
49 #endif
50
51
52 namespace google_breakpad {
53
54 using std::map;
55
56 // These structures and techniques are illustrated in
57 // Mac OS X Internals, Amit Singh, ch 9.7
58 struct ExceptionMessage {
59 mach_msg_header_t header;
60 mach_msg_body_t body;
61 mach_msg_port_descriptor_t thread;
62 mach_msg_port_descriptor_t task;
63 NDR_record_t ndr;
64 exception_type_t exception;
65 mach_msg_type_number_t code_count;
66 integer_t code[EXCEPTION_CODE_MAX];
67 char padding[512];
68 };
69
70 struct ExceptionParameters {
ExceptionParametersgoogle_breakpad::ExceptionParameters71 ExceptionParameters() : count(0) {}
72 mach_msg_type_number_t count;
73 exception_mask_t masks[EXC_TYPES_COUNT];
74 mach_port_t ports[EXC_TYPES_COUNT];
75 exception_behavior_t behaviors[EXC_TYPES_COUNT];
76 thread_state_flavor_t flavors[EXC_TYPES_COUNT];
77 };
78
79 struct ExceptionReplyMessage {
80 mach_msg_header_t header;
81 NDR_record_t ndr;
82 kern_return_t return_code;
83 };
84
85 // Only catch these three exceptions. The other ones are nebulously defined
86 // and may result in treating a non-fatal exception as fatal.
87 exception_mask_t s_exception_mask = EXC_MASK_BAD_ACCESS |
88 EXC_MASK_BAD_INSTRUCTION | EXC_MASK_ARITHMETIC | EXC_MASK_BREAKPOINT;
89
90 extern "C"
91 {
92 // Forward declarations for functions that need "C" style compilation
93 boolean_t exc_server(mach_msg_header_t *request,
94 mach_msg_header_t *reply);
95
96 // This symbol must be visible to dlsym() - see
97 // http://code.google.com/p/google-breakpad/issues/detail?id=345 for details.
98 kern_return_t catch_exception_raise(mach_port_t target_port,
99 mach_port_t failed_thread,
100 mach_port_t task,
101 exception_type_t exception,
102 exception_data_t code,
103 mach_msg_type_number_t code_count)
104 __attribute__((visibility("default")));
105
106 kern_return_t ForwardException(mach_port_t task,
107 mach_port_t failed_thread,
108 exception_type_t exception,
109 exception_data_t code,
110 mach_msg_type_number_t code_count);
111
112 kern_return_t exception_raise(mach_port_t target_port,
113 mach_port_t failed_thread,
114 mach_port_t task,
115 exception_type_t exception,
116 exception_data_t exception_code,
117 mach_msg_type_number_t exception_code_count);
118
119 kern_return_t
120 exception_raise_state(mach_port_t target_port,
121 mach_port_t failed_thread,
122 mach_port_t task,
123 exception_type_t exception,
124 exception_data_t exception_code,
125 mach_msg_type_number_t code_count,
126 thread_state_flavor_t *target_flavor,
127 thread_state_t in_thread_state,
128 mach_msg_type_number_t in_thread_state_count,
129 thread_state_t out_thread_state,
130 mach_msg_type_number_t *out_thread_state_count);
131
132 kern_return_t
133 exception_raise_state_identity(mach_port_t target_port,
134 mach_port_t failed_thread,
135 mach_port_t task,
136 exception_type_t exception,
137 exception_data_t exception_code,
138 mach_msg_type_number_t exception_code_count,
139 thread_state_flavor_t *target_flavor,
140 thread_state_t in_thread_state,
141 mach_msg_type_number_t in_thread_state_count,
142 thread_state_t out_thread_state,
143 mach_msg_type_number_t *out_thread_state_count);
144
145 kern_return_t breakpad_exception_raise_state(mach_port_t exception_port,
146 exception_type_t exception,
147 const exception_data_t code,
148 mach_msg_type_number_t codeCnt,
149 int *flavor,
150 const thread_state_t old_state,
151 mach_msg_type_number_t old_stateCnt,
152 thread_state_t new_state,
153 mach_msg_type_number_t *new_stateCnt
154 );
155
156 kern_return_t breakpad_exception_raise_state_identity(mach_port_t exception_port,
157 mach_port_t thread,
158 mach_port_t task,
159 exception_type_t exception,
160 exception_data_t code,
161 mach_msg_type_number_t codeCnt,
162 int *flavor,
163 thread_state_t old_state,
164 mach_msg_type_number_t old_stateCnt,
165 thread_state_t new_state,
166 mach_msg_type_number_t *new_stateCnt
167 );
168
169 kern_return_t breakpad_exception_raise(mach_port_t port, mach_port_t failed_thread,
170 mach_port_t task,
171 exception_type_t exception,
172 exception_data_t code,
173 mach_msg_type_number_t code_count);
174 }
175
176
177
breakpad_exception_raise_state(mach_port_t exception_port,exception_type_t exception,const exception_data_t code,mach_msg_type_number_t codeCnt,int * flavor,const thread_state_t old_state,mach_msg_type_number_t old_stateCnt,thread_state_t new_state,mach_msg_type_number_t * new_stateCnt)178 kern_return_t breakpad_exception_raise_state(mach_port_t exception_port,
179 exception_type_t exception,
180 const exception_data_t code,
181 mach_msg_type_number_t codeCnt,
182 int *flavor,
183 const thread_state_t old_state,
184 mach_msg_type_number_t old_stateCnt,
185 thread_state_t new_state,
186 mach_msg_type_number_t *new_stateCnt
187 )
188 {
189 return KERN_SUCCESS;
190 }
191
breakpad_exception_raise_state_identity(mach_port_t exception_port,mach_port_t thread,mach_port_t task,exception_type_t exception,exception_data_t code,mach_msg_type_number_t codeCnt,int * flavor,thread_state_t old_state,mach_msg_type_number_t old_stateCnt,thread_state_t new_state,mach_msg_type_number_t * new_stateCnt)192 kern_return_t breakpad_exception_raise_state_identity(mach_port_t exception_port,
193 mach_port_t thread,
194 mach_port_t task,
195 exception_type_t exception,
196 exception_data_t code,
197 mach_msg_type_number_t codeCnt,
198 int *flavor,
199 thread_state_t old_state,
200 mach_msg_type_number_t old_stateCnt,
201 thread_state_t new_state,
202 mach_msg_type_number_t *new_stateCnt
203 )
204 {
205 return KERN_SUCCESS;
206 }
207
breakpad_exception_raise(mach_port_t port,mach_port_t failed_thread,mach_port_t task,exception_type_t exception,exception_data_t code,mach_msg_type_number_t code_count)208 kern_return_t breakpad_exception_raise(mach_port_t port, mach_port_t failed_thread,
209 mach_port_t task,
210 exception_type_t exception,
211 exception_data_t code,
212 mach_msg_type_number_t code_count) {
213
214 if (task != mach_task_self()) {
215 return KERN_FAILURE;
216 }
217 return ForwardException(task, failed_thread, exception, code, code_count);
218 }
219
220
ExceptionHandler(const string & dump_path,FilterCallback filter,MinidumpCallback callback,void * callback_context,bool install_handler,const char * port_name)221 ExceptionHandler::ExceptionHandler(const string &dump_path,
222 FilterCallback filter,
223 MinidumpCallback callback,
224 void *callback_context,
225 bool install_handler,
226 const char *port_name)
227 : dump_path_(),
228 filter_(filter),
229 callback_(callback),
230 callback_context_(callback_context),
231 directCallback_(NULL),
232 handler_thread_(NULL),
233 handler_port_(MACH_PORT_NULL),
234 previous_(NULL),
235 installed_exception_handler_(false),
236 is_in_teardown_(false),
237 last_minidump_write_result_(false),
238 use_minidump_write_mutex_(false) {
239 // This will update to the ID and C-string pointers
240 set_dump_path(dump_path);
241 MinidumpGenerator::GatherSystemInformation();
242 if (port_name)
243 crash_generation_client_.reset(new CrashGenerationClient(port_name));
244 Setup(install_handler);
245 }
246
247 // special constructor if we want to bypass minidump writing and
248 // simply get a callback with the exception information
ExceptionHandler(DirectCallback callback,void * callback_context,bool install_handler)249 ExceptionHandler::ExceptionHandler(DirectCallback callback,
250 void *callback_context,
251 bool install_handler)
252 : dump_path_(),
253 filter_(NULL),
254 callback_(NULL),
255 callback_context_(callback_context),
256 directCallback_(callback),
257 handler_thread_(NULL),
258 handler_port_(MACH_PORT_NULL),
259 previous_(NULL),
260 installed_exception_handler_(false),
261 is_in_teardown_(false),
262 last_minidump_write_result_(false),
263 use_minidump_write_mutex_(false) {
264 MinidumpGenerator::GatherSystemInformation();
265 Setup(install_handler);
266 }
267
~ExceptionHandler()268 ExceptionHandler::~ExceptionHandler() {
269 Teardown();
270 }
271
WriteMinidump(bool write_exception_stream)272 bool ExceptionHandler::WriteMinidump(bool write_exception_stream) {
273 // If we're currently writing, just return
274 if (use_minidump_write_mutex_)
275 return false;
276
277 use_minidump_write_mutex_ = true;
278 last_minidump_write_result_ = false;
279
280 // Lock the mutex. Since we just created it, this will return immediately.
281 if (pthread_mutex_lock(&minidump_write_mutex_) == 0) {
282 // Send an empty message to the handle port so that a minidump will
283 // be written
284 SendMessageToHandlerThread(write_exception_stream ?
285 kWriteDumpWithExceptionMessage :
286 kWriteDumpMessage);
287
288 // Wait for the minidump writer to complete its writing. It will unlock
289 // the mutex when completed
290 pthread_mutex_lock(&minidump_write_mutex_);
291 }
292
293 use_minidump_write_mutex_ = false;
294 UpdateNextID();
295 return last_minidump_write_result_;
296 }
297
298 // static
WriteMinidump(const string & dump_path,bool write_exception_stream,MinidumpCallback callback,void * callback_context)299 bool ExceptionHandler::WriteMinidump(const string &dump_path,
300 bool write_exception_stream,
301 MinidumpCallback callback,
302 void *callback_context) {
303 ExceptionHandler handler(dump_path, NULL, callback, callback_context, false,
304 NULL);
305 return handler.WriteMinidump(write_exception_stream);
306 }
307
308 // static
WriteMinidumpForChild(mach_port_t child,mach_port_t child_blamed_thread,const string & dump_path,MinidumpCallback callback,void * callback_context)309 bool ExceptionHandler::WriteMinidumpForChild(mach_port_t child,
310 mach_port_t child_blamed_thread,
311 const string &dump_path,
312 MinidumpCallback callback,
313 void *callback_context) {
314 ScopedTaskSuspend suspend(child);
315
316 MinidumpGenerator generator(child, MACH_PORT_NULL);
317 string dump_id;
318 string dump_filename = generator.UniqueNameInDirectory(dump_path, &dump_id);
319
320 generator.SetExceptionInformation(EXC_BREAKPOINT,
321 #if defined (__i386__) || defined(__x86_64__)
322 EXC_I386_BPT,
323 #elif defined (__ppc__) || defined (__ppc64__)
324 EXC_PPC_BREAKPOINT,
325 #else
326 #error architecture not supported
327 #endif
328 0,
329 child_blamed_thread);
330 bool result = generator.Write(dump_filename.c_str());
331
332 if (callback) {
333 return callback(dump_path.c_str(), dump_id.c_str(),
334 callback_context, result);
335 }
336 return result;
337 }
338
WriteMinidumpWithException(int exception_type,int exception_code,int exception_subcode,mach_port_t thread_name,bool exit_after_write)339 bool ExceptionHandler::WriteMinidumpWithException(int exception_type,
340 int exception_code,
341 int exception_subcode,
342 mach_port_t thread_name,
343 bool exit_after_write) {
344 bool result = false;
345
346 if (directCallback_) {
347 if (directCallback_(callback_context_,
348 exception_type,
349 exception_code,
350 exception_subcode,
351 thread_name) ) {
352 if (exit_after_write)
353 _exit(exception_type);
354 }
355 } else if (IsOutOfProcess()) {
356 if (exception_type && exception_code) {
357 // If this is a real exception, give the filter (if any) a chance to
358 // decide if this should be sent.
359 if (filter_ && !filter_(callback_context_))
360 return false;
361 return crash_generation_client_->RequestDumpForException(
362 exception_type,
363 exception_code,
364 exception_subcode,
365 thread_name);
366 }
367 } else {
368 string minidump_id;
369
370 // Putting the MinidumpGenerator in its own context will ensure that the
371 // destructor is executed, closing the newly created minidump file.
372 if (!dump_path_.empty()) {
373 MinidumpGenerator md;
374 if (exception_type && exception_code) {
375 // If this is a real exception, give the filter (if any) a chance to
376 // decide if this should be sent.
377 if (filter_ && !filter_(callback_context_))
378 return false;
379
380 md.SetExceptionInformation(exception_type, exception_code,
381 exception_subcode, thread_name);
382 }
383
384 result = md.Write(next_minidump_path_c_);
385 }
386
387 // Call user specified callback (if any)
388 if (callback_) {
389 // If the user callback returned true and we're handling an exception
390 // (rather than just writing out the file), then we should exit without
391 // forwarding the exception to the next handler.
392 if (callback_(dump_path_c_, next_minidump_id_c_, callback_context_,
393 result)) {
394 if (exit_after_write)
395 _exit(exception_type);
396 }
397 }
398 }
399
400 return result;
401 }
402
ForwardException(mach_port_t task,mach_port_t failed_thread,exception_type_t exception,exception_data_t code,mach_msg_type_number_t code_count)403 kern_return_t ForwardException(mach_port_t task, mach_port_t failed_thread,
404 exception_type_t exception,
405 exception_data_t code,
406 mach_msg_type_number_t code_count) {
407 // At this time, we should have called Uninstall() on the exception handler
408 // so that the current exception ports are the ones that we should be
409 // forwarding to.
410 ExceptionParameters current;
411
412 current.count = EXC_TYPES_COUNT;
413 mach_port_t current_task = mach_task_self();
414 kern_return_t result = task_get_exception_ports(current_task,
415 s_exception_mask,
416 current.masks,
417 ¤t.count,
418 current.ports,
419 current.behaviors,
420 current.flavors);
421
422 // Find the first exception handler that matches the exception
423 unsigned int found;
424 for (found = 0; found < current.count; ++found) {
425 if (current.masks[found] & (1 << exception)) {
426 break;
427 }
428 }
429
430 // Nothing to forward
431 if (found == current.count) {
432 fprintf(stderr, "** No previous ports for forwarding!! \n");
433 exit(KERN_FAILURE);
434 }
435
436 mach_port_t target_port = current.ports[found];
437 exception_behavior_t target_behavior = current.behaviors[found];
438 thread_state_flavor_t target_flavor = current.flavors[found];
439
440 mach_msg_type_number_t thread_state_count = THREAD_STATE_MAX;
441 breakpad_thread_state_data_t thread_state;
442 switch (target_behavior) {
443 case EXCEPTION_DEFAULT:
444 result = exception_raise(target_port, failed_thread, task, exception,
445 code, code_count);
446 break;
447
448 case EXCEPTION_STATE:
449 result = thread_get_state(failed_thread, target_flavor, thread_state,
450 &thread_state_count);
451 if (result == KERN_SUCCESS)
452 result = exception_raise_state(target_port, failed_thread, task,
453 exception, code,
454 code_count, &target_flavor,
455 thread_state, thread_state_count,
456 thread_state, &thread_state_count);
457 if (result == KERN_SUCCESS)
458 result = thread_set_state(failed_thread, target_flavor, thread_state,
459 thread_state_count);
460 break;
461
462 case EXCEPTION_STATE_IDENTITY:
463 result = thread_get_state(failed_thread, target_flavor, thread_state,
464 &thread_state_count);
465 if (result == KERN_SUCCESS)
466 result = exception_raise_state_identity(target_port, failed_thread,
467 task, exception, code,
468 code_count, &target_flavor,
469 thread_state,
470 thread_state_count,
471 thread_state,
472 &thread_state_count);
473 if (result == KERN_SUCCESS)
474 result = thread_set_state(failed_thread, target_flavor, thread_state,
475 thread_state_count);
476 break;
477
478 default:
479 fprintf(stderr, "** Unknown exception behavior\n");
480 result = KERN_FAILURE;
481 break;
482 }
483
484 return result;
485 }
486
487 // Callback from exc_server()
catch_exception_raise(mach_port_t port,mach_port_t failed_thread,mach_port_t task,exception_type_t exception,exception_data_t code,mach_msg_type_number_t code_count)488 kern_return_t catch_exception_raise(mach_port_t port, mach_port_t failed_thread,
489 mach_port_t task,
490 exception_type_t exception,
491 exception_data_t code,
492 mach_msg_type_number_t code_count) {
493 if (task != mach_task_self()) {
494 return KERN_FAILURE;
495 }
496 return ForwardException(task, failed_thread, exception, code, code_count);
497 }
498
499 // static
WaitForMessage(void * exception_handler_class)500 void *ExceptionHandler::WaitForMessage(void *exception_handler_class) {
501 ExceptionHandler *self =
502 reinterpret_cast<ExceptionHandler *>(exception_handler_class);
503 ExceptionMessage receive;
504
505 // Wait for the exception info
506 while (1) {
507 receive.header.msgh_local_port = self->handler_port_;
508 receive.header.msgh_size = static_cast<mach_msg_size_t>(sizeof(receive));
509 kern_return_t result = mach_msg(&(receive.header),
510 MACH_RCV_MSG | MACH_RCV_LARGE, 0,
511 receive.header.msgh_size,
512 self->handler_port_,
513 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
514
515
516 if (result == KERN_SUCCESS) {
517 // Uninstall our handler so that we don't get in a loop if the process of
518 // writing out a minidump causes an exception. However, if the exception
519 // was caused by a fork'd process, don't uninstall things
520
521 // If the actual exception code is zero, then we're calling this handler
522 // in a way that indicates that we want to either exit this thread or
523 // generate a minidump
524 //
525 // While reporting, all threads (except this one) must be suspended
526 // to avoid misleading stacks. If appropriate they will be resumed
527 // afterwards.
528 if (!receive.exception) {
529 // Don't touch self, since this message could have been sent
530 // from its destructor.
531 if (receive.header.msgh_id == kShutdownMessage)
532 return NULL;
533
534 self->SuspendThreads();
535
536 #if USE_PROTECTED_ALLOCATIONS
537 if(gBreakpadAllocator)
538 gBreakpadAllocator->Unprotect();
539 #endif
540
541 mach_port_t thread = MACH_PORT_NULL;
542 int exception_type = 0;
543 int exception_code = 0;
544 if (receive.header.msgh_id == kWriteDumpWithExceptionMessage) {
545 thread = receive.thread.name;
546 exception_type = EXC_BREAKPOINT;
547 #if defined (__i386__) || defined(__x86_64__)
548 exception_code = EXC_I386_BPT;
549 #elif defined (__ppc__) || defined (__ppc64__)
550 exception_code = EXC_PPC_BREAKPOINT;
551 #else
552 #error architecture not supported
553 #endif
554 }
555
556 // Write out the dump and save the result for later retrieval
557 self->last_minidump_write_result_ =
558 self->WriteMinidumpWithException(exception_type, exception_code,
559 0, thread,
560 false);
561
562 #if USE_PROTECTED_ALLOCATIONS
563 if(gBreakpadAllocator)
564 gBreakpadAllocator->Protect();
565 #endif
566
567 self->ResumeThreads();
568
569 if (self->use_minidump_write_mutex_)
570 pthread_mutex_unlock(&self->minidump_write_mutex_);
571 } else {
572 // When forking a child process with the exception handler installed,
573 // if the child crashes, it will send the exception back to the parent
574 // process. The check for task == self_task() ensures that only
575 // exceptions that occur in the parent process are caught and
576 // processed. If the exception was not caused by this task, we
577 // still need to call into the exception server and have it return
578 // KERN_FAILURE (see breakpad_exception_raise) in order for the kernel
579 // to move onto the host exception handler for the child task
580 if (receive.task.name == mach_task_self()) {
581 self->SuspendThreads();
582
583 #if USE_PROTECTED_ALLOCATIONS
584 if(gBreakpadAllocator)
585 gBreakpadAllocator->Unprotect();
586 #endif
587
588 int subcode = 0;
589 if (receive.exception == EXC_BAD_ACCESS && receive.code_count > 1)
590 subcode = receive.code[1];
591
592 // Generate the minidump with the exception data.
593 self->WriteMinidumpWithException(receive.exception, receive.code[0],
594 subcode, receive.thread.name, true);
595
596 self->UninstallHandler(true);
597
598 #if USE_PROTECTED_ALLOCATIONS
599 if(gBreakpadAllocator)
600 gBreakpadAllocator->Protect();
601 #endif
602 }
603 // Pass along the exception to the server, which will setup the
604 // message and call breakpad_exception_raise() and put the return
605 // code into the reply.
606 ExceptionReplyMessage reply;
607 if (!exc_server(&receive.header, &reply.header))
608 exit(1);
609
610 // Send a reply and exit
611 result = mach_msg(&(reply.header), MACH_SEND_MSG,
612 reply.header.msgh_size, 0, MACH_PORT_NULL,
613 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
614 }
615 }
616 }
617
618 return NULL;
619 }
620
InstallHandler()621 bool ExceptionHandler::InstallHandler() {
622 try {
623 #if USE_PROTECTED_ALLOCATIONS
624 previous_ = new (gBreakpadAllocator->Allocate(sizeof(ExceptionParameters)) )
625 ExceptionParameters();
626 #else
627 previous_ = new ExceptionParameters();
628 #endif
629
630 }
631 catch (std::bad_alloc) {
632 return false;
633 }
634
635 // Save the current exception ports so that we can forward to them
636 previous_->count = EXC_TYPES_COUNT;
637 mach_port_t current_task = mach_task_self();
638 kern_return_t result = task_get_exception_ports(current_task,
639 s_exception_mask,
640 previous_->masks,
641 &previous_->count,
642 previous_->ports,
643 previous_->behaviors,
644 previous_->flavors);
645
646 // Setup the exception ports on this task
647 if (result == KERN_SUCCESS)
648 result = task_set_exception_ports(current_task, s_exception_mask,
649 handler_port_, EXCEPTION_DEFAULT,
650 THREAD_STATE_NONE);
651
652 installed_exception_handler_ = (result == KERN_SUCCESS);
653
654 return installed_exception_handler_;
655 }
656
UninstallHandler(bool in_exception)657 bool ExceptionHandler::UninstallHandler(bool in_exception) {
658 kern_return_t result = KERN_SUCCESS;
659
660 if (installed_exception_handler_) {
661 mach_port_t current_task = mach_task_self();
662
663 // Restore the previous ports
664 for (unsigned int i = 0; i < previous_->count; ++i) {
665 result = task_set_exception_ports(current_task, previous_->masks[i],
666 previous_->ports[i],
667 previous_->behaviors[i],
668 previous_->flavors[i]);
669 if (result != KERN_SUCCESS)
670 return false;
671 }
672
673 // this delete should NOT happen if an exception just occurred!
674 if (!in_exception) {
675 #if USE_PROTECTED_ALLOCATIONS
676 previous_->~ExceptionParameters();
677 #else
678 delete previous_;
679 #endif
680 }
681
682 previous_ = NULL;
683 installed_exception_handler_ = false;
684 }
685
686 return result == KERN_SUCCESS;
687 }
688
Setup(bool install_handler)689 bool ExceptionHandler::Setup(bool install_handler) {
690 if (pthread_mutex_init(&minidump_write_mutex_, NULL))
691 return false;
692
693 // Create a receive right
694 mach_port_t current_task = mach_task_self();
695 kern_return_t result = mach_port_allocate(current_task,
696 MACH_PORT_RIGHT_RECEIVE,
697 &handler_port_);
698 // Add send right
699 if (result == KERN_SUCCESS)
700 result = mach_port_insert_right(current_task, handler_port_, handler_port_,
701 MACH_MSG_TYPE_MAKE_SEND);
702
703 if (install_handler && result == KERN_SUCCESS)
704 if (!InstallHandler())
705 return false;
706
707 if (result == KERN_SUCCESS) {
708 // Install the handler in its own thread, detached as we won't be joining.
709 pthread_attr_t attr;
710 pthread_attr_init(&attr);
711 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
712 int thread_create_result = pthread_create(&handler_thread_, &attr,
713 &WaitForMessage, this);
714 pthread_attr_destroy(&attr);
715 result = thread_create_result ? KERN_FAILURE : KERN_SUCCESS;
716 }
717
718 return result == KERN_SUCCESS ? true : false;
719 }
720
Teardown()721 bool ExceptionHandler::Teardown() {
722 kern_return_t result = KERN_SUCCESS;
723 is_in_teardown_ = true;
724
725 if (!UninstallHandler(false))
726 return false;
727
728 // Send an empty message so that the handler_thread exits
729 if (SendMessageToHandlerThread(kShutdownMessage)) {
730 mach_port_t current_task = mach_task_self();
731 result = mach_port_deallocate(current_task, handler_port_);
732 if (result != KERN_SUCCESS)
733 return false;
734 } else {
735 return false;
736 }
737
738 handler_thread_ = NULL;
739 handler_port_ = NULL;
740 pthread_mutex_destroy(&minidump_write_mutex_);
741
742 return result == KERN_SUCCESS;
743 }
744
SendMessageToHandlerThread(HandlerThreadMessage message_id)745 bool ExceptionHandler::SendMessageToHandlerThread(
746 HandlerThreadMessage message_id) {
747 ExceptionMessage msg;
748 memset(&msg, 0, sizeof(msg));
749 msg.header.msgh_id = message_id;
750 if (message_id == kWriteDumpMessage ||
751 message_id == kWriteDumpWithExceptionMessage) {
752 // Include this thread's port.
753 msg.thread.name = mach_thread_self();
754 msg.thread.disposition = MACH_MSG_TYPE_PORT_SEND;
755 msg.thread.type = MACH_MSG_PORT_DESCRIPTOR;
756 }
757 msg.header.msgh_size = sizeof(msg) - sizeof(msg.padding);
758 msg.header.msgh_remote_port = handler_port_;
759 msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND,
760 MACH_MSG_TYPE_MAKE_SEND_ONCE);
761 kern_return_t result = mach_msg(&(msg.header),
762 MACH_SEND_MSG | MACH_SEND_TIMEOUT,
763 msg.header.msgh_size, 0, 0,
764 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
765
766 return result == KERN_SUCCESS;
767 }
768
UpdateNextID()769 void ExceptionHandler::UpdateNextID() {
770 next_minidump_path_ =
771 (MinidumpGenerator::UniqueNameInDirectory(dump_path_, &next_minidump_id_));
772
773 next_minidump_path_c_ = next_minidump_path_.c_str();
774 next_minidump_id_c_ = next_minidump_id_.c_str();
775 }
776
SuspendThreads()777 bool ExceptionHandler::SuspendThreads() {
778 thread_act_port_array_t threads_for_task;
779 mach_msg_type_number_t thread_count;
780
781 if (task_threads(mach_task_self(), &threads_for_task, &thread_count))
782 return false;
783
784 // suspend all of the threads except for this one
785 for (unsigned int i = 0; i < thread_count; ++i) {
786 if (threads_for_task[i] != mach_thread_self()) {
787 if (thread_suspend(threads_for_task[i]))
788 return false;
789 }
790 }
791
792 return true;
793 }
794
ResumeThreads()795 bool ExceptionHandler::ResumeThreads() {
796 thread_act_port_array_t threads_for_task;
797 mach_msg_type_number_t thread_count;
798
799 if (task_threads(mach_task_self(), &threads_for_task, &thread_count))
800 return false;
801
802 // resume all of the threads except for this one
803 for (unsigned int i = 0; i < thread_count; ++i) {
804 if (threads_for_task[i] != mach_thread_self()) {
805 if (thread_resume(threads_for_task[i]))
806 return false;
807 }
808 }
809
810 return true;
811 }
812
813 } // namespace google_breakpad
814