1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/crashreporter/google-breakpad/src/client/solaris/handler/exception_handler.cc Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,258 @@ 1.4 +// Copyright (c) 2007, Google Inc. 1.5 +// All rights reserved. 1.6 +// 1.7 +// Redistribution and use in source and binary forms, with or without 1.8 +// modification, are permitted provided that the following conditions are 1.9 +// met: 1.10 +// 1.11 +// * Redistributions of source code must retain the above copyright 1.12 +// notice, this list of conditions and the following disclaimer. 1.13 +// * Redistributions in binary form must reproduce the above 1.14 +// copyright notice, this list of conditions and the following disclaimer 1.15 +// in the documentation and/or other materials provided with the 1.16 +// distribution. 1.17 +// * Neither the name of Google Inc. nor the names of its 1.18 +// contributors may be used to endorse or promote products derived from 1.19 +// this software without specific prior written permission. 1.20 +// 1.21 +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 1.22 +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 1.23 +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 1.24 +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 1.25 +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 1.26 +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 1.27 +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 1.28 +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 1.29 +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 1.30 +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 1.31 +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 1.32 + 1.33 +// Author: Alfred Peng 1.34 + 1.35 +#include <signal.h> 1.36 +#include <sys/stat.h> 1.37 +#include <sys/types.h> 1.38 +#include <unistd.h> 1.39 + 1.40 +#include <cassert> 1.41 +#include <cstdlib> 1.42 +#include <ctime> 1.43 + 1.44 +#include "client/solaris/handler/exception_handler.h" 1.45 +#include "common/solaris/guid_creator.h" 1.46 +#include "common/solaris/message_output.h" 1.47 +#include "google_breakpad/common/minidump_format.h" 1.48 + 1.49 +namespace google_breakpad { 1.50 + 1.51 +// Signals that we are interested. 1.52 +static const int kSigTable[] = { 1.53 + SIGSEGV, 1.54 + SIGABRT, 1.55 + SIGFPE, 1.56 + SIGILL, 1.57 + SIGBUS 1.58 +}; 1.59 + 1.60 +std::vector<ExceptionHandler*> *ExceptionHandler::handler_stack_ = NULL; 1.61 +int ExceptionHandler::handler_stack_index_ = 0; 1.62 +pthread_mutex_t ExceptionHandler::handler_stack_mutex_ = 1.63 + PTHREAD_MUTEX_INITIALIZER; 1.64 + 1.65 +ExceptionHandler::ExceptionHandler(const string &dump_path, 1.66 + FilterCallback filter, 1.67 + MinidumpCallback callback, 1.68 + void *callback_context, 1.69 + bool install_handler) 1.70 + : filter_(filter), 1.71 + callback_(callback), 1.72 + callback_context_(callback_context), 1.73 + dump_path_(), 1.74 + installed_handler_(install_handler) { 1.75 + set_dump_path(dump_path); 1.76 + 1.77 + if (install_handler) { 1.78 + SetupHandler(); 1.79 + } 1.80 + 1.81 + if (install_handler) { 1.82 + pthread_mutex_lock(&handler_stack_mutex_); 1.83 + 1.84 + if (handler_stack_ == NULL) 1.85 + handler_stack_ = new std::vector<ExceptionHandler *>; 1.86 + handler_stack_->push_back(this); 1.87 + pthread_mutex_unlock(&handler_stack_mutex_); 1.88 + } 1.89 +} 1.90 + 1.91 +ExceptionHandler::~ExceptionHandler() { 1.92 + TeardownAllHandlers(); 1.93 + pthread_mutex_lock(&handler_stack_mutex_); 1.94 + if (handler_stack_->back() == this) { 1.95 + handler_stack_->pop_back(); 1.96 + } else { 1.97 + print_message1(2, "warning: removing Breakpad handler out of order\n"); 1.98 + for (std::vector<ExceptionHandler *>::iterator iterator = 1.99 + handler_stack_->begin(); 1.100 + iterator != handler_stack_->end(); 1.101 + ++iterator) { 1.102 + if (*iterator == this) { 1.103 + handler_stack_->erase(iterator); 1.104 + } 1.105 + } 1.106 + } 1.107 + 1.108 + if (handler_stack_->empty()) { 1.109 + // When destroying the last ExceptionHandler that installed a handler, 1.110 + // clean up the handler stack. 1.111 + delete handler_stack_; 1.112 + handler_stack_ = NULL; 1.113 + } 1.114 + pthread_mutex_unlock(&handler_stack_mutex_); 1.115 +} 1.116 + 1.117 +bool ExceptionHandler::WriteMinidump() { 1.118 + return InternalWriteMinidump(0, 0, NULL); 1.119 +} 1.120 + 1.121 +// static 1.122 +bool ExceptionHandler::WriteMinidump(const string &dump_path, 1.123 + MinidumpCallback callback, 1.124 + void *callback_context) { 1.125 + ExceptionHandler handler(dump_path, NULL, callback, 1.126 + callback_context, false); 1.127 + return handler.InternalWriteMinidump(0, 0, NULL); 1.128 +} 1.129 + 1.130 +void ExceptionHandler::SetupHandler() { 1.131 + // Signal on a different stack to avoid using the stack 1.132 + // of the crashing lwp. 1.133 + struct sigaltstack sig_stack; 1.134 + sig_stack.ss_sp = malloc(MINSIGSTKSZ); 1.135 + if (sig_stack.ss_sp == NULL) 1.136 + return; 1.137 + sig_stack.ss_size = MINSIGSTKSZ; 1.138 + sig_stack.ss_flags = 0; 1.139 + 1.140 + if (sigaltstack(&sig_stack, NULL) < 0) 1.141 + return; 1.142 + for (size_t i = 0; i < sizeof(kSigTable) / sizeof(kSigTable[0]); ++i) 1.143 + SetupHandler(kSigTable[i]); 1.144 +} 1.145 + 1.146 +void ExceptionHandler::SetupHandler(int signo) { 1.147 + struct sigaction act, old_act; 1.148 + act.sa_handler = HandleException; 1.149 + act.sa_flags = SA_ONSTACK; 1.150 + if (sigaction(signo, &act, &old_act) < 0) 1.151 + return; 1.152 + old_handlers_[signo] = old_act.sa_handler; 1.153 +} 1.154 + 1.155 +void ExceptionHandler::TeardownHandler(int signo) { 1.156 + if (old_handlers_.find(signo) != old_handlers_.end()) { 1.157 + struct sigaction act; 1.158 + act.sa_handler = old_handlers_[signo]; 1.159 + act.sa_flags = 0; 1.160 + sigaction(signo, &act, 0); 1.161 + } 1.162 +} 1.163 + 1.164 +void ExceptionHandler::TeardownAllHandlers() { 1.165 + for (size_t i = 0; i < sizeof(kSigTable) / sizeof(kSigTable[0]); ++i) { 1.166 + TeardownHandler(kSigTable[i]); 1.167 + } 1.168 +} 1.169 + 1.170 +// static 1.171 +void ExceptionHandler::HandleException(int signo) { 1.172 +//void ExceptionHandler::HandleException(int signo, siginfo_t *sip, ucontext_t *sig_ctx) { 1.173 + // The context information about the signal is put on the stack of 1.174 + // the signal handler frame as value parameter. For some reasons, the 1.175 + // prototype of the handler doesn't declare this information as parameter, we 1.176 + // will do it by hand. The stack layout for a signal handler frame is here: 1.177 + // http://src.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/lib/libproc/common/Pstack.c#81 1.178 + // 1.179 + // However, if we are being called by another signal handler passing the 1.180 + // signal up the chain, then we may not have this random extra parameter, 1.181 + // so we may have to walk the stack to find it. We do the actual work 1.182 + // on another thread, where it's a little safer, but we want the ebp 1.183 + // from this frame to find it. 1.184 + uintptr_t current_ebp = (uintptr_t)_getfp(); 1.185 + 1.186 + pthread_mutex_lock(&handler_stack_mutex_); 1.187 + ExceptionHandler *current_handler = 1.188 + handler_stack_->at(handler_stack_->size() - ++handler_stack_index_); 1.189 + pthread_mutex_unlock(&handler_stack_mutex_); 1.190 + 1.191 + // Restore original handler. 1.192 + current_handler->TeardownHandler(signo); 1.193 + 1.194 + ucontext_t *sig_ctx = NULL; 1.195 + if (current_handler->InternalWriteMinidump(signo, current_ebp, &sig_ctx)) { 1.196 +// if (current_handler->InternalWriteMinidump(signo, &sig_ctx)) { 1.197 + // Fully handled this exception, safe to exit. 1.198 + exit(EXIT_FAILURE); 1.199 + } else { 1.200 + // Exception not fully handled, will call the next handler in stack to 1.201 + // process it. 1.202 + typedef void (*SignalHandler)(int signo); 1.203 + SignalHandler old_handler = 1.204 + reinterpret_cast<SignalHandler>(current_handler->old_handlers_[signo]); 1.205 + if (old_handler != NULL) 1.206 + old_handler(signo); 1.207 + } 1.208 + 1.209 + pthread_mutex_lock(&handler_stack_mutex_); 1.210 + current_handler->SetupHandler(signo); 1.211 + --handler_stack_index_; 1.212 + // All the handlers in stack have been invoked to handle the exception, 1.213 + // normally the process should be terminated and should not reach here. 1.214 + // In case we got here, ask the OS to handle it to avoid endless loop, 1.215 + // normally the OS will generate a core and termiate the process. This 1.216 + // may be desired to debug the program. 1.217 + if (handler_stack_index_ == 0) 1.218 + signal(signo, SIG_DFL); 1.219 + pthread_mutex_unlock(&handler_stack_mutex_); 1.220 +} 1.221 + 1.222 +bool ExceptionHandler::InternalWriteMinidump(int signo, 1.223 + uintptr_t sighandler_ebp, 1.224 + ucontext_t **sig_ctx) { 1.225 + if (filter_ && !filter_(callback_context_)) 1.226 + return false; 1.227 + 1.228 + bool success = false; 1.229 + GUID guid; 1.230 + char guid_str[kGUIDStringLength + 1]; 1.231 + if (CreateGUID(&guid) && GUIDToString(&guid, guid_str, sizeof(guid_str))) { 1.232 + char minidump_path[PATH_MAX]; 1.233 + snprintf(minidump_path, sizeof(minidump_path), "%s/%s.dmp", 1.234 + dump_path_c_, guid_str); 1.235 + 1.236 + // Block all the signals we want to process when writing minidump. 1.237 + // We don't want it to be interrupted. 1.238 + sigset_t sig_blocked, sig_old; 1.239 + bool blocked = true; 1.240 + sigfillset(&sig_blocked); 1.241 + for (size_t i = 0; i < sizeof(kSigTable) / sizeof(kSigTable[0]); ++i) 1.242 + sigdelset(&sig_blocked, kSigTable[i]); 1.243 + if (sigprocmask(SIG_BLOCK, &sig_blocked, &sig_old) != 0) { 1.244 + blocked = false; 1.245 + print_message1(2, "HandleException: failed to block signals.\n"); 1.246 + } 1.247 + 1.248 + success = minidump_generator_.WriteMinidumpToFile( 1.249 + minidump_path, signo, sighandler_ebp, sig_ctx); 1.250 + 1.251 + // Unblock the signals. 1.252 + if (blocked) 1.253 + sigprocmask(SIG_SETMASK, &sig_old, &sig_old); 1.254 + 1.255 + if (callback_) 1.256 + success = callback_(dump_path_c_, guid_str, callback_context_, success); 1.257 + } 1.258 + return success; 1.259 +} 1.260 + 1.261 +} // namespace google_breakpad