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