Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2281)

Side by Side Diff: breakpad/linux/exception_handler.cc

Issue 414049: Linux: Use upstream google-breakpad instead of our fork.... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: Created 11 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « breakpad/linux/exception_handler.h ('k') | breakpad/linux/exception_handler_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2009, 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 // The ExceptionHandler object installs signal handlers for a number of
31 // signals. We rely on the signal handler running on the thread which crashed
32 // in order to identify it. This is true of the synchronous signals (SEGV etc),
33 // but not true of ABRT. Thus, if you send ABRT to yourself in a program which
34 // uses ExceptionHandler, you need to use tgkill to direct it to the current
35 // thread.
36 //
37 // The signal flow looks like this:
38 //
39 // SignalHandler (uses a global stack of ExceptionHandler objects to find
40 // | one to handle the signal. If the first rejects it, try
41 // | the second etc...)
42 // V
43 // HandleSignal ----------------------------| (clones a new process which
44 // | | shares an address space with
45 // (wait for cloned | the crashed process. This
46 // process) | allows us to ptrace the crashed
47 // | | process)
48 // V V
49 // (set signal handler to ThreadEntry (static function to bounce
50 // SIG_DFL and rethrow, | back into the object)
51 // killing the crashed |
52 // process) V
53 // DoDump (writes minidump)
54 // |
55 // V
56 // sys_exit
57 //
58
59 // This code is a little fragmented. Different functions of the ExceptionHandler
60 // class run in a number of different contexts. Some of them run in a normal
61 // context and are easy to code, others run in a compromised context and the
62 // restrictions at the top of minidump_writer.cc apply: no libc and use the
63 // alternative malloc. Each function should have comment above it detailing the
64 // context which it runs in.
65
66 #include "breakpad/linux/exception_handler.h"
67
68 #include <errno.h>
69 #include <fcntl.h>
70 #include <linux/limits.h>
71 #include <sched.h>
72 #include <signal.h>
73 #include <stdio.h>
74 #include <sys/mman.h>
75 #include <sys/signal.h>
76 #include <sys/syscall.h>
77 #include <sys/ucontext.h>
78 #include <sys/user.h>
79 #include <sys/wait.h>
80 #include <unistd.h>
81
82 #include "breakpad/linux/linux_libc_support.h"
83 #include "breakpad/linux/linux_syscall_support.h"
84 #include "breakpad/linux/memory.h"
85 #include "breakpad/linux/minidump_writer.h"
86 #include "common/linux/guid_creator.h"
87
88 // A wrapper for the tgkill syscall: send a signal to a specific thread.
89 static int tgkill(pid_t tgid, pid_t tid, int sig) {
90 syscall(__NR_tgkill, tgid, tid, sig);
91 }
92
93 namespace google_breakpad {
94
95 // The list of signals which we consider to be crashes. The default action for
96 // all these signals must be Core (see man 7 signal) because we rethrow the
97 // signal after handling it and expect that it'll be fatal.
98 static const int kExceptionSignals[] = {
99 SIGSEGV, SIGABRT, SIGFPE, SIGILL, SIGBUS, -1
100 };
101
102 // We can stack multiple exception handlers. In that case, this is the global
103 // which holds the stack.
104 std::vector<ExceptionHandler*>* ExceptionHandler::handler_stack_ = NULL;
105 unsigned ExceptionHandler::handler_stack_index_ = 0;
106 pthread_mutex_t ExceptionHandler::handler_stack_mutex_ =
107 PTHREAD_MUTEX_INITIALIZER;
108
109 // Runs before crashing: normal context.
110 ExceptionHandler::ExceptionHandler(const std::string &dump_path,
111 FilterCallback filter,
112 MinidumpCallback callback,
113 void *callback_context,
114 bool install_handler)
115 : filter_(filter),
116 callback_(callback),
117 callback_context_(callback_context),
118 dump_path_(),
119 handler_installed_(install_handler),
120 crash_handler_(NULL) {
121 set_dump_path(dump_path);
122
123 if (install_handler) {
124 InstallHandlers();
125
126 pthread_mutex_lock(&handler_stack_mutex_);
127 if (handler_stack_ == NULL)
128 handler_stack_ = new std::vector<ExceptionHandler *>;
129 handler_stack_->push_back(this);
130 pthread_mutex_unlock(&handler_stack_mutex_);
131 }
132 }
133
134 // Runs before crashing: normal context.
135 ExceptionHandler::~ExceptionHandler() {
136 UninstallHandlers();
137 }
138
139 // Runs before crashing: normal context.
140 bool ExceptionHandler::InstallHandlers() {
141 // We run the signal handlers on an alternative stack because we might have
142 // crashed because of a stack overflow.
143
144 // We use this value rather than SIGSTKSZ because we would end up overrunning
145 // such a small stack.
146 static const unsigned kSigStackSize = 8192;
147
148 signal_stack = malloc(kSigStackSize);
149 stack_t stack;
150 memset(&stack, 0, sizeof(stack));
151 stack.ss_sp = signal_stack;
152 stack.ss_size = kSigStackSize;
153
154 if (sigaltstack(&stack, NULL) == -1)
155 return false;
156
157 struct sigaction sa;
158 memset(&sa, 0, sizeof(sa));
159 sigemptyset(&sa.sa_mask);
160
161 // mask all exception signals when we're handling one of them.
162 for (unsigned i = 0; kExceptionSignals[i] != -1; ++i)
163 sigaddset(&sa.sa_mask, kExceptionSignals[i]);
164
165 sa.sa_sigaction = SignalHandler;
166 sa.sa_flags = SA_ONSTACK | SA_SIGINFO;
167
168 for (unsigned i = 0; kExceptionSignals[i] != -1; ++i) {
169 struct sigaction* old = new struct sigaction;
170 if (sigaction(kExceptionSignals[i], &sa, old) == -1)
171 return false;
172 old_handlers_.push_back(std::make_pair(kExceptionSignals[i], old));
173 }
174 }
175
176 // Runs before crashing: normal context.
177 void ExceptionHandler::UninstallHandlers() {
178 for (unsigned i = 0; i < old_handlers_.size(); ++i) {
179 struct sigaction *action =
180 reinterpret_cast<struct sigaction*>(old_handlers_[i].second);
181 sigaction(old_handlers_[i].first, action, NULL);
182 delete action;
183 }
184
185 old_handlers_.clear();
186 }
187
188 // Runs before crashing: normal context.
189 void ExceptionHandler::UpdateNextID() {
190 GUID guid;
191 char guid_str[kGUIDStringLength + 1];
192 if (CreateGUID(&guid) && GUIDToString(&guid, guid_str, sizeof(guid_str))) {
193 next_minidump_id_ = guid_str;
194 next_minidump_id_c_ = next_minidump_id_.c_str();
195
196 char minidump_path[PATH_MAX];
197 snprintf(minidump_path, sizeof(minidump_path), "%s/%s.dmp",
198 dump_path_c_,
199 guid_str);
200
201 next_minidump_path_ = minidump_path;
202 next_minidump_path_c_ = next_minidump_path_.c_str();
203 }
204 }
205
206 // This function runs in a compromised context: see the top of the file.
207 // Runs on the crashing thread.
208 // static
209 void ExceptionHandler::SignalHandler(int sig, siginfo_t* info, void* uc) {
210 // All the exception signals are blocked at this point.
211
212 pthread_mutex_lock(&handler_stack_mutex_);
213
214 if (!handler_stack_->size()) {
215 pthread_mutex_unlock(&handler_stack_mutex_);
216 return;
217 }
218
219 for (int i = handler_stack_->size() - 1; i >= 0; --i) {
220 if ((*handler_stack_)[i]->HandleSignal(sig, info, uc)) {
221 // successfully handled: We are in an invalid state since an exception
222 // signal has been delivered. We don't call the exit handlers because
223 // they could end up corrupting on-disk state.
224 break;
225 }
226 }
227
228 pthread_mutex_unlock(&handler_stack_mutex_);
229
230 // Terminate ourselves with the same signal so that our parent knows that we
231 // crashed. The default action for all the signals which we catch is Core, so
232 // this is the end of us.
233 signal(sig, SIG_DFL);
234 tgkill(getpid(), sys_gettid(), sig);
235
236 // not reached.
237 }
238
239 struct ThreadArgument {
240 pid_t pid; // the crashing process
241 ExceptionHandler* handler;
242 const void* context; // a CrashContext structure
243 size_t context_size;
244 };
245
246 // This is the entry function for the cloned process. We are in a compromised
247 // context here: see the top of the file.
248 // static
249 int ExceptionHandler::ThreadEntry(void *arg) {
250 const ThreadArgument *thread_arg = reinterpret_cast<ThreadArgument*>(arg);
251 return thread_arg->handler->DoDump(thread_arg->pid, thread_arg->context,
252 thread_arg->context_size) == false;
253 }
254
255 // This function runs in a compromised context: see the top of the file.
256 // Runs on the crashing thread.
257 bool ExceptionHandler::HandleSignal(int sig, siginfo_t* info, void* uc) {
258 if (filter_ && !filter_(callback_context_))
259 return false;
260
261 // Allow ourselves to be dumped.
262 sys_prctl(PR_SET_DUMPABLE, 1);
263
264 CrashContext context;
265 memcpy(&context.siginfo, info, sizeof(siginfo_t));
266 memcpy(&context.context, uc, sizeof(struct ucontext));
267 memcpy(&context.float_state, ((struct ucontext *)uc)->uc_mcontext.fpregs,
268 sizeof(context.float_state));
269 context.tid = sys_gettid();
270
271 if (crash_handler_ && crash_handler_(&context, sizeof(context),
272 callback_context_))
273 return true;
274
275 static const unsigned kChildStackSize = 8000;
276 PageAllocator allocator;
277 uint8_t* stack = (uint8_t*) allocator.Alloc(kChildStackSize);
278 if (!stack)
279 return false;
280 // clone() needs the top-most address. (scrub just to be safe)
281 stack += kChildStackSize;
282 my_memset(stack - 16, 0, 16);
283
284 ThreadArgument thread_arg;
285 thread_arg.handler = this;
286 thread_arg.pid = getpid();
287 thread_arg.context = &context;
288 thread_arg.context_size = sizeof(context);
289
290 const pid_t child = sys_clone(
291 ThreadEntry, stack, CLONE_FILES | CLONE_FS | CLONE_UNTRACED,
292 &thread_arg, NULL, NULL, NULL);
293 int r, status;
294 do {
295 r = sys_waitpid(child, &status, __WALL);
296 } while (r == -1 && errno == EINTR);
297
298 if (r == -1) {
299 static const char msg[] = "ExceptionHandler::HandleSignal: waitpid failed:";
300 sys_write(2, msg, sizeof(msg) - 1);
301 sys_write(2, strerror(errno), strlen(strerror(errno)));
302 sys_write(2, "\n", 1);
303 }
304
305 bool success = r != -1 && WIFEXITED(status) && WEXITSTATUS(status) == 0;
306
307 if (callback_)
308 success = callback_(dump_path_c_, next_minidump_id_c_,
309 callback_context_, success);
310
311 return success;
312 }
313
314 // This function runs in a compromised context: see the top of the file.
315 // Runs on the cloned process.
316 bool ExceptionHandler::DoDump(pid_t crashing_process, const void* context,
317 size_t context_size) {
318 return google_breakpad::WriteMinidump(
319 next_minidump_path_c_, crashing_process, context, context_size);
320 }
321
322 } // namespace google_breakpad
OLDNEW
« no previous file with comments | « breakpad/linux/exception_handler.h ('k') | breakpad/linux/exception_handler_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698