Home | History | Annotate | Download | only in crash_generation
      1 // Copyright (c) 2010 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 "client/mac/crash_generation/crash_generation_server.h"
     31 
     32 #include <pthread.h>
     33 
     34 #include "client/mac/crash_generation/client_info.h"
     35 #include "client/mac/handler/minidump_generator.h"
     36 #include "common/mac/scoped_task_suspend-inl.h"
     37 
     38 namespace google_breakpad {
     39 
     40 CrashGenerationServer::CrashGenerationServer(
     41     const char *mach_port_name,
     42     FilterCallback filter,
     43     void *filter_context,
     44     OnClientDumpRequestCallback dump_callback,
     45     void *dump_context,
     46     OnClientExitingCallback exit_callback,
     47     void *exit_context,
     48     bool generate_dumps,
     49     const std::string &dump_path)
     50     : filter_(filter),
     51       filter_context_(filter_context),
     52       dump_callback_(dump_callback),
     53       dump_context_(dump_context),
     54       exit_callback_(exit_callback),
     55       exit_context_(exit_context),
     56       generate_dumps_(generate_dumps),
     57       dump_dir_(dump_path.empty() ? "/tmp" : dump_path),
     58       started_(false),
     59       receive_port_(mach_port_name),
     60       mach_port_name_(mach_port_name) {
     61 }
     62 
     63 CrashGenerationServer::~CrashGenerationServer() {
     64   if (started_)
     65     Stop();
     66 }
     67 
     68 bool CrashGenerationServer::Start() {
     69   int thread_create_result = pthread_create(&server_thread_, NULL,
     70                                             &WaitForMessages, this);
     71   started_ = thread_create_result == 0;
     72   return started_;
     73 }
     74 
     75 bool CrashGenerationServer::Stop() {
     76   if (!started_)
     77     return false;
     78 
     79   // Send a quit message to the background thread, and then join it.
     80   MachPortSender sender(mach_port_name_.c_str());
     81   MachSendMessage quit_message(kQuitMessage);
     82   const mach_msg_timeout_t kSendTimeoutMs = 2 * 1000;
     83   kern_return_t result = sender.SendMessage(quit_message, kSendTimeoutMs);
     84   if (result == KERN_SUCCESS) {
     85     int thread_join_result = pthread_join(server_thread_, NULL);
     86     started_ = thread_join_result != 0;
     87   }
     88 
     89   return !started_;
     90 }
     91 
     92 // static
     93 void *CrashGenerationServer::WaitForMessages(void *server) {
     94   CrashGenerationServer *self =
     95       reinterpret_cast<CrashGenerationServer*>(server);
     96   while (self->WaitForOneMessage()) {}
     97   return NULL;
     98 }
     99 
    100 bool CrashGenerationServer::WaitForOneMessage() {
    101   MachReceiveMessage message;
    102   kern_return_t result = receive_port_.WaitForMessage(&message,
    103                                                       MACH_MSG_TIMEOUT_NONE);
    104   if (result == KERN_SUCCESS) {
    105     switch (message.GetMessageID()) {
    106       case kDumpRequestMessage: {
    107         ExceptionInfo &info = (ExceptionInfo &)*message.GetData();
    108 
    109         mach_port_t remote_task = message.GetTranslatedPort(0);
    110         mach_port_t crashing_thread = message.GetTranslatedPort(1);
    111         mach_port_t handler_thread = message.GetTranslatedPort(2);
    112         mach_port_t ack_port = message.GetTranslatedPort(3);
    113         pid_t remote_pid = -1;
    114         pid_for_task(remote_task, &remote_pid);
    115         ClientInfo client(remote_pid);
    116 
    117         bool result;
    118         std::string dump_path;
    119         if (generate_dumps_ && (!filter_ || filter_(filter_context_))) {
    120           ScopedTaskSuspend suspend(remote_task);
    121 
    122           MinidumpGenerator generator(remote_task, handler_thread);
    123           dump_path = generator.UniqueNameInDirectory(dump_dir_, NULL);
    124 
    125           if (info.exception_type && info.exception_code) {
    126             generator.SetExceptionInformation(info.exception_type,
    127                                               info.exception_code,
    128                                               info.exception_subcode,
    129                                               crashing_thread);
    130           }
    131           result = generator.Write(dump_path.c_str());
    132         } else {
    133           result = true;
    134         }
    135 
    136         if (result && dump_callback_) {
    137           dump_callback_(dump_context_, client, dump_path);
    138         }
    139 
    140         // TODO(ted): support a way for the client to send additional data,
    141         // perhaps with a callback so users of the server can read the data
    142         // themselves?
    143 
    144         if (ack_port != MACH_PORT_DEAD && ack_port != MACH_PORT_NULL) {
    145           MachPortSender sender(ack_port);
    146           MachSendMessage ack_message(kAcknowledgementMessage);
    147           const mach_msg_timeout_t kSendTimeoutMs = 2 * 1000;
    148 
    149           sender.SendMessage(ack_message, kSendTimeoutMs);
    150         }
    151 
    152         if (exit_callback_) {
    153           exit_callback_(exit_context_, client);
    154         }
    155         break;
    156       }
    157       case kQuitMessage:
    158         return false;
    159     }
    160   } else {  // result != KERN_SUCCESS
    161     return false;
    162   }
    163   return true;
    164 }
    165 
    166 }  // namespace google_breakpad
    167