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