aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorted.mielczarek@gmail.com <ted.mielczarek@gmail.com@4c0a9323-5329-0410-9bdc-e9ce6186880e>2012-09-18 18:51:56 +0000
committerted.mielczarek@gmail.com <ted.mielczarek@gmail.com@4c0a9323-5329-0410-9bdc-e9ce6186880e>2012-09-18 18:51:56 +0000
commit67364c1326644c953caf34c92edc91117c71282c (patch)
tree2d1c902172ba53783488fe4d53bfbc0a381cfd9f
parentAllow generating minidumps from live processes on Windows. (diff)
downloadbreakpad-67364c1326644c953caf34c92edc91117c71282c.tar.xz
Allow generating minidumps from live process on Linux via ExceptionHandler
Original patch by Chris Jones <jones.chris.g@gmail.com> at https://bugzilla.mozilla.org/show_bug.cgi?id=544936 and https://bugzilla.mozilla.org/show_bug.cgi?id=555309 R=mark at https://breakpad.appspot.com/449003/ git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@1043 4c0a9323-5329-0410-9bdc-e9ce6186880e
-rw-r--r--src/client/linux/crash_generation/client_info.h11
-rw-r--r--src/client/linux/crash_generation/crash_generation_server.cc5
-rw-r--r--src/client/linux/handler/exception_handler.cc35
-rw-r--r--src/client/linux/handler/exception_handler.h17
-rw-r--r--src/client/linux/handler/exception_handler_unittest.cc66
-rw-r--r--src/client/linux/minidump_writer/minidump_writer.cc33
-rw-r--r--src/client/linux/minidump_writer/minidump_writer.h8
-rw-r--r--src/google_breakpad/common/minidump_exception_linux.h3
-rw-r--r--src/processor/minidump_processor.cc3
9 files changed, 173 insertions, 8 deletions
diff --git a/src/client/linux/crash_generation/client_info.h b/src/client/linux/crash_generation/client_info.h
index 173b34d8..75fe1a8a 100644
--- a/src/client/linux/crash_generation/client_info.h
+++ b/src/client/linux/crash_generation/client_info.h
@@ -34,7 +34,16 @@ namespace google_breakpad {
class CrashGenerationServer;
-struct ClientInfo {
+class ClientInfo {
+ public:
+ ClientInfo(pid_t pid, CrashGenerationServer* crash_server)
+ : crash_server_(crash_server_),
+ pid_(pid) {}
+
+ CrashGenerationServer* crash_server() const { return crash_server_; }
+ pid_t pid() const { return pid_; }
+
+ private:
CrashGenerationServer* crash_server_;
pid_t pid_;
};
diff --git a/src/client/linux/crash_generation/crash_generation_server.cc b/src/client/linux/crash_generation/crash_generation_server.cc
index c5c59743..ff891353 100644
--- a/src/client/linux/crash_generation/crash_generation_server.cc
+++ b/src/client/linux/crash_generation/crash_generation_server.cc
@@ -396,10 +396,7 @@ CrashGenerationServer::ClientEvent(short revents)
}
if (dump_callback_) {
- ClientInfo info;
-
- info.crash_server_ = this;
- info.pid_ = crashing_pid;
+ ClientInfo info(crashing_pid, this);
dump_callback_(dump_context_, &info, &minidump_filename);
}
diff --git a/src/client/linux/handler/exception_handler.cc b/src/client/linux/handler/exception_handler.cc
index 6f0b8de4..aad2a0e0 100644
--- a/src/client/linux/handler/exception_handler.cc
+++ b/src/client/linux/handler/exception_handler.cc
@@ -529,7 +529,7 @@ bool ExceptionHandler::WriteMinidump() {
static_cast<void>(ftruncate(minidump_descriptor_.fd(), 0));
}
- // Allow ourselves to be dumped.
+ // Allow this process to be dumped.
sys_prctl(PR_SET_DUMPABLE, 1);
CrashContext context;
@@ -543,6 +543,22 @@ bool ExceptionHandler::WriteMinidump() {
#endif
context.tid = sys_gettid();
+ // Add an exception stream to the minidump for better reporting.
+ memset(&context.siginfo, 0, sizeof(context.siginfo));
+ context.siginfo.si_signo = MD_EXCEPTION_CODE_LIN_DUMP_REQUESTED;
+#if defined(__i386__)
+ context.siginfo.si_addr =
+ reinterpret_cast<void*>(context.context.uc_mcontext.gregs[REG_EIP]);
+#elif defined(__x86_64__)
+ context.siginfo.si_addr =
+ reinterpret_cast<void*>(context.context.uc_mcontext.gregs[REG_RIP]);
+#elif defined(__arm__)
+ context.siginfo.si_addr =
+ reinterpret_cast<void*>(context.context.uc_mcontext.arm_pc);
+#else
+#error "This code has not been ported to your platform yet."
+#endif
+
return GenerateDump(&context);
}
@@ -586,4 +602,21 @@ void ExceptionHandler::UnregisterAppMemory(void* ptr) {
}
}
+// static
+bool ExceptionHandler::WriteMinidumpForChild(pid_t child,
+ pid_t child_blamed_thread,
+ const string& dump_path,
+ MinidumpCallback callback,
+ void* callback_context) {
+ // This function is not run in a compromised context.
+ MinidumpDescriptor descriptor(dump_path);
+ descriptor.UpdatePath();
+ if (!google_breakpad::WriteMinidump(descriptor.path(),
+ child,
+ child_blamed_thread))
+ return false;
+
+ return callback ? callback(descriptor, callback_context, true) : true;
+}
+
} // namespace google_breakpad
diff --git a/src/client/linux/handler/exception_handler.h b/src/client/linux/handler/exception_handler.h
index 68953adf..7e6f8649 100644
--- a/src/client/linux/handler/exception_handler.h
+++ b/src/client/linux/handler/exception_handler.h
@@ -167,6 +167,23 @@ class ExceptionHandler {
MinidumpCallback callback,
void* callback_context);
+ // Write a minidump of |child| immediately. This can be used to
+ // capture the execution state of |child| independently of a crash.
+ // Pass a meaningful |child_blamed_thread| to make that thread in
+ // the child process the one from which a crash signature is
+ // extracted.
+ //
+ // WARNING: the return of this function *must* happen before
+ // the code that will eventually reap |child| executes.
+ // Otherwise there's a pernicious race condition in which |child|
+ // exits, is reaped, another process created with its pid, then that
+ // new process dumped.
+ static bool WriteMinidumpForChild(pid_t child,
+ pid_t child_blamed_thread,
+ const string& dump_path,
+ MinidumpCallback callback,
+ void* callback_context);
+
// This structure is passed to minidump_writer.h:WriteMinidump via an opaque
// blob. It shouldn't be needed in any user code.
struct CrashContext {
diff --git a/src/client/linux/handler/exception_handler_unittest.cc b/src/client/linux/handler/exception_handler_unittest.cc
index 341d2fb0..80839730 100644
--- a/src/client/linux/handler/exception_handler_unittest.cc
+++ b/src/client/linux/handler/exception_handler_unittest.cc
@@ -895,6 +895,25 @@ TEST(ExceptionHandlerTest, ExternalDumper) {
unlink(templ.c_str());
}
+TEST(ExceptionHandlerTest, WriteMinidumpExceptionStream) {
+ AutoTempDir temp_dir;
+ ExceptionHandler handler(MinidumpDescriptor(temp_dir.path()), NULL, NULL,
+ NULL, false, -1);
+ ASSERT_TRUE(handler.WriteMinidump());
+
+ string minidump_path = handler.minidump_descriptor().path();
+
+ // Read the minidump and check the exception stream.
+ Minidump minidump(minidump_path);
+ ASSERT_TRUE(minidump.Read());
+ MinidumpException* exception = minidump.GetException();
+ ASSERT_TRUE(exception);
+ const MDRawExceptionStream* raw = exception->exception();
+ ASSERT_TRUE(raw);
+ EXPECT_EQ(MD_EXCEPTION_CODE_LIN_DUMP_REQUESTED,
+ raw->exception_record.exception_code);
+}
+
TEST(ExceptionHandlerTest, GenerateMultipleDumpsWithFD) {
AutoTempDir temp_dir;
std::string path;
@@ -1021,3 +1040,50 @@ TEST(ExceptionHandlerTest, AdditionalMemoryRemove) {
delete[] memory;
}
+
+static bool SimpleCallback(const MinidumpDescriptor& descriptor,
+ void* context,
+ bool succeeded) {
+ string* filename = reinterpret_cast<string*>(context);
+ *filename = descriptor.path();
+ return true;
+}
+
+TEST(ExceptionHandlerTest, WriteMinidumpForChild) {
+ int fds[2];
+ ASSERT_NE(-1, pipe(fds));
+
+ const pid_t child = fork();
+ if (child == 0) {
+ close(fds[1]);
+ char b;
+ HANDLE_EINTR(read(fds[0], &b, sizeof(b)));
+ close(fds[0]);
+ syscall(__NR_exit);
+ }
+ close(fds[0]);
+
+ AutoTempDir temp_dir;
+ string minidump_filename;
+ ASSERT_TRUE(
+ ExceptionHandler::WriteMinidumpForChild(child, child,
+ temp_dir.path(), SimpleCallback,
+ (void*)&minidump_filename));
+
+ Minidump minidump(minidump_filename);
+ ASSERT_TRUE(minidump.Read());
+ // Check that the crashing thread is the main thread of |child|
+ MinidumpException* exception = minidump.GetException();
+ ASSERT_TRUE(exception);
+ u_int32_t thread_id;
+ ASSERT_TRUE(exception->GetThreadID(&thread_id));
+ EXPECT_EQ(child, thread_id);
+
+ const MDRawExceptionStream* raw = exception->exception();
+ ASSERT_TRUE(raw);
+ EXPECT_EQ(MD_EXCEPTION_CODE_LIN_DUMP_REQUESTED,
+ raw->exception_record.exception_code);
+
+ close(fds[1]);
+ unlink(minidump_filename.c_str());
+}
diff --git a/src/client/linux/minidump_writer/minidump_writer.cc b/src/client/linux/minidump_writer/minidump_writer.cc
index b081b298..02055640 100644
--- a/src/client/linux/minidump_writer/minidump_writer.cc
+++ b/src/client/linux/minidump_writer/minidump_writer.cc
@@ -686,6 +686,7 @@ class MinidumpWriter {
// signal handler with the alternative stack, which would be deeply
// unhelpful.
if (static_cast<pid_t>(thread.thread_id) == GetCrashThread() &&
+ ucontext_ &&
!dumper_->IsPostMortem()) {
const void* stack;
size_t stack_len;
@@ -776,8 +777,13 @@ class MinidumpWriter {
PopSeccompStackFrame(cpu.get(), thread, stack_copy);
thread.thread_context = cpu.location();
if (dumper_->threads()[i] == GetCrashThread()) {
- assert(dumper_->IsPostMortem());
crashing_thread_context_ = cpu.location();
+ if (!dumper_->IsPostMortem()) {
+ // This is the crashing thread of a live process, but
+ // no context was provided, so set the crash address
+ // while the instruction pointer is already here.
+ dumper_->set_crash_address(GetInstructionPointer(info));
+ }
}
}
@@ -1092,6 +1098,10 @@ class MinidumpWriter {
uintptr_t GetInstructionPointer() {
return ucontext_->uc_mcontext.gregs[REG_EIP];
}
+
+ uintptr_t GetInstructionPointer(const ThreadInfo& info) {
+ return info.regs.eip;
+ }
#elif defined(__x86_64)
uintptr_t GetStackPointer() {
return ucontext_->uc_mcontext.gregs[REG_RSP];
@@ -1100,6 +1110,10 @@ class MinidumpWriter {
uintptr_t GetInstructionPointer() {
return ucontext_->uc_mcontext.gregs[REG_RIP];
}
+
+ uintptr_t GetInstructionPointer(const ThreadInfo& info) {
+ return info.regs.rip;
+ }
#elif defined(__ARM_EABI__)
uintptr_t GetStackPointer() {
return ucontext_->uc_mcontext.arm_sp;
@@ -1108,6 +1122,10 @@ class MinidumpWriter {
uintptr_t GetInstructionPointer() {
return ucontext_->uc_mcontext.arm_pc;
}
+
+ uintptr_t GetInstructionPointer(const ThreadInfo& info) {
+ return info.regs.uregs[15];
+ }
#else
#error "This code has not been ported to your platform yet."
#endif
@@ -1435,6 +1453,19 @@ bool WriteMinidump(int minidump_fd, pid_t crashing_process,
MappingList(), AppMemoryList());
}
+bool WriteMinidump(const char* minidump_path, pid_t process,
+ pid_t process_blamed_thread) {
+ LinuxPtraceDumper dumper(process);
+ // MinidumpWriter will set crash address
+ dumper.set_crash_signal(MD_EXCEPTION_CODE_LIN_DUMP_REQUESTED);
+ dumper.set_crash_thread(process_blamed_thread);
+ MinidumpWriter writer(minidump_path, -1, NULL, MappingList(),
+ AppMemoryList(), &dumper);
+ if (!writer.Init())
+ return false;
+ return writer.Dump();
+}
+
bool WriteMinidump(const char* minidump_path, pid_t crashing_process,
const void* blob, size_t blob_size,
const MappingList& mappings,
diff --git a/src/client/linux/minidump_writer/minidump_writer.h b/src/client/linux/minidump_writer/minidump_writer.h
index 1370d938..28bfbdaf 100644
--- a/src/client/linux/minidump_writer/minidump_writer.h
+++ b/src/client/linux/minidump_writer/minidump_writer.h
@@ -83,6 +83,14 @@ bool WriteMinidump(const char* minidump_path, pid_t crashing_process,
bool WriteMinidump(int minidump_fd, pid_t crashing_process,
const void* blob, size_t blob_size);
+// Alternate form of WriteMinidump() that works with processes that
+// are not expected to have crashed. If |process_blamed_thread| is
+// meaningful, it will be the one from which a crash signature is
+// extracted. It is not expected that this function will be called
+// from a compromised context, but it is safe to do so.
+bool WriteMinidump(const char* minidump_path, pid_t process,
+ pid_t process_blamed_thread);
+
// These overloads also allow passing a list of known mappings and
// a list of additional memory regions to be included in the minidump.
bool WriteMinidump(const char* minidump_path, pid_t crashing_process,
diff --git a/src/google_breakpad/common/minidump_exception_linux.h b/src/google_breakpad/common/minidump_exception_linux.h
index d52c7519..c8c87568 100644
--- a/src/google_breakpad/common/minidump_exception_linux.h
+++ b/src/google_breakpad/common/minidump_exception_linux.h
@@ -79,7 +79,8 @@ typedef enum {
MD_EXCEPTION_CODE_LIN_SIGWINCH = 28, /* Window size change (4.3 BSD, Sun) */
MD_EXCEPTION_CODE_LIN_SIGIO = 29, /* I/O now possible (4.2 BSD) */
MD_EXCEPTION_CODE_LIN_SIGPWR = 30, /* Power failure restart (System V) */
- MD_EXCEPTION_CODE_LIN_SIGSYS = 31 /* Bad system call */
+ MD_EXCEPTION_CODE_LIN_SIGSYS = 31, /* Bad system call */
+ MD_EXCEPTION_CODE_LIN_DUMP_REQUESTED = -1 /* No exception, dump requested */
} MDExceptionCodeLinux;
#endif /* GOOGLE_BREAKPAD_COMMON_MINIDUMP_EXCEPTION_LINUX_H__ */
diff --git a/src/processor/minidump_processor.cc b/src/processor/minidump_processor.cc
index 6dbe81ec..0f9d8d75 100644
--- a/src/processor/minidump_processor.cc
+++ b/src/processor/minidump_processor.cc
@@ -939,6 +939,9 @@ string MinidumpProcessor::GetCrashReason(Minidump *dump, u_int64_t *address) {
case MD_EXCEPTION_CODE_LIN_SIGSYS:
reason = "SIGSYS";
break;
+ case MD_EXCEPTION_CODE_LIN_DUMP_REQUESTED:
+ reason = "DUMP_REQUESTED";
+ break;
default:
BPLOG(INFO) << "Unknown exception reason " << reason;
break;