Issue 182: linux handler doesn't have sigcontext if called from a previous signal handler. r=mento / Liu Li

git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@188 4c0a9323-5329-0410-9bdc-e9ce6186880e
This commit is contained in:
ted.mielczarek 2007-06-11 16:01:46 +00:00
parent 2e86dfe705
commit 21d58c7281
7 changed files with 87 additions and 25 deletions

View file

@ -118,7 +118,7 @@ ExceptionHandler::~ExceptionHandler() {
} }
bool ExceptionHandler::WriteMinidump() { bool ExceptionHandler::WriteMinidump() {
return InternalWriteMinidump(0, NULL); return InternalWriteMinidump(0, 0, NULL);
} }
// static // static
@ -127,7 +127,7 @@ bool ExceptionHandler::WriteMinidump(const string &dump_path,
void *callback_context) { void *callback_context) {
ExceptionHandler handler(dump_path, NULL, callback, ExceptionHandler handler(dump_path, NULL, callback,
callback_context, false); callback_context, false);
return handler.InternalWriteMinidump(0, NULL); return handler.InternalWriteMinidump(0, 0, NULL);
} }
void ExceptionHandler::SetupHandler() { void ExceptionHandler::SetupHandler() {
@ -176,8 +176,15 @@ void ExceptionHandler::HandleException(int signo) {
// the signal handler frame as value parameter. For some reasons, the // the signal handler frame as value parameter. For some reasons, the
// prototype of the handler doesn't declare this information as parameter, we // prototype of the handler doesn't declare this information as parameter, we
// will do it by hand. It is the second parameter above the signal number. // will do it by hand. It is the second parameter above the signal number.
const struct sigcontext *sig_ctx = // However, if we are being called by another signal handler passing the
reinterpret_cast<const struct sigcontext *>(&signo + 1); // signal up the chain, then we may not have this random extra parameter,
// so we may have to walk the stack to find it. We do the actual work
// on another thread, where it's a little safer, but we want the ebp
// from this frame to find it.
uintptr_t current_ebp = 0;
asm volatile ("movl %%ebp, %0"
:"=m"(current_ebp));
pthread_mutex_lock(&handler_stack_mutex_); pthread_mutex_lock(&handler_stack_mutex_);
ExceptionHandler *current_handler = ExceptionHandler *current_handler =
handler_stack_->at(handler_stack_->size() - ++handler_stack_index_); handler_stack_->at(handler_stack_->size() - ++handler_stack_index_);
@ -185,7 +192,9 @@ void ExceptionHandler::HandleException(int signo) {
// Restore original handler. // Restore original handler.
current_handler->TeardownHandler(signo); current_handler->TeardownHandler(signo);
if (current_handler->InternalWriteMinidump(signo, sig_ctx)) {
struct sigcontext *sig_ctx = NULL;
if (current_handler->InternalWriteMinidump(signo, current_ebp, &sig_ctx)) {
// Fully handled this exception, safe to exit. // Fully handled this exception, safe to exit.
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} else { } else {
@ -194,7 +203,7 @@ void ExceptionHandler::HandleException(int signo) {
typedef void (*SignalHandler)(int signo, struct sigcontext); typedef void (*SignalHandler)(int signo, struct sigcontext);
SignalHandler old_handler = SignalHandler old_handler =
reinterpret_cast<SignalHandler>(current_handler->old_handlers_[signo]); reinterpret_cast<SignalHandler>(current_handler->old_handlers_[signo]);
if (old_handler != NULL) if (old_handler != NULL && sig_ctx != NULL)
old_handler(signo, *sig_ctx); old_handler(signo, *sig_ctx);
} }
@ -212,7 +221,8 @@ void ExceptionHandler::HandleException(int signo) {
} }
bool ExceptionHandler::InternalWriteMinidump(int signo, bool ExceptionHandler::InternalWriteMinidump(int signo,
const struct sigcontext *sig_ctx) { uintptr_t sighandler_ebp,
struct sigcontext **sig_ctx) {
if (filter_ && !filter_(callback_context_)) if (filter_ && !filter_(callback_context_))
return false; return false;
@ -239,7 +249,7 @@ bool ExceptionHandler::InternalWriteMinidump(int signo,
} }
success = minidump_generator_.WriteMinidumpToFile( success = minidump_generator_.WriteMinidumpToFile(
minidump_path, signo, sig_ctx); minidump_path, signo, sighandler_ebp, sig_ctx);
// Unblock the signals. // Unblock the signals.
if (blocked) { if (blocked) {

View file

@ -132,7 +132,7 @@ class ExceptionHandler {
// execution state independently of a crash. Returns true on success. // execution state independently of a crash. Returns true on success.
bool WriteMinidump(); bool WriteMinidump();
// Convenience form of WriteMinidump which does not require an // Convenience form of WriteMinidump which does not require an
// ExceptionHandler instance. // ExceptionHandler instance.
static bool WriteMinidump(const string &dump_path, static bool WriteMinidump(const string &dump_path,
MinidumpCallback callback, MinidumpCallback callback,
@ -151,7 +151,14 @@ class ExceptionHandler {
// Signal handler. // Signal handler.
static void HandleException(int signo); static void HandleException(int signo);
bool InternalWriteMinidump(int signo, const struct sigcontext *sig_ctx); // If called from a signal handler, sighandler_ebp is the ebp of
// that signal handler's frame, and sig_ctx is an out parameter
// that will be set to point at the sigcontext that was placed
// on the stack by the kernel. You can pass zero and NULL
// for the second and third parameters if you are not calling
// this from a signal handler.
bool InternalWriteMinidump(int signo, uintptr_t sighandler_ebp,
struct sigcontext **sig_ctx);
private: private:
FilterCallback filter_; FilterCallback filter_;

View file

@ -381,4 +381,30 @@ bool LinuxThread::IsAddressMapped(uintptr_t address) const {
return addr.is_mapped; return addr.is_mapped;
} }
bool LinuxThread::FindSigContext(uintptr_t sighandler_ebp,
struct sigcontext **sig_ctx) {
uintptr_t previous_ebp;
const int MAX_STACK_DEPTH = 10;
int depth_counter = 0;
do {
// We're looking for a |struct sigcontext| as the second parameter
// to a signal handler function call. Luckily, the sigcontext
// has an ebp member which should match the ebp pointed to
// by the ebp of the signal handler frame.
previous_ebp = reinterpret_cast<uintptr_t>(GetNextFrame(
reinterpret_cast<void**>(sighandler_ebp)));
// The stack looks like this:
// | previous ebp | previous eip | first param | second param |,
// so we need to offset by 3 to get to the second parameter.
*sig_ctx = reinterpret_cast<struct sigcontext*>(sighandler_ebp +
3 * sizeof(uintptr_t));
sighandler_ebp = previous_ebp;
depth_counter++;
} while(previous_ebp != (*sig_ctx)->ebp && sighandler_ebp != 0 &&
IsAddressMapped(sighandler_ebp) && depth_counter < MAX_STACK_DEPTH);
return previous_ebp == (*sig_ctx)->ebp && previous_ebp != 0;
}
} // namespace google_breakpad } // namespace google_breakpad

View file

@ -171,7 +171,10 @@ class LinuxThread {
int ListModules(CallbackParam<ModuleCallback> *callback_param) const; int ListModules(CallbackParam<ModuleCallback> *callback_param) const;
// Get the bottom of the stack from ebp. // Get the bottom of the stack from ebp.
uintptr_t GetThreadStackBottom(uintptr_t current_esp) const; uintptr_t GetThreadStackBottom(uintptr_t current_ebp) const;
// Finds a sigcontext on the stack given the ebp of our signal handler.
bool FindSigContext(uintptr_t sighandler_ebp, struct sigcontext **sig_ctx);
private: private:
// This callback will run when a new thread has been found. // This callback will run when a new thread has been found.

View file

@ -78,8 +78,14 @@ struct WriterArgument {
// Signal number when crash happed. Can be 0 if this is a requested dump. // Signal number when crash happed. Can be 0 if this is a requested dump.
int signo; int signo;
// Signal contex when crash happed. Can be NULL if this is a requested dump. // The ebp of the signal handler frame. Can be zero if this
const struct sigcontext *sig_ctx; // is a requested dump.
uintptr_t sighandler_ebp;
// Signal context when crash happed. Can be NULL if this is a requested dump.
// This is actually an out parameter, but it will be filled in at the start
// of the writer thread.
struct sigcontext *sig_ctx;
// Used to get information about the threads. // Used to get information about the threads.
LinuxThread *thread_lister; LinuxThread *thread_lister;
@ -272,10 +278,10 @@ bool WriteCrashedThreadStream(MinidumpFileWriter *minidump_writer,
UntypedMDRVA memory(minidump_writer); UntypedMDRVA memory(minidump_writer);
if (!WriteThreadStack(writer_args->sig_ctx->ebp, if (!WriteThreadStack(writer_args->sig_ctx->ebp,
writer_args->sig_ctx->esp, writer_args->sig_ctx->esp,
writer_args->thread_lister, writer_args->thread_lister,
&memory, &memory,
&thread->stack)) &thread->stack))
return false; return false;
TypedMDRVA<MDRawContextX86> context(minidump_writer); TypedMDRVA<MDRawContextX86> context(minidump_writer);
@ -714,12 +720,15 @@ int Write(void *argument) {
if (!writer_args->thread_lister->SuspendAllThreads()) if (!writer_args->thread_lister->SuspendAllThreads())
return -1; return -1;
if (writer_args->sig_ctx != NULL) { if (writer_args->sighandler_ebp != 0 &&
writer_args->thread_lister->FindSigContext(writer_args->sighandler_ebp,
&writer_args->sig_ctx)) {
writer_args->crashed_stack_bottom = writer_args->crashed_stack_bottom =
writer_args->thread_lister->GetThreadStackBottom(writer_args->sig_ctx->ebp); writer_args->thread_lister->GetThreadStackBottom(
writer_args->sig_ctx->ebp);
int crashed_pid = FindCrashingThread(writer_args->crashed_stack_bottom, int crashed_pid = FindCrashingThread(writer_args->crashed_stack_bottom,
writer_args->requester_pid, writer_args->requester_pid,
writer_args->thread_lister); writer_args->thread_lister);
if (crashed_pid > 0) if (crashed_pid > 0)
writer_args->crashed_pid = crashed_pid; writer_args->crashed_pid = crashed_pid;
} }
@ -769,7 +778,8 @@ void MinidumpGenerator::AllocateStack() {
bool MinidumpGenerator::WriteMinidumpToFile(const char *file_pathname, bool MinidumpGenerator::WriteMinidumpToFile(const char *file_pathname,
int signo, int signo,
const struct sigcontext *sig_ctx) const { uintptr_t sighandler_ebp,
struct sigcontext **sig_ctx) const {
assert(file_pathname != NULL); assert(file_pathname != NULL);
assert(stack_ != NULL); assert(stack_ != NULL);
@ -786,12 +796,15 @@ bool MinidumpGenerator::WriteMinidumpToFile(const char *file_pathname,
argument.requester_pid = getpid(); argument.requester_pid = getpid();
argument.crashed_pid = getpid(); argument.crashed_pid = getpid();
argument.signo = signo; argument.signo = signo;
argument.sig_ctx = sig_ctx; argument.sighandler_ebp = sighandler_ebp;
argument.sig_ctx = NULL;
int cloned_pid = clone(Write, stack_.get() + kStackSize, int cloned_pid = clone(Write, stack_.get() + kStackSize,
CLONE_VM | CLONE_FILES | CLONE_FS | CLONE_UNTRACED, CLONE_VM | CLONE_FILES | CLONE_FS | CLONE_UNTRACED,
(void*)&argument); (void*)&argument);
waitpid(cloned_pid, NULL, __WALL); waitpid(cloned_pid, NULL, __WALL);
if (sig_ctx != NULL)
*sig_ctx = argument.sig_ctx;
return true; return true;
} }

View file

@ -32,6 +32,8 @@
#ifndef CLIENT_LINUX_HANDLER_MINIDUMP_GENERATOR_H__ #ifndef CLIENT_LINUX_HANDLER_MINIDUMP_GENERATOR_H__
#define CLIENT_LINUX_HANDLER_MINIDUMP_GENERATOR_H__ #define CLIENT_LINUX_HANDLER_MINIDUMP_GENERATOR_H__
#include <stdint.h>
#include "google_breakpad/common/breakpad_types.h" #include "google_breakpad/common/breakpad_types.h"
#include "processor/scoped_ptr.h" #include "processor/scoped_ptr.h"
@ -54,7 +56,8 @@ class MinidumpGenerator {
// Write minidump. // Write minidump.
bool WriteMinidumpToFile(const char *file_pathname, bool WriteMinidumpToFile(const char *file_pathname,
int signo, int signo,
const struct sigcontext *sig_ctx) const; uintptr_t sighandler_ebp,
struct sigcontext **sig_ctx) const;
private: private:
// Allocate memory for stack. // Allocate memory for stack.
void AllocateStack(); void AllocateStack();

View file

@ -77,7 +77,7 @@ static void CreateThread(int num) {
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
CreateThread(10); CreateThread(10);
google_breakpad::MinidumpGenerator mg; google_breakpad::MinidumpGenerator mg;
if (mg.WriteMinidumpToFile("minidump_test.out", -1, NULL)) if (mg.WriteMinidumpToFile("minidump_test.out", -1, 0, NULL))
printf("Succeeded written minidump\n"); printf("Succeeded written minidump\n");
else else
printf("Failed to write minidump\n"); printf("Failed to write minidump\n");