/ src / client / solaris / handler / exception_handler.cc
exception_handler.cc
  1  // Copyright 2007 Google LLC
  2  //
  3  // Redistribution and use in source and binary forms, with or without
  4  // modification, are permitted provided that the following conditions are
  5  // met:
  6  //
  7  //     * Redistributions of source code must retain the above copyright
  8  // notice, this list of conditions and the following disclaimer.
  9  //     * Redistributions in binary form must reproduce the above
 10  // copyright notice, this list of conditions and the following disclaimer
 11  // in the documentation and/or other materials provided with the
 12  // distribution.
 13  //     * Neither the name of Google LLC nor the names of its
 14  // contributors may be used to endorse or promote products derived from
 15  // this software without specific prior written permission.
 16  //
 17  // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 18  // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 19  // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 20  // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 21  // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 22  // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 23  // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 24  // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 25  // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 26  // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 27  // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 28  
 29  // Author: Alfred Peng
 30  
 31  #ifdef HAVE_CONFIG_H
 32  #include <config.h>  // Must come first
 33  #endif
 34  
 35  #include <signal.h>
 36  #include <sys/stat.h>
 37  #include <sys/types.h>
 38  #include <unistd.h>
 39  
 40  #include <cassert>
 41  #include <cstdlib>
 42  #include <ctime>
 43  
 44  #include "client/solaris/handler/exception_handler.h"
 45  #include "common/solaris/guid_creator.h"
 46  #include "common/solaris/message_output.h"
 47  #include "google_breakpad/common/minidump_format.h"
 48  
 49  namespace google_breakpad {
 50  
 51  // Signals that we are interested.
 52  static const int kSigTable[] = {
 53    SIGSEGV,
 54    SIGABRT,
 55    SIGFPE,
 56    SIGILL,
 57    SIGBUS
 58  };
 59  
 60  std::vector<ExceptionHandler*>* ExceptionHandler::handler_stack_ = NULL;
 61  int ExceptionHandler::handler_stack_index_ = 0;
 62  pthread_mutex_t ExceptionHandler::handler_stack_mutex_ =
 63    PTHREAD_MUTEX_INITIALIZER;
 64  
 65  ExceptionHandler::ExceptionHandler(const string& dump_path,
 66                                     FilterCallback filter,
 67                                     MinidumpCallback callback,
 68                                     void* callback_context,
 69                                     bool install_handler)
 70      : filter_(filter),
 71        callback_(callback),
 72        callback_context_(callback_context),
 73        dump_path_(),
 74        installed_handler_(install_handler) {
 75    set_dump_path(dump_path);
 76  
 77    if (install_handler) {
 78      SetupHandler();
 79    }
 80  
 81    if (install_handler) {
 82      pthread_mutex_lock(&handler_stack_mutex_);
 83  
 84      if (handler_stack_ == NULL)
 85        handler_stack_ = new std::vector<ExceptionHandler*>;
 86      handler_stack_->push_back(this);
 87      pthread_mutex_unlock(&handler_stack_mutex_);
 88    }
 89  }
 90  
 91  ExceptionHandler::~ExceptionHandler() {
 92    TeardownAllHandlers();
 93    pthread_mutex_lock(&handler_stack_mutex_);
 94    if (handler_stack_->back() == this) {
 95      handler_stack_->pop_back();
 96    } else {
 97      print_message1(2, "warning: removing Breakpad handler out of order\n");
 98      for (std::vector<ExceptionHandler*>::iterator iterator =
 99           handler_stack_->begin();
100           iterator != handler_stack_->end();
101           ++iterator) {
102        if (*iterator == this) {
103          handler_stack_->erase(iterator);
104        }
105      }
106    }
107  
108    if (handler_stack_->empty()) {
109      // When destroying the last ExceptionHandler that installed a handler,
110      // clean up the handler stack.
111      delete handler_stack_;
112      handler_stack_ = NULL;
113    }
114    pthread_mutex_unlock(&handler_stack_mutex_);
115  }
116  
117  bool ExceptionHandler::WriteMinidump() {
118    return InternalWriteMinidump(0, 0, NULL);
119  }
120  
121  // static
122  bool ExceptionHandler::WriteMinidump(const string& dump_path,
123                                       MinidumpCallback callback,
124                                       void* callback_context) {
125    ExceptionHandler handler(dump_path, NULL, callback,
126                             callback_context, false);
127    return handler.InternalWriteMinidump(0, 0, NULL);
128  }
129  
130  void ExceptionHandler::SetupHandler() {
131    // Signal on a different stack to avoid using the stack
132    // of the crashing lwp.
133    struct sigaltstack sig_stack;
134    sig_stack.ss_sp = malloc(MINSIGSTKSZ);
135    if (sig_stack.ss_sp == NULL)
136      return;
137    sig_stack.ss_size = MINSIGSTKSZ;
138    sig_stack.ss_flags = 0;
139  
140    if (sigaltstack(&sig_stack, NULL) < 0)
141      return;
142    for (size_t i = 0; i < sizeof(kSigTable) / sizeof(kSigTable[0]); ++i)
143      SetupHandler(kSigTable[i]);
144  }
145  
146  void ExceptionHandler::SetupHandler(int signo) {
147    struct sigaction act, old_act;
148    act.sa_handler = HandleException;
149    act.sa_flags = SA_ONSTACK;
150    if (sigaction(signo, &act, &old_act) < 0)
151      return;
152    old_handlers_[signo] = old_act.sa_handler;
153  }
154  
155  void ExceptionHandler::TeardownHandler(int signo) {
156    if (old_handlers_.find(signo) != old_handlers_.end()) {
157      struct sigaction act;
158      act.sa_handler = old_handlers_[signo];
159      act.sa_flags = 0;
160      sigaction(signo, &act, 0);
161    }
162  }
163  
164  void ExceptionHandler::TeardownAllHandlers() {
165    for (size_t i = 0; i < sizeof(kSigTable) / sizeof(kSigTable[0]); ++i) {
166      TeardownHandler(kSigTable[i]);
167    }
168  }
169  
170  // static
171  void ExceptionHandler::HandleException(int signo) {
172  //void ExceptionHandler::HandleException(int signo, siginfo_t* sip, ucontext_t* sig_ctx) {
173    // The context information about the signal is put on the stack of
174    // the signal handler frame as value parameter. For some reasons, the
175    // prototype of the handler doesn't declare this information as parameter, we
176    // will do it by hand. The stack layout for a signal handler frame is here:
177    // http://src.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/lib/libproc/common/Pstack.c#81
178    //
179    // However, if we are being called by another signal handler passing the
180    // signal up the chain, then we may not have this random extra parameter,
181    // so we may have to walk the stack to find it.  We do the actual work
182    // on another thread, where it's a little safer, but we want the ebp
183    // from this frame to find it.
184    uintptr_t current_ebp = (uintptr_t)_getfp();
185  
186    pthread_mutex_lock(&handler_stack_mutex_);
187    ExceptionHandler* current_handler =
188      handler_stack_->at(handler_stack_->size() - ++handler_stack_index_);
189    pthread_mutex_unlock(&handler_stack_mutex_);
190  
191    // Restore original handler.
192    current_handler->TeardownHandler(signo);
193  
194    ucontext_t* sig_ctx = NULL;
195    if (current_handler->InternalWriteMinidump(signo, current_ebp, &sig_ctx)) {
196  //  if (current_handler->InternalWriteMinidump(signo, &sig_ctx)) {
197      // Fully handled this exception, safe to exit.
198      exit(EXIT_FAILURE);
199    } else {
200      // Exception not fully handled, will call the next handler in stack to
201      // process it.
202      typedef void (*SignalHandler)(int signo);
203      SignalHandler old_handler =
204        reinterpret_cast<SignalHandler>(current_handler->old_handlers_[signo]);
205      if (old_handler != NULL)
206        old_handler(signo);
207    }
208  
209    pthread_mutex_lock(&handler_stack_mutex_);
210    current_handler->SetupHandler(signo);
211    --handler_stack_index_;
212    // All the handlers in stack have been invoked to handle the exception,
213    // normally the process should be terminated and should not reach here.
214    // In case we got here, ask the OS to handle it to avoid endless loop,
215    // normally the OS will generate a core and termiate the process. This
216    // may be desired to debug the program.
217    if (handler_stack_index_ == 0)
218      signal(signo, SIG_DFL);
219    pthread_mutex_unlock(&handler_stack_mutex_);
220  }
221  
222  bool ExceptionHandler::InternalWriteMinidump(int signo,
223                                               uintptr_t sighandler_ebp,
224                                               ucontext_t** sig_ctx) {
225    if (filter_ && !filter_(callback_context_))
226      return false;
227  
228    bool success = false;
229    GUID guid;
230    char guid_str[kGUIDStringLength + 1];
231    if (CreateGUID(&guid) && GUIDToString(&guid, guid_str, sizeof(guid_str))) {
232      char minidump_path[PATH_MAX];
233      snprintf(minidump_path, sizeof(minidump_path), "%s/%s.dmp",
234               dump_path_c_, guid_str);
235  
236      // Block all the signals we want to process when writing minidump.
237      // We don't want it to be interrupted.
238      sigset_t sig_blocked, sig_old;
239      bool blocked = true;
240      sigfillset(&sig_blocked);
241      for (size_t i = 0; i < sizeof(kSigTable) / sizeof(kSigTable[0]); ++i)
242        sigdelset(&sig_blocked, kSigTable[i]);
243      if (sigprocmask(SIG_BLOCK, &sig_blocked, &sig_old) != 0) {
244        blocked = false;
245        print_message1(2, "HandleException: failed to block signals.\n");
246      }
247  
248      success = minidump_generator_.WriteMinidumpToFile(
249                         minidump_path, signo, sighandler_ebp, sig_ctx);
250  
251      // Unblock the signals.
252      if (blocked)
253        sigprocmask(SIG_SETMASK, &sig_old, &sig_old);
254  
255      if (callback_)
256        success = callback_(dump_path_c_, guid_str, callback_context_, success);
257    }
258    return success;
259  }
260  
261  }  // namespace google_breakpad