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