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 46namespace google_breakpad { 47 48// Signals that we are interested. 49static const int kSigTable[] = { 50 SIGSEGV, 51 SIGABRT, 52 SIGFPE, 53 SIGILL, 54 SIGBUS 55}; 56 57std::vector<ExceptionHandler*> *ExceptionHandler::handler_stack_ = NULL; 58int ExceptionHandler::handler_stack_index_ = 0; 59pthread_mutex_t ExceptionHandler::handler_stack_mutex_ = 60 PTHREAD_MUTEX_INITIALIZER; 61 62ExceptionHandler::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 88ExceptionHandler::~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 114bool ExceptionHandler::WriteMinidump() { 115 return InternalWriteMinidump(0, 0, NULL); 116} 117 118// static 119bool 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 127void 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 143void 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 152void 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 161void ExceptionHandler::TeardownAllHandlers() { 162 for (size_t i = 0; i < sizeof(kSigTable) / sizeof(kSigTable[0]); ++i) { 163 TeardownHandler(kSigTable[i]); 164 } 165} 166 167// static 168void 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 219bool 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