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