exception_handler.cc revision ebebd0fcef7540fc51c23de50d45e79e260a30cb
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 37#ifndef USE_PROTECTED_ALLOCATIONS 38#define USE_PROTECTED_ALLOCATIONS 0 39#endif 40 41// If USE_PROTECTED_ALLOCATIONS is activated then the 42// gBreakpadAllocator needs to be setup in other code 43// ahead of time. Please see ProtectedMemoryAllocator.h 44// for more details. 45#if USE_PROTECTED_ALLOCATIONS 46 #include "protected_memory_allocator.h" 47 extern ProtectedMemoryAllocator *gBreakpadAllocator; 48#endif 49 50 51namespace google_breakpad { 52 53using std::map; 54 55// These structures and techniques are illustrated in 56// Mac OS X Internals, Amit Singh, ch 9.7 57struct ExceptionMessage { 58 mach_msg_header_t header; 59 mach_msg_body_t body; 60 mach_msg_port_descriptor_t thread; 61 mach_msg_port_descriptor_t task; 62 NDR_record_t ndr; 63 exception_type_t exception; 64 mach_msg_type_number_t code_count; 65 integer_t code[EXCEPTION_CODE_MAX]; 66 char padding[512]; 67}; 68 69struct ExceptionParameters { 70 ExceptionParameters() : count(0) {} 71 mach_msg_type_number_t count; 72 exception_mask_t masks[EXC_TYPES_COUNT]; 73 mach_port_t ports[EXC_TYPES_COUNT]; 74 exception_behavior_t behaviors[EXC_TYPES_COUNT]; 75 thread_state_flavor_t flavors[EXC_TYPES_COUNT]; 76}; 77 78struct ExceptionReplyMessage { 79 mach_msg_header_t header; 80 NDR_record_t ndr; 81 kern_return_t return_code; 82}; 83 84// Only catch these three exceptions. The other ones are nebulously defined 85// and may result in treating a non-fatal exception as fatal. 86exception_mask_t s_exception_mask = EXC_MASK_BAD_ACCESS | 87EXC_MASK_BAD_INSTRUCTION | EXC_MASK_ARITHMETIC | EXC_MASK_BREAKPOINT; 88 89extern "C" 90{ 91 // Forward declarations for functions that need "C" style compilation 92 boolean_t exc_server(mach_msg_header_t *request, 93 mach_msg_header_t *reply); 94 95 // This symbol must be visible to dlsym() - see 96 // http://code.google.com/p/google-breakpad/issues/detail?id=345 for details. 97 kern_return_t catch_exception_raise(mach_port_t target_port, 98 mach_port_t failed_thread, 99 mach_port_t task, 100 exception_type_t exception, 101 exception_data_t code, 102 mach_msg_type_number_t code_count) 103 __attribute__((visibility("default"))); 104 105 kern_return_t ForwardException(mach_port_t task, 106 mach_port_t failed_thread, 107 exception_type_t exception, 108 exception_data_t code, 109 mach_msg_type_number_t code_count); 110 111 kern_return_t exception_raise(mach_port_t target_port, 112 mach_port_t failed_thread, 113 mach_port_t task, 114 exception_type_t exception, 115 exception_data_t exception_code, 116 mach_msg_type_number_t exception_code_count); 117 118 kern_return_t 119 exception_raise_state(mach_port_t target_port, 120 mach_port_t failed_thread, 121 mach_port_t task, 122 exception_type_t exception, 123 exception_data_t exception_code, 124 mach_msg_type_number_t code_count, 125 thread_state_flavor_t *target_flavor, 126 thread_state_t thread_state, 127 mach_msg_type_number_t thread_state_count, 128 thread_state_t thread_state, 129 mach_msg_type_number_t *thread_state_count); 130 131 kern_return_t 132 exception_raise_state_identity(mach_port_t target_port, 133 mach_port_t failed_thread, 134 mach_port_t task, 135 exception_type_t exception, 136 exception_data_t exception_code, 137 mach_msg_type_number_t exception_code_count, 138 thread_state_flavor_t *target_flavor, 139 thread_state_t thread_state, 140 mach_msg_type_number_t thread_state_count, 141 thread_state_t thread_state, 142 mach_msg_type_number_t *thread_state_count); 143 144 kern_return_t breakpad_exception_raise_state(mach_port_t exception_port, 145 exception_type_t exception, 146 const exception_data_t code, 147 mach_msg_type_number_t codeCnt, 148 int *flavor, 149 const thread_state_t old_state, 150 mach_msg_type_number_t old_stateCnt, 151 thread_state_t new_state, 152 mach_msg_type_number_t *new_stateCnt 153 ); 154 155 kern_return_t breakpad_exception_raise_state_identity(mach_port_t exception_port, 156 mach_port_t thread, 157 mach_port_t task, 158 exception_type_t exception, 159 exception_data_t code, 160 mach_msg_type_number_t codeCnt, 161 int *flavor, 162 thread_state_t old_state, 163 mach_msg_type_number_t old_stateCnt, 164 thread_state_t new_state, 165 mach_msg_type_number_t *new_stateCnt 166 ); 167 168 kern_return_t breakpad_exception_raise(mach_port_t port, mach_port_t failed_thread, 169 mach_port_t task, 170 exception_type_t exception, 171 exception_data_t code, 172 mach_msg_type_number_t code_count); 173} 174 175 176 177kern_return_t breakpad_exception_raise_state(mach_port_t exception_port, 178 exception_type_t exception, 179 const exception_data_t code, 180 mach_msg_type_number_t codeCnt, 181 int *flavor, 182 const thread_state_t old_state, 183 mach_msg_type_number_t old_stateCnt, 184 thread_state_t new_state, 185 mach_msg_type_number_t *new_stateCnt 186 ) 187{ 188 return KERN_SUCCESS; 189} 190 191kern_return_t breakpad_exception_raise_state_identity(mach_port_t exception_port, 192 mach_port_t thread, 193 mach_port_t task, 194 exception_type_t exception, 195 exception_data_t code, 196 mach_msg_type_number_t codeCnt, 197 int *flavor, 198 thread_state_t old_state, 199 mach_msg_type_number_t old_stateCnt, 200 thread_state_t new_state, 201 mach_msg_type_number_t *new_stateCnt 202 ) 203{ 204 return KERN_SUCCESS; 205} 206 207kern_return_t breakpad_exception_raise(mach_port_t port, mach_port_t failed_thread, 208 mach_port_t task, 209 exception_type_t exception, 210 exception_data_t code, 211 mach_msg_type_number_t code_count) { 212 213 if (task != mach_task_self()) { 214 return KERN_FAILURE; 215 } 216 return ForwardException(task, failed_thread, exception, code, code_count); 217} 218 219 220ExceptionHandler::ExceptionHandler(const string &dump_path, 221 FilterCallback filter, 222 MinidumpCallback callback, 223 void *callback_context, 224 bool install_handler) 225 : dump_path_(), 226 filter_(filter), 227 callback_(callback), 228 callback_context_(callback_context), 229 directCallback_(NULL), 230 handler_thread_(NULL), 231 handler_port_(MACH_PORT_NULL), 232 previous_(NULL), 233 installed_exception_handler_(false), 234 is_in_teardown_(false), 235 last_minidump_write_result_(false), 236 use_minidump_write_mutex_(false) { 237 // This will update to the ID and C-string pointers 238 set_dump_path(dump_path); 239 MinidumpGenerator::GatherSystemInformation(); 240 Setup(install_handler); 241} 242 243// special constructor if we want to bypass minidump writing and 244// simply get a callback with the exception information 245ExceptionHandler::ExceptionHandler(DirectCallback callback, 246 void *callback_context, 247 bool install_handler) 248 : dump_path_(), 249 filter_(NULL), 250 callback_(NULL), 251 callback_context_(callback_context), 252 directCallback_(callback), 253 handler_thread_(NULL), 254 handler_port_(MACH_PORT_NULL), 255 previous_(NULL), 256 installed_exception_handler_(false), 257 is_in_teardown_(false), 258 last_minidump_write_result_(false), 259 use_minidump_write_mutex_(false) { 260 MinidumpGenerator::GatherSystemInformation(); 261 Setup(install_handler); 262} 263 264ExceptionHandler::~ExceptionHandler() { 265 Teardown(); 266} 267 268bool ExceptionHandler::WriteMinidump() { 269 // If we're currently writing, just return 270 if (use_minidump_write_mutex_) 271 return false; 272 273 use_minidump_write_mutex_ = true; 274 last_minidump_write_result_ = false; 275 276 // Lock the mutex. Since we just created it, this will return immediately. 277 if (pthread_mutex_lock(&minidump_write_mutex_) == 0) { 278 // Send an empty message to the handle port so that a minidump will 279 // be written 280 SendEmptyMachMessage(); 281 282 // Wait for the minidump writer to complete its writing. It will unlock 283 // the mutex when completed 284 pthread_mutex_lock(&minidump_write_mutex_); 285 } 286 287 use_minidump_write_mutex_ = false; 288 UpdateNextID(); 289 return last_minidump_write_result_; 290} 291 292// static 293bool ExceptionHandler::WriteMinidump(const string &dump_path, 294 MinidumpCallback callback, 295 void *callback_context) { 296 ExceptionHandler handler(dump_path, NULL, callback, callback_context, false); 297 return handler.WriteMinidump(); 298} 299 300bool ExceptionHandler::WriteMinidumpWithException(int exception_type, 301 int exception_code, 302 int exception_subcode, 303 mach_port_t thread_name) { 304 bool result = false; 305 306 if (directCallback_) { 307 if (directCallback_(callback_context_, 308 exception_type, 309 exception_code, 310 exception_subcode, 311 thread_name) ) { 312 if (exception_type && exception_code) 313 _exit(exception_type); 314 } 315 } else { 316 string minidump_id; 317 318 // Putting the MinidumpGenerator in its own context will ensure that the 319 // destructor is executed, closing the newly created minidump file. 320 if (!dump_path_.empty()) { 321 MinidumpGenerator md; 322 if (exception_type && exception_code) { 323 // If this is a real exception, give the filter (if any) a chance to 324 // decided if this should be sent 325 if (filter_ && !filter_(callback_context_)) 326 return false; 327 328 md.SetExceptionInformation(exception_type, exception_code, 329 exception_subcode, thread_name); 330 } 331 332 result = md.Write(next_minidump_path_c_); 333 } 334 335 // Call user specified callback (if any) 336 if (callback_) { 337 // If the user callback returned true and we're handling an exception 338 // (rather than just writing out the file), then we should exit without 339 // forwarding the exception to the next handler. 340 if (callback_(dump_path_c_, next_minidump_id_c_, callback_context_, 341 result)) { 342 if (exception_type && exception_code) 343 _exit(exception_type); 344 } 345 } 346 } 347 348 return result; 349} 350 351kern_return_t ForwardException(mach_port_t task, mach_port_t failed_thread, 352 exception_type_t exception, 353 exception_data_t code, 354 mach_msg_type_number_t code_count) { 355 // At this time, we should have called Uninstall() on the exception handler 356 // so that the current exception ports are the ones that we should be 357 // forwarding to. 358 ExceptionParameters current; 359 360 current.count = EXC_TYPES_COUNT; 361 mach_port_t current_task = mach_task_self(); 362 kern_return_t result = task_get_exception_ports(current_task, 363 s_exception_mask, 364 current.masks, 365 ¤t.count, 366 current.ports, 367 current.behaviors, 368 current.flavors); 369 370 // Find the first exception handler that matches the exception 371 unsigned int found; 372 for (found = 0; found < current.count; ++found) { 373 if (current.masks[found] & (1 << exception)) { 374 break; 375 } 376 } 377 378 // Nothing to forward 379 if (found == current.count) { 380 fprintf(stderr, "** No previous ports for forwarding!! \n"); 381 exit(KERN_FAILURE); 382 } 383 384 mach_port_t target_port = current.ports[found]; 385 exception_behavior_t target_behavior = current.behaviors[found]; 386 thread_state_flavor_t target_flavor = current.flavors[found]; 387 388 mach_msg_type_number_t thread_state_count = THREAD_STATE_MAX; 389 breakpad_thread_state_data_t thread_state; 390 switch (target_behavior) { 391 case EXCEPTION_DEFAULT: 392 result = exception_raise(target_port, failed_thread, task, exception, 393 code, code_count); 394 break; 395 396 case EXCEPTION_STATE: 397 result = thread_get_state(failed_thread, target_flavor, thread_state, 398 &thread_state_count); 399 if (result == KERN_SUCCESS) 400 result = exception_raise_state(target_port, failed_thread, task, 401 exception, code, 402 code_count, &target_flavor, 403 thread_state, thread_state_count, 404 thread_state, &thread_state_count); 405 if (result == KERN_SUCCESS) 406 result = thread_set_state(failed_thread, target_flavor, thread_state, 407 thread_state_count); 408 break; 409 410 case EXCEPTION_STATE_IDENTITY: 411 result = thread_get_state(failed_thread, target_flavor, thread_state, 412 &thread_state_count); 413 if (result == KERN_SUCCESS) 414 result = exception_raise_state_identity(target_port, failed_thread, 415 task, exception, code, 416 code_count, &target_flavor, 417 thread_state, 418 thread_state_count, 419 thread_state, 420 &thread_state_count); 421 if (result == KERN_SUCCESS) 422 result = thread_set_state(failed_thread, target_flavor, thread_state, 423 thread_state_count); 424 break; 425 426 default: 427 fprintf(stderr, "** Unknown exception behavior\n"); 428 result = KERN_FAILURE; 429 break; 430 } 431 432 return result; 433} 434 435// Callback from exc_server() 436kern_return_t catch_exception_raise(mach_port_t port, mach_port_t failed_thread, 437 mach_port_t task, 438 exception_type_t exception, 439 exception_data_t code, 440 mach_msg_type_number_t code_count) { 441 return ForwardException(task, failed_thread, exception, code, code_count); 442} 443 444// static 445void *ExceptionHandler::WaitForMessage(void *exception_handler_class) { 446 ExceptionHandler *self = 447 reinterpret_cast<ExceptionHandler *>(exception_handler_class); 448 ExceptionMessage receive; 449 450 // Wait for the exception info 451 while (1) { 452 receive.header.msgh_local_port = self->handler_port_; 453 receive.header.msgh_size = sizeof(receive); 454 kern_return_t result = mach_msg(&(receive.header), 455 MACH_RCV_MSG | MACH_RCV_LARGE, 0, 456 sizeof(receive), self->handler_port_, 457 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); 458 459 460 if (result == KERN_SUCCESS) { 461 // Uninstall our handler so that we don't get in a loop if the process of 462 // writing out a minidump causes an exception. However, if the exception 463 // was caused by a fork'd process, don't uninstall things 464 465 // If the actual exception code is zero, then we're calling this handler 466 // in a way that indicates that we want to either exit this thread or 467 // generate a minidump 468 // 469 // While reporting, all threads (except this one) must be suspended 470 // to avoid misleading stacks. If appropriate they will be resumed 471 // afterwards. 472 if (!receive.exception) { 473 if (self->is_in_teardown_) 474 return NULL; 475 476 self->SuspendThreads(); 477 478#if USE_PROTECTED_ALLOCATIONS 479 if(gBreakpadAllocator) 480 gBreakpadAllocator->Unprotect(); 481#endif 482 483 // Write out the dump and save the result for later retrieval 484 self->last_minidump_write_result_ = 485 self->WriteMinidumpWithException(0, 0, 0, 0); 486 487 self->UninstallHandler(false); 488 489#if USE_PROTECTED_ALLOCATIONS 490 if(gBreakpadAllocator) 491 gBreakpadAllocator->Protect(); 492#endif 493 494 self->ResumeThreads(); 495 496 if (self->use_minidump_write_mutex_) 497 pthread_mutex_unlock(&self->minidump_write_mutex_); 498 } else { 499 // When forking a child process with the exception handler installed, 500 // if the child crashes, it will send the exception back to the parent 501 // process. The check for task == self_task() ensures that only 502 // exceptions that occur in the parent process are caught and 503 // processed. If the exception was not caused by this task, we 504 // still need to call into the exception server and have it return 505 // KERN_FAILURE (see breakpad_exception_raise) in order for the kernel 506 // to move onto the host exception handler for the child task 507 if (receive.task.name == mach_task_self()) { 508 self->SuspendThreads(); 509 510#if USE_PROTECTED_ALLOCATIONS 511 if(gBreakpadAllocator) 512 gBreakpadAllocator->Unprotect(); 513#endif 514 515 int subcode = 0; 516 if (receive.exception == EXC_BAD_ACCESS && receive.code_count > 1) 517 subcode = receive.code[1]; 518 519 // Generate the minidump with the exception data. 520 self->WriteMinidumpWithException(receive.exception, receive.code[0], 521 subcode, receive.thread.name); 522 523 self->UninstallHandler(true); 524 525#if USE_PROTECTED_ALLOCATIONS 526 if(gBreakpadAllocator) 527 gBreakpadAllocator->Protect(); 528#endif 529 } 530 // Pass along the exception to the server, which will setup the 531 // message and call breakpad_exception_raise() and put the return 532 // code into the reply. 533 ExceptionReplyMessage reply; 534 if (!exc_server(&receive.header, &reply.header)) 535 exit(1); 536 537 // Send a reply and exit 538 result = mach_msg(&(reply.header), MACH_SEND_MSG, 539 reply.header.msgh_size, 0, MACH_PORT_NULL, 540 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); 541 } 542 } 543 } 544 545 return NULL; 546} 547 548bool ExceptionHandler::InstallHandler() { 549 try { 550#if USE_PROTECTED_ALLOCATIONS 551 previous_ = new (gBreakpadAllocator->Allocate(sizeof(ExceptionParameters)) ) 552 ExceptionParameters(); 553#else 554 previous_ = new ExceptionParameters(); 555#endif 556 557 } 558 catch (std::bad_alloc) { 559 return false; 560 } 561 562 // Save the current exception ports so that we can forward to them 563 previous_->count = EXC_TYPES_COUNT; 564 mach_port_t current_task = mach_task_self(); 565 kern_return_t result = task_get_exception_ports(current_task, 566 s_exception_mask, 567 previous_->masks, 568 &previous_->count, 569 previous_->ports, 570 previous_->behaviors, 571 previous_->flavors); 572 573 // Setup the exception ports on this task 574 if (result == KERN_SUCCESS) 575 result = task_set_exception_ports(current_task, s_exception_mask, 576 handler_port_, EXCEPTION_DEFAULT, 577 THREAD_STATE_NONE); 578 579 installed_exception_handler_ = (result == KERN_SUCCESS); 580 581 return installed_exception_handler_; 582} 583 584bool ExceptionHandler::UninstallHandler(bool in_exception) { 585 kern_return_t result = KERN_SUCCESS; 586 587 if (installed_exception_handler_) { 588 mach_port_t current_task = mach_task_self(); 589 590 // Restore the previous ports 591 for (unsigned int i = 0; i < previous_->count; ++i) { 592 result = task_set_exception_ports(current_task, previous_->masks[i], 593 previous_->ports[i], 594 previous_->behaviors[i], 595 previous_->flavors[i]); 596 if (result != KERN_SUCCESS) 597 return false; 598 } 599 600 // this delete should NOT happen if an exception just occurred! 601 if (!in_exception) { 602#if USE_PROTECTED_ALLOCATIONS 603 previous_->~ExceptionParameters(); 604#else 605 delete previous_; 606#endif 607 } 608 609 previous_ = NULL; 610 installed_exception_handler_ = false; 611 } 612 613 return result == KERN_SUCCESS; 614} 615 616bool ExceptionHandler::Setup(bool install_handler) { 617 if (pthread_mutex_init(&minidump_write_mutex_, NULL)) 618 return false; 619 620 // Create a receive right 621 mach_port_t current_task = mach_task_self(); 622 kern_return_t result = mach_port_allocate(current_task, 623 MACH_PORT_RIGHT_RECEIVE, 624 &handler_port_); 625 // Add send right 626 if (result == KERN_SUCCESS) 627 result = mach_port_insert_right(current_task, handler_port_, handler_port_, 628 MACH_MSG_TYPE_MAKE_SEND); 629 630 if (install_handler && result == KERN_SUCCESS) 631 if (!InstallHandler()) 632 return false; 633 634 if (result == KERN_SUCCESS) { 635 // Install the handler in its own thread, detached as we won't be joining. 636 pthread_attr_t attr; 637 pthread_attr_init(&attr); 638 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 639 int thread_create_result = pthread_create(&handler_thread_, &attr, 640 &WaitForMessage, this); 641 pthread_attr_destroy(&attr); 642 result = thread_create_result ? KERN_FAILURE : KERN_SUCCESS; 643 } 644 645 return result == KERN_SUCCESS ? true : false; 646} 647 648bool ExceptionHandler::Teardown() { 649 kern_return_t result = KERN_SUCCESS; 650 is_in_teardown_ = true; 651 652 if (!UninstallHandler(false)) 653 return false; 654 655 // Send an empty message so that the handler_thread exits 656 if (SendEmptyMachMessage()) { 657 mach_port_t current_task = mach_task_self(); 658 result = mach_port_deallocate(current_task, handler_port_); 659 if (result != KERN_SUCCESS) 660 return false; 661 } else { 662 return false; 663 } 664 665 handler_thread_ = NULL; 666 handler_port_ = NULL; 667 pthread_mutex_destroy(&minidump_write_mutex_); 668 669 return result == KERN_SUCCESS; 670} 671 672bool ExceptionHandler::SendEmptyMachMessage() { 673 ExceptionMessage empty; 674 memset(&empty, 0, sizeof(empty)); 675 empty.header.msgh_size = sizeof(empty) - sizeof(empty.padding); 676 empty.header.msgh_remote_port = handler_port_; 677 empty.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 678 MACH_MSG_TYPE_MAKE_SEND_ONCE); 679 kern_return_t result = mach_msg(&(empty.header), 680 MACH_SEND_MSG | MACH_SEND_TIMEOUT, 681 empty.header.msgh_size, 0, 0, 682 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); 683 684 return result == KERN_SUCCESS; 685} 686 687void ExceptionHandler::UpdateNextID() { 688 next_minidump_path_ = 689 (MinidumpGenerator::UniqueNameInDirectory(dump_path_, &next_minidump_id_)); 690 691 next_minidump_path_c_ = next_minidump_path_.c_str(); 692 next_minidump_id_c_ = next_minidump_id_.c_str(); 693} 694 695bool ExceptionHandler::SuspendThreads() { 696 thread_act_port_array_t threads_for_task; 697 mach_msg_type_number_t thread_count; 698 699 if (task_threads(mach_task_self(), &threads_for_task, &thread_count)) 700 return false; 701 702 // suspend all of the threads except for this one 703 for (unsigned int i = 0; i < thread_count; ++i) { 704 if (threads_for_task[i] != mach_thread_self()) { 705 if (thread_suspend(threads_for_task[i])) 706 return false; 707 } 708 } 709 710 return true; 711} 712 713bool ExceptionHandler::ResumeThreads() { 714 thread_act_port_array_t threads_for_task; 715 mach_msg_type_number_t thread_count; 716 717 if (task_threads(mach_task_self(), &threads_for_task, &thread_count)) 718 return false; 719 720 // resume all of the threads except for this one 721 for (unsigned int i = 0; i < thread_count; ++i) { 722 if (threads_for_task[i] != mach_thread_self()) { 723 if (thread_resume(threads_for_task[i])) 724 return false; 725 } 726 } 727 728 return true; 729} 730 731} // namespace google_breakpad 732