mirror of
https://github.com/yuzu-emu/breakpad.git
synced 2024-12-23 13:15:39 +00:00
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:
parent
2e86dfe705
commit
21d58c7281
|
@ -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) {
|
||||||
|
|
|
@ -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_;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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");
|
||||||
|
|
Loading…
Reference in a new issue