mirror of
https://github.com/yuzu-emu/breakpad.git
synced 2024-11-28 01:04:19 +01:00
Allow generating minidumps from live processes on Windows.
R=mark at https://breakpad.appspot.com/115002/ git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@1042 4c0a9323-5329-0410-9bdc-e9ce6186880e
This commit is contained in:
parent
61d9b9ff96
commit
6a5ab68d56
@ -738,6 +738,62 @@ bool ExceptionHandler::WriteMinidump(const wstring &dump_path,
|
||||
return handler.WriteMinidump();
|
||||
}
|
||||
|
||||
// static
|
||||
bool ExceptionHandler::WriteMinidumpForChild(HANDLE child,
|
||||
DWORD child_blamed_thread,
|
||||
const wstring& dump_path,
|
||||
MinidumpCallback callback,
|
||||
void* callback_context) {
|
||||
EXCEPTION_RECORD ex;
|
||||
CONTEXT ctx;
|
||||
EXCEPTION_POINTERS exinfo = { NULL, NULL };
|
||||
DWORD last_suspend_count = -1;
|
||||
HANDLE child_thread_handle = OpenThread(THREAD_GET_CONTEXT |
|
||||
THREAD_QUERY_INFORMATION |
|
||||
THREAD_SUSPEND_RESUME,
|
||||
FALSE,
|
||||
child_blamed_thread);
|
||||
// This thread may have died already, so not opening the handle is a
|
||||
// non-fatal error.
|
||||
if (child_thread_handle != NULL) {
|
||||
last_suspend_count = SuspendThread(child_thread_handle);
|
||||
if (last_suspend_count >= 0) {
|
||||
ctx.ContextFlags = CONTEXT_ALL;
|
||||
if (GetThreadContext(child_thread_handle, &ctx)) {
|
||||
memset(&ex, 0, sizeof(ex));
|
||||
ex.ExceptionCode = EXCEPTION_BREAKPOINT;
|
||||
#if defined(_M_IX86)
|
||||
ex.ExceptionAddress = reinterpret_cast<PVOID>(ctx.Eip);
|
||||
#elif defined(_M_X64)
|
||||
ex.ExceptionAddress = reinterpret_cast<PVOID>(ctx.Rip);
|
||||
#endif
|
||||
exinfo.ExceptionRecord = &ex;
|
||||
exinfo.ContextRecord = &ctx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ExceptionHandler handler(dump_path, NULL, callback, callback_context,
|
||||
HANDLER_NONE);
|
||||
bool success = handler.WriteMinidumpWithExceptionForProcess(
|
||||
child_blamed_thread,
|
||||
exinfo.ExceptionRecord ? &exinfo : NULL,
|
||||
NULL, child, false);
|
||||
|
||||
if (last_suspend_count >= 0) {
|
||||
ResumeThread(child_thread_handle);
|
||||
}
|
||||
|
||||
CloseHandle(child_thread_handle);
|
||||
|
||||
if (callback) {
|
||||
success = callback(handler.dump_path_c_, handler.next_minidump_id_c_,
|
||||
callback_context, NULL, NULL, success);
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool ExceptionHandler::WriteMinidumpWithException(
|
||||
DWORD requesting_thread_id,
|
||||
EXCEPTION_POINTERS* exinfo,
|
||||
@ -756,114 +812,11 @@ bool ExceptionHandler::WriteMinidumpWithException(
|
||||
if (IsOutOfProcess()) {
|
||||
success = crash_generation_client_->RequestDump(exinfo, assertion);
|
||||
} else {
|
||||
if (minidump_write_dump_) {
|
||||
HANDLE dump_file = CreateFile(next_minidump_path_c_,
|
||||
GENERIC_WRITE,
|
||||
0, // no sharing
|
||||
NULL,
|
||||
CREATE_NEW, // fail if exists
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
NULL);
|
||||
if (dump_file != INVALID_HANDLE_VALUE) {
|
||||
MINIDUMP_EXCEPTION_INFORMATION except_info;
|
||||
except_info.ThreadId = requesting_thread_id;
|
||||
except_info.ExceptionPointers = exinfo;
|
||||
except_info.ClientPointers = FALSE;
|
||||
|
||||
// Add an MDRawBreakpadInfo stream to the minidump, to provide
|
||||
// additional information about the exception handler to the Breakpad
|
||||
// processor. The information will help the processor determine which
|
||||
// threads are relevant. The Breakpad processor does not require this
|
||||
// information but can function better with Breakpad-generated dumps
|
||||
// when it is present. The native debugger is not harmed by the
|
||||
// presence of this information.
|
||||
MDRawBreakpadInfo breakpad_info;
|
||||
breakpad_info.validity = MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID |
|
||||
MD_BREAKPAD_INFO_VALID_REQUESTING_THREAD_ID;
|
||||
breakpad_info.dump_thread_id = GetCurrentThreadId();
|
||||
breakpad_info.requesting_thread_id = requesting_thread_id;
|
||||
|
||||
// Leave room in user_stream_array for a possible assertion info stream.
|
||||
MINIDUMP_USER_STREAM user_stream_array[2];
|
||||
user_stream_array[0].Type = MD_BREAKPAD_INFO_STREAM;
|
||||
user_stream_array[0].BufferSize = sizeof(breakpad_info);
|
||||
user_stream_array[0].Buffer = &breakpad_info;
|
||||
|
||||
MINIDUMP_USER_STREAM_INFORMATION user_streams;
|
||||
user_streams.UserStreamCount = 1;
|
||||
user_streams.UserStreamArray = user_stream_array;
|
||||
|
||||
if (assertion) {
|
||||
user_stream_array[1].Type = MD_ASSERTION_INFO_STREAM;
|
||||
user_stream_array[1].BufferSize = sizeof(MDRawAssertionInfo);
|
||||
user_stream_array[1].Buffer = assertion;
|
||||
++user_streams.UserStreamCount;
|
||||
}
|
||||
|
||||
// Older versions of DbgHelp.dll don't correctly put the memory around
|
||||
// the faulting instruction pointer into the minidump. This
|
||||
// callback will ensure that it gets included.
|
||||
if (exinfo) {
|
||||
// Find a memory region of 256 bytes centered on the
|
||||
// faulting instruction pointer.
|
||||
const ULONG64 instruction_pointer =
|
||||
#if defined(_M_IX86)
|
||||
exinfo->ContextRecord->Eip;
|
||||
#elif defined(_M_AMD64)
|
||||
exinfo->ContextRecord->Rip;
|
||||
#else
|
||||
#error Unsupported platform
|
||||
#endif
|
||||
|
||||
MEMORY_BASIC_INFORMATION info;
|
||||
if (VirtualQuery(reinterpret_cast<LPCVOID>(instruction_pointer),
|
||||
&info,
|
||||
sizeof(MEMORY_BASIC_INFORMATION)) != 0 &&
|
||||
info.State == MEM_COMMIT) {
|
||||
// Attempt to get 128 bytes before and after the instruction
|
||||
// pointer, but settle for whatever's available up to the
|
||||
// boundaries of the memory region.
|
||||
const ULONG64 kIPMemorySize = 256;
|
||||
ULONG64 base =
|
||||
(std::max)(reinterpret_cast<ULONG64>(info.BaseAddress),
|
||||
instruction_pointer - (kIPMemorySize / 2));
|
||||
ULONG64 end_of_range =
|
||||
(std::min)(instruction_pointer + (kIPMemorySize / 2),
|
||||
reinterpret_cast<ULONG64>(info.BaseAddress)
|
||||
+ info.RegionSize);
|
||||
ULONG size = static_cast<ULONG>(end_of_range - base);
|
||||
|
||||
AppMemory& elt = app_memory_info_.front();
|
||||
elt.ptr = base;
|
||||
elt.length = size;
|
||||
}
|
||||
}
|
||||
|
||||
MinidumpCallbackContext context;
|
||||
context.iter = app_memory_info_.begin();
|
||||
context.end = app_memory_info_.end();
|
||||
|
||||
// Skip the reserved element if there was no instruction memory
|
||||
if (context.iter->ptr == 0) {
|
||||
context.iter++;
|
||||
}
|
||||
|
||||
MINIDUMP_CALLBACK_INFORMATION callback;
|
||||
callback.CallbackRoutine = MinidumpWriteDumpCallback;
|
||||
callback.CallbackParam = reinterpret_cast<void*>(&context);
|
||||
|
||||
// The explicit comparison to TRUE avoids a warning (C4800).
|
||||
success = (minidump_write_dump_(GetCurrentProcess(),
|
||||
GetCurrentProcessId(),
|
||||
dump_file,
|
||||
dump_type_,
|
||||
exinfo ? &except_info : NULL,
|
||||
&user_streams,
|
||||
&callback) == TRUE);
|
||||
|
||||
CloseHandle(dump_file);
|
||||
}
|
||||
}
|
||||
success = WriteMinidumpWithExceptionForProcess(requesting_thread_id,
|
||||
exinfo,
|
||||
assertion,
|
||||
GetCurrentProcess(),
|
||||
true);
|
||||
}
|
||||
|
||||
if (callback_) {
|
||||
@ -917,6 +870,132 @@ BOOL CALLBACK ExceptionHandler::MinidumpWriteDumpCallback(
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
bool ExceptionHandler::WriteMinidumpWithExceptionForProcess(
|
||||
DWORD requesting_thread_id,
|
||||
EXCEPTION_POINTERS* exinfo,
|
||||
MDRawAssertionInfo* assertion,
|
||||
HANDLE process,
|
||||
bool write_requester_stream) {
|
||||
bool success = false;
|
||||
if (minidump_write_dump_) {
|
||||
HANDLE dump_file = CreateFile(next_minidump_path_c_,
|
||||
GENERIC_WRITE,
|
||||
0, // no sharing
|
||||
NULL,
|
||||
CREATE_NEW, // fail if exists
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
NULL);
|
||||
if (dump_file != INVALID_HANDLE_VALUE) {
|
||||
MINIDUMP_EXCEPTION_INFORMATION except_info;
|
||||
except_info.ThreadId = requesting_thread_id;
|
||||
except_info.ExceptionPointers = exinfo;
|
||||
except_info.ClientPointers = FALSE;
|
||||
|
||||
// Leave room in user_stream_array for possible breakpad and
|
||||
// assertion info streams.
|
||||
MINIDUMP_USER_STREAM user_stream_array[2];
|
||||
MINIDUMP_USER_STREAM_INFORMATION user_streams;
|
||||
user_streams.UserStreamCount = 0;
|
||||
user_streams.UserStreamArray = user_stream_array;
|
||||
|
||||
if (write_requester_stream) {
|
||||
// Add an MDRawBreakpadInfo stream to the minidump, to provide
|
||||
// additional information about the exception handler to the Breakpad
|
||||
// processor. The information will help the processor determine which
|
||||
// threads are relevant. The Breakpad processor does not require this
|
||||
// information but can function better with Breakpad-generated dumps
|
||||
// when it is present. The native debugger is not harmed by the
|
||||
// presence of this information.
|
||||
MDRawBreakpadInfo breakpad_info;
|
||||
breakpad_info.validity = MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID |
|
||||
MD_BREAKPAD_INFO_VALID_REQUESTING_THREAD_ID;
|
||||
breakpad_info.dump_thread_id = GetCurrentThreadId();
|
||||
breakpad_info.requesting_thread_id = requesting_thread_id;
|
||||
|
||||
int index = user_streams.UserStreamCount;
|
||||
user_stream_array[index].Type = MD_BREAKPAD_INFO_STREAM;
|
||||
user_stream_array[index].BufferSize = sizeof(breakpad_info);
|
||||
user_stream_array[index].Buffer = &breakpad_info;
|
||||
++user_streams.UserStreamCount;
|
||||
}
|
||||
|
||||
if (assertion) {
|
||||
int index = user_streams.UserStreamCount;
|
||||
user_stream_array[index].Type = MD_ASSERTION_INFO_STREAM;
|
||||
user_stream_array[index].BufferSize = sizeof(MDRawAssertionInfo);
|
||||
user_stream_array[index].Buffer = assertion;
|
||||
++user_streams.UserStreamCount;
|
||||
}
|
||||
|
||||
// Older versions of DbgHelp.dll don't correctly put the memory around
|
||||
// the faulting instruction pointer into the minidump. This
|
||||
// callback will ensure that it gets included.
|
||||
if (exinfo) {
|
||||
// Find a memory region of 256 bytes centered on the
|
||||
// faulting instruction pointer.
|
||||
const ULONG64 instruction_pointer =
|
||||
#if defined(_M_IX86)
|
||||
exinfo->ContextRecord->Eip;
|
||||
#elif defined(_M_AMD64)
|
||||
exinfo->ContextRecord->Rip;
|
||||
#else
|
||||
#error Unsupported platform
|
||||
#endif
|
||||
|
||||
MEMORY_BASIC_INFORMATION info;
|
||||
if (VirtualQueryEx(process,
|
||||
reinterpret_cast<LPCVOID>(instruction_pointer),
|
||||
&info,
|
||||
sizeof(MEMORY_BASIC_INFORMATION)) != 0 &&
|
||||
info.State == MEM_COMMIT) {
|
||||
// Attempt to get 128 bytes before and after the instruction
|
||||
// pointer, but settle for whatever's available up to the
|
||||
// boundaries of the memory region.
|
||||
const ULONG64 kIPMemorySize = 256;
|
||||
ULONG64 base =
|
||||
(std::max)(reinterpret_cast<ULONG64>(info.BaseAddress),
|
||||
instruction_pointer - (kIPMemorySize / 2));
|
||||
ULONG64 end_of_range =
|
||||
(std::min)(instruction_pointer + (kIPMemorySize / 2),
|
||||
reinterpret_cast<ULONG64>(info.BaseAddress)
|
||||
+ info.RegionSize);
|
||||
ULONG size = static_cast<ULONG>(end_of_range - base);
|
||||
|
||||
AppMemory& elt = app_memory_info_.front();
|
||||
elt.ptr = base;
|
||||
elt.length = size;
|
||||
}
|
||||
}
|
||||
|
||||
MinidumpCallbackContext context;
|
||||
context.iter = app_memory_info_.begin();
|
||||
context.end = app_memory_info_.end();
|
||||
|
||||
// Skip the reserved element if there was no instruction memory
|
||||
if (context.iter->ptr == 0) {
|
||||
context.iter++;
|
||||
}
|
||||
|
||||
MINIDUMP_CALLBACK_INFORMATION callback;
|
||||
callback.CallbackRoutine = MinidumpWriteDumpCallback;
|
||||
callback.CallbackParam = reinterpret_cast<void*>(&context);
|
||||
|
||||
// The explicit comparison to TRUE avoids a warning (C4800).
|
||||
success = (minidump_write_dump_(process,
|
||||
GetCurrentProcessId(),
|
||||
dump_file,
|
||||
dump_type_,
|
||||
exinfo ? &except_info : NULL,
|
||||
&user_streams,
|
||||
&callback) == TRUE);
|
||||
|
||||
CloseHandle(dump_file);
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
void ExceptionHandler::UpdateNextID() {
|
||||
assert(uuid_create_);
|
||||
UUID id = {0};
|
||||
|
@ -221,6 +221,17 @@ class ExceptionHandler {
|
||||
static bool WriteMinidump(const wstring &dump_path,
|
||||
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.
|
||||
static bool WriteMinidumpForChild(HANDLE child,
|
||||
DWORD child_blamed_thread,
|
||||
const wstring& dump_path,
|
||||
MinidumpCallback callback,
|
||||
void* callback_context);
|
||||
|
||||
// Get the thread ID of the thread requesting the dump (either the exception
|
||||
// thread or any other thread that called WriteMinidump directly). This
|
||||
// may be useful if you want to include additional thread state in your
|
||||
@ -305,8 +316,9 @@ class ExceptionHandler {
|
||||
bool WriteMinidumpOnHandlerThread(EXCEPTION_POINTERS* exinfo,
|
||||
MDRawAssertionInfo* assertion);
|
||||
|
||||
// This function does the actual writing of a minidump. It is called
|
||||
// on the handler thread. requesting_thread_id is the ID of the thread
|
||||
// This function is called on the handler thread. It calls into
|
||||
// WriteMinidumpWithExceptionForProcess() with a handle to the
|
||||
// current process. requesting_thread_id is the ID of the thread
|
||||
// that requested the dump. If the dump is requested as a result of
|
||||
// an exception, exinfo contains exception information, otherwise,
|
||||
// it is NULL.
|
||||
@ -321,6 +333,20 @@ class ExceptionHandler {
|
||||
const PMINIDUMP_CALLBACK_INPUT callback_input,
|
||||
PMINIDUMP_CALLBACK_OUTPUT callback_output);
|
||||
|
||||
// This function does the actual writing of a minidump. It is
|
||||
// called on the handler thread. requesting_thread_id is the ID of
|
||||
// the thread that requested the dump, if that information is
|
||||
// meaningful. If the dump is requested as a result of an
|
||||
// exception, exinfo contains exception information, otherwise, it
|
||||
// is NULL. process is the one that will be dumped. If
|
||||
// requesting_thread_id is meaningful and should be added to the
|
||||
// minidump, write_requester_stream is |true|.
|
||||
bool WriteMinidumpWithExceptionForProcess(DWORD requesting_thread_id,
|
||||
EXCEPTION_POINTERS* exinfo,
|
||||
MDRawAssertionInfo* assertion,
|
||||
HANDLE process,
|
||||
bool write_requester_stream);
|
||||
|
||||
// Generates a new ID and stores it in next_minidump_id_, and stores the
|
||||
// path of the next minidump to be written in next_minidump_path_.
|
||||
void UpdateNextID();
|
||||
|
Loading…
Reference in New Issue
Block a user