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                                                  &current.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