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