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 38namespace google_breakpad { 39 40CrashGenerationServer::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 63CrashGenerationServer::~CrashGenerationServer() { 64 if (started_) 65 Stop(); 66} 67 68bool 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 75bool 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 93void *CrashGenerationServer::WaitForMessages(void *server) { 94 CrashGenerationServer *self = 95 reinterpret_cast<CrashGenerationServer*>(server); 96 while (self->WaitForOneMessage()) {} 97 return NULL; 98} 99 100bool 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