~ubuntu-branches/ubuntu/trusty/tomahawk/trusty-proposed

« back to all changes in this revision

Viewing changes to thirdparty/breakpad/client/solaris/handler/exception_handler.cc

  • Committer: Package Import Robot
  • Author(s): Harald Sitter
  • Date: 2013-03-07 21:50:13 UTC
  • Revision ID: package-import@ubuntu.com-20130307215013-6gdjkdds7i9uenvs
Tags: upstream-0.6.0+dfsg
ImportĀ upstreamĀ versionĀ 0.6.0+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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