Allow adding extra memory regions to minidump on linux/windows

A=Bill McCloskey <wmccloskey@mozilla.com> R=ted at https://bugzilla.mozilla.org/show_bug.cgi?id=662646

git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@989 4c0a9323-5329-0410-9bdc-e9ce6186880e
This commit is contained in:
ted.mielczarek 2012-07-19 22:03:39 +00:00
parent 72542b052d
commit 2f56276fbf
10 changed files with 305 additions and 29 deletions

60
src/client/windows/handler/exception_handler.cc Normal file → Executable file
View file

@ -46,9 +46,7 @@ static const int kExceptionHandlerThreadInitialStackSize = 64 * 1024;
// This is passed as the context to the MinidumpWriteDump callback.
typedef struct {
ULONG64 memory_base;
ULONG memory_size;
bool finished;
AppMemoryList::const_iterator iter, end;
} MinidumpCallbackContext;
vector<ExceptionHandler*>* ExceptionHandler::handler_stack_ = NULL;
@ -220,6 +218,9 @@ void ExceptionHandler::Initialize(const wstring& dump_path,
set_dump_path(dump_path);
}
// Reserve one element for the instruction memory
app_memory_info_.push_back(AppMemory(0, 0));
// There is a race condition here. If the first instance has not yet
// initialized the critical section, the second (and later) instances may
// try to use uninitialized critical section object. The feature of multiple
@ -795,9 +796,6 @@ bool ExceptionHandler::WriteMinidumpWithException(
++user_streams.UserStreamCount;
}
MINIDUMP_CALLBACK_INFORMATION callback;
MinidumpCallbackContext context;
MINIDUMP_CALLBACK_INFORMATION* callback_pointer = NULL;
// 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.
@ -822,23 +820,33 @@ bool ExceptionHandler::WriteMinidumpWithException(
// pointer, but settle for whatever's available up to the
// boundaries of the memory region.
const ULONG64 kIPMemorySize = 256;
context.memory_base =
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);
context.memory_size =
static_cast<ULONG>(end_of_range - context.memory_base);
ULONG size = static_cast<ULONG>(end_of_range - base);
context.finished = false;
callback.CallbackRoutine = MinidumpWriteDumpCallback;
callback.CallbackParam = reinterpret_cast<void*>(&context);
callback_pointer = &callback;
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(),
@ -846,7 +854,7 @@ bool ExceptionHandler::WriteMinidumpWithException(
dump_type_,
exinfo ? &except_info : NULL,
&user_streams,
callback_pointer) == TRUE);
&callback) == TRUE);
CloseHandle(dump_file);
}
@ -874,13 +882,13 @@ BOOL CALLBACK ExceptionHandler::MinidumpWriteDumpCallback(
case MemoryCallback: {
MinidumpCallbackContext* callback_context =
reinterpret_cast<MinidumpCallbackContext*>(context);
if (callback_context->finished)
if (callback_context->iter == callback_context->end)
return FALSE;
// Include the specified memory region.
callback_output->MemoryBase = callback_context->memory_base;
callback_output->MemorySize = callback_context->memory_size;
callback_context->finished = true;
callback_output->MemoryBase = callback_context->iter->ptr;
callback_output->MemorySize = callback_context->iter->length;
callback_context->iter++;
return TRUE;
}
@ -924,4 +932,20 @@ void ExceptionHandler::UpdateNextID() {
next_minidump_path_c_ = next_minidump_path_.c_str();
}
void ExceptionHandler::RegisterAppMemory(void *ptr, size_t length) {
app_memory_info_.push_back(AppMemory(reinterpret_cast<ULONG64>(ptr),
static_cast<ULONG>(length)));
}
void ExceptionHandler::UnregisterAppMemory(void *ptr) {
for (AppMemoryList::iterator iter = app_memory_info_.begin();
iter != app_memory_info_.end();
++iter) {
if (iter->ptr == reinterpret_cast<ULONG64>(ptr)) {
app_memory_info_.erase(iter);
return;
}
}
}
} // namespace google_breakpad

View file

@ -67,6 +67,7 @@
#include <string>
#include <vector>
#include <list>
#include "client/windows/common/ipc_protocol.h"
#include "client/windows/crash_generation/crash_generation_client.h"
@ -78,6 +79,16 @@ namespace google_breakpad {
using std::vector;
using std::wstring;
// These entries store a list of memory regions that the client wants included
// in the minidump.
struct AppMemory {
AppMemory(ULONG64 ptr, ULONG length) : ptr(ptr), length(length) {}
ULONG64 ptr;
ULONG length;
};
typedef std::list<AppMemory> AppMemoryList;
class ExceptionHandler {
public:
// A callback function to run before Breakpad performs any substantial
@ -219,6 +230,11 @@ class ExceptionHandler {
// Returns whether out-of-process dump generation is used or not.
bool IsOutOfProcess() const { return crash_generation_client_.get() != NULL; }
// Calling RegisterAppMemory(p, len) causes len bytes starting
// at address p to be copied to the minidump when a crash happens.
void RegisterAppMemory(void *ptr, size_t length);
void UnregisterAppMemory(void *ptr);
private:
friend class AutoExceptionHandler;
@ -404,6 +420,10 @@ class ExceptionHandler {
// to not interfere with debuggers.
bool handle_debug_exceptions_;
// Callers can request additional memory regions to be included in
// the dump.
AppMemoryList app_memory_info_;
// A stack of ExceptionHandler objects that have installed unhandled
// exception filters. This vector is used by HandleException to determine
// which ExceptionHandler object to route an exception to. When an