1 // Copyright (c) 2007, 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 // Author: Alfred Peng 31 32 #include <signal.h> 33 #include <sys/stat.h> 34 #include <sys/types.h> 35 #include <unistd.h> 36 37 #include <cassert> 38 #include <cstdlib> 39 #include <ctime> 40 41 #include "client/solaris/handler/exception_handler.h" 42 #include "common/solaris/guid_creator.h" 43 #include "common/solaris/message_output.h" 44 #include "google_breakpad/common/minidump_format.h" 45 46 namespace google_breakpad { 47 48 // Signals that we are interested. 49 static const int kSigTable[] = { 50 SIGSEGV, 51 SIGABRT, 52 SIGFPE, 53 SIGILL, 54 SIGBUS 55 }; 56 57 std::vector<ExceptionHandler*> *ExceptionHandler::handler_stack_ = NULL; 58 int ExceptionHandler::handler_stack_index_ = 0; 59 pthread_mutex_t ExceptionHandler::handler_stack_mutex_ = 60 PTHREAD_MUTEX_INITIALIZER; 61 62 ExceptionHandler::ExceptionHandler(const string &dump_path, 63 FilterCallback filter, 64 MinidumpCallback callback, 65 void *callback_context, 66 bool install_handler) 67 : filter_(filter), 68 callback_(callback), 69 callback_context_(callback_context), 70 dump_path_(), 71 installed_handler_(install_handler) { 72 set_dump_path(dump_path); 73 74 if (install_handler) { 75 SetupHandler(); 76 } 77 78 if (install_handler) { 79 pthread_mutex_lock(&handler_stack_mutex_); 80 81 if (handler_stack_ == NULL) 82 handler_stack_ = new std::vector<ExceptionHandler *>; 83 handler_stack_->push_back(this); 84 pthread_mutex_unlock(&handler_stack_mutex_); 85 } 86 } 87 88 ExceptionHandler::~ExceptionHandler() { 89 TeardownAllHandlers(); 90 pthread_mutex_lock(&handler_stack_mutex_); 91 if (handler_stack_->back() == this) { 92 handler_stack_->pop_back(); 93 } else { 94 print_message1(2, "warning: removing Breakpad handler out of order\n"); 95 for (std::vector<ExceptionHandler *>::iterator iterator = 96 handler_stack_->begin(); 97 iterator != handler_stack_->end(); 98 ++iterator) { 99 if (*iterator == this) { 100 handler_stack_->erase(iterator); 101 } 102 } 103 } 104 105 if (handler_stack_->empty()) { 106 // When destroying the last ExceptionHandler that installed a handler, 107 // clean up the handler stack. 108 delete handler_stack_; 109 handler_stack_ = NULL; 110 } 111 pthread_mutex_unlock(&handler_stack_mutex_); 112 } 113 114 bool ExceptionHandler::WriteMinidump() { 115 return InternalWriteMinidump(0, 0, NULL); 116 } 117 118 // static 119 bool ExceptionHandler::WriteMinidump(const string &dump_path, 120 MinidumpCallback callback, 121 void *callback_context) { 122 ExceptionHandler handler(dump_path, NULL, callback, 123 callback_context, false); 124 return handler.InternalWriteMinidump(0, 0, NULL); 125 } 126 127 void ExceptionHandler::SetupHandler() { 128 // Signal on a different stack to avoid using the stack 129 // of the crashing lwp. 130 struct sigaltstack sig_stack; 131 sig_stack.ss_sp = malloc(MINSIGSTKSZ); 132 if (sig_stack.ss_sp == NULL) 133 return; 134 sig_stack.ss_size = MINSIGSTKSZ; 135 sig_stack.ss_flags = 0; 136 137 if (sigaltstack(&sig_stack, NULL) < 0) 138 return; 139 for (size_t i = 0; i < sizeof(kSigTable) / sizeof(kSigTable[0]); ++i) 140 SetupHandler(kSigTable[i]); 141 } 142 143 void ExceptionHandler::SetupHandler(int signo) { 144 struct sigaction act, old_act; 145 act.sa_handler = HandleException; 146 act.sa_flags = SA_ONSTACK; 147 if (sigaction(signo, &act, &old_act) < 0) 148 return; 149 old_handlers_[signo] = old_act.sa_handler; 150 } 151 152 void ExceptionHandler::TeardownHandler(int signo) { 153 if (old_handlers_.find(signo) != old_handlers_.end()) { 154 struct sigaction act; 155 act.sa_handler = old_handlers_[signo]; 156 act.sa_flags = 0; 157 sigaction(signo, &act, 0); 158 } 159 } 160 161 void ExceptionHandler::TeardownAllHandlers() { 162 for (size_t i = 0; i < sizeof(kSigTable) / sizeof(kSigTable[0]); ++i) { 163 TeardownHandler(kSigTable[i]); 164 } 165 } 166 167 // static 168 void ExceptionHandler::HandleException(int signo) { 169 //void ExceptionHandler::HandleException(int signo, siginfo_t *sip, ucontext_t *sig_ctx) { 170 // The context information about the signal is put on the stack of 171 // the signal handler frame as value parameter. For some reasons, the 172 // prototype of the handler doesn't declare this information as parameter, we 173 // will do it by hand. The stack layout for a signal handler frame is here: 174 // http://src.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/lib/libproc/common/Pstack.c#81 175 // 176 // However, if we are being called by another signal handler passing the 177 // signal up the chain, then we may not have this random extra parameter, 178 // so we may have to walk the stack to find it. We do the actual work 179 // on another thread, where it's a little safer, but we want the ebp 180 // from this frame to find it. 181 uintptr_t current_ebp = (uintptr_t)_getfp(); 182 183 pthread_mutex_lock(&handler_stack_mutex_); 184 ExceptionHandler *current_handler = 185 handler_stack_->at(handler_stack_->size() - ++handler_stack_index_); 186 pthread_mutex_unlock(&handler_stack_mutex_); 187 188 // Restore original handler. 189 current_handler->TeardownHandler(signo); 190 191 ucontext_t *sig_ctx = NULL; 192 if (current_handler->InternalWriteMinidump(signo, current_ebp, &sig_ctx)) { 193 // if (current_handler->InternalWriteMinidump(signo, &sig_ctx)) { 194 // Fully handled this exception, safe to exit. 195 exit(EXIT_FAILURE); 196 } else { 197 // Exception not fully handled, will call the next handler in stack to 198 // process it. 199 typedef void (*SignalHandler)(int signo); 200 SignalHandler old_handler = 201 reinterpret_cast<SignalHandler>(current_handler->old_handlers_[signo]); 202 if (old_handler != NULL) 203 old_handler(signo); 204 } 205 206 pthread_mutex_lock(&handler_stack_mutex_); 207 current_handler->SetupHandler(signo); 208 --handler_stack_index_; 209 // All the handlers in stack have been invoked to handle the exception, 210 // normally the process should be terminated and should not reach here. 211 // In case we got here, ask the OS to handle it to avoid endless loop, 212 // normally the OS will generate a core and termiate the process. This 213 // may be desired to debug the program. 214 if (handler_stack_index_ == 0) 215 signal(signo, SIG_DFL); 216 pthread_mutex_unlock(&handler_stack_mutex_); 217 } 218 219 bool ExceptionHandler::InternalWriteMinidump(int signo, 220 uintptr_t sighandler_ebp, 221 ucontext_t **sig_ctx) { 222 if (filter_ && !filter_(callback_context_)) 223 return false; 224 225 bool success = false; 226 GUID guid; 227 char guid_str[kGUIDStringLength + 1]; 228 if (CreateGUID(&guid) && GUIDToString(&guid, guid_str, sizeof(guid_str))) { 229 char minidump_path[PATH_MAX]; 230 snprintf(minidump_path, sizeof(minidump_path), "%s/%s.dmp", 231 dump_path_c_, guid_str); 232 233 // Block all the signals we want to process when writing minidump. 234 // We don't want it to be interrupted. 235 sigset_t sig_blocked, sig_old; 236 bool blocked = true; 237 sigfillset(&sig_blocked); 238 for (size_t i = 0; i < sizeof(kSigTable) / sizeof(kSigTable[0]); ++i) 239 sigdelset(&sig_blocked, kSigTable[i]); 240 if (sigprocmask(SIG_BLOCK, &sig_blocked, &sig_old) != 0) { 241 blocked = false; 242 print_message1(2, "HandleException: failed to block signals.\n"); 243 } 244 245 success = minidump_generator_.WriteMinidumpToFile( 246 minidump_path, signo, sighandler_ebp, sig_ctx); 247 248 // Unblock the signals. 249 if (blocked) 250 sigprocmask(SIG_SETMASK, &sig_old, &sig_old); 251 252 if (callback_) 253 success = callback_(dump_path_c_, guid_str, callback_context_, success); 254 } 255 return success; 256 } 257 258 } // namespace google_breakpad 259