Bug 662646 - Add API to register memory for inclusion in crash reports (r=ted)

This commit is contained in:
Bill McCloskey 2011-07-07 17:31:01 -07:00
parent 725b795a38
commit feabc1b293
8 changed files with 169 additions and 31 deletions

View File

@ -445,7 +445,8 @@ bool ExceptionHandler::DoDump(pid_t crashing_process, const void* context,
crashing_process,
context,
context_size,
mapping_info_);
mapping_info_,
app_memory_info_);
}
// static
@ -539,4 +540,19 @@ void ExceptionHandler::AddMappingInfo(const std::string& name,
mapping_info_.push_back(mapping);
}
void ExceptionHandler::RegisterAppMemory(void *ptr, size_t length) {
app_memory_info_.push_back(AppMemory(ptr, length));
}
void ExceptionHandler::UnregisterAppMemory(void *ptr) {
for (AppMemoryList::iterator iter = app_memory_info_.begin();
iter != app_memory_info_.end();
++iter) {
if (iter->ptr == ptr) {
app_memory_info_.erase(iter);
return;
}
}
}
} // namespace google_breakpad

View File

@ -219,6 +219,11 @@ class ExceptionHandler {
size_t mapping_size,
size_t file_offset);
// 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:
void Init(const std::string &dump_path,
const int server_fd);
@ -278,6 +283,10 @@ class ExceptionHandler {
// Callers can add extra info about mappings for cases where the
// dumper code cannot extract enough information from /proc/<pid>/maps.
MappingList mapping_info_;
// Callers can request additional memory regions to be included in
// the dump.
AppMemoryList app_memory_info_;
};
} // namespace google_breakpad

View File

@ -418,7 +418,8 @@ class MinidumpWriter {
MinidumpWriter(const char* filename,
pid_t crashing_pid,
const ExceptionHandler::CrashContext* context,
const MappingList& mappings)
const MappingList& mappings,
const AppMemoryList& appmem)
: filename_(filename),
siginfo_(&context->siginfo),
ucontext_(&context->context),
@ -432,14 +433,16 @@ class MinidumpWriter {
crashing_tid_pc_(0),
dumper_(crashing_pid),
memory_blocks_(dumper_.allocator()),
mapping_info_(mappings) {
mapping_info_(mappings),
app_memory_info_(appmem) {
}
// case (2) above
MinidumpWriter(const char* filename,
pid_t pid,
pid_t blame_thread,
const MappingList& mappings)
const MappingList& mappings,
const AppMemoryList& appmem)
: filename_(filename),
siginfo_(NULL), // we fill this in if we find blame_thread
ucontext_(NULL),
@ -448,7 +451,8 @@ class MinidumpWriter {
crashing_tid_pc_(0), // set if we find blame_thread
dumper_(pid),
memory_blocks_(dumper_.allocator()),
mapping_info_(mappings) {
mapping_info_(mappings),
app_memory_info_(appmem) {
}
bool Init() {
@ -515,6 +519,9 @@ class MinidumpWriter {
return false;
dir.CopyIndex(dir_index++, &dirent);
if (!WriteAppMemory())
return false;
if (!WriteMemoryListStream(&dirent))
return false;
dir.CopyIndex(dir_index++, &dirent);
@ -824,6 +831,28 @@ class MinidumpWriter {
return true;
}
bool WriteAppMemory() {
for (AppMemoryList::const_iterator iter = app_memory_info_.begin();
iter != app_memory_info_.end();
++iter) {
uint8_t* data_copy =
(uint8_t*) dumper_.allocator()->Alloc(iter->length);
dumper_.CopyFromProcess(data_copy, crashing_tid_, iter->ptr,
iter->length);
UntypedMDRVA memory(&minidump_writer_);
if (!memory.Allocate(iter->length))
return false;
memory.Copy(data_copy, iter->length);
MDMemoryDescriptor desc;
desc.start_of_memory_range = (uintptr_t)iter->ptr;
desc.memory = memory.location();
memory_blocks_.push_back(desc);
}
return true;
}
static bool ShouldIncludeMapping(const MappingInfo& mapping) {
if (mapping.name[0] == 0 || // we only want modules with filenames.
mapping.offset || // we only want to include one mapping per shared lib.
@ -1362,22 +1391,27 @@ popline:
wasteful_vector<MDMemoryDescriptor> memory_blocks_;
// Additional information about some mappings provided by the caller.
const MappingList& mapping_info_;
// Callers can request additional memory regions to be included in
// the dump.
const AppMemoryList& app_memory_info_;
};
bool WriteMinidump(const char* filename, pid_t crashing_process,
const void* blob, size_t blob_size) {
MappingList m;
return WriteMinidump(filename, crashing_process, blob, blob_size, m);
AppMemoryList a;
return WriteMinidump(filename, crashing_process, blob, blob_size, m, a);
}
bool WriteMinidump(const char* filename, pid_t crashing_process,
const void* blob, size_t blob_size,
const MappingList& mappings) {
const MappingList& mappings,
const AppMemoryList& appmem) {
if (blob_size != sizeof(ExceptionHandler::CrashContext))
return false;
const ExceptionHandler::CrashContext* context =
reinterpret_cast<const ExceptionHandler::CrashContext*>(blob);
MinidumpWriter writer(filename, crashing_process, context, mappings);
MinidumpWriter writer(filename, crashing_process, context, mappings, appmem);
if (!writer.Init())
return false;
return writer.Dump();
@ -1387,7 +1421,8 @@ bool WriteMinidump(const char* filename, pid_t process,
pid_t process_blamed_thread) {
//TODO: support mappings here
MappingList m;
MinidumpWriter writer(filename, process, process_blamed_thread, m);
AppMemoryList a;
MinidumpWriter writer(filename, process, process_blamed_thread, m, a);
if (!writer.Init())
return false;
return writer.Dump();

View File

@ -44,6 +44,16 @@ namespace google_breakpad {
typedef std::pair<struct MappingInfo, u_int8_t[sizeof(MDGUID)]> MappingEntry;
typedef std::list<MappingEntry> MappingList;
// These entries store a list of memory regions that the client wants included
// in the minidump.
struct AppMemory {
AppMemory(void *ptr, size_t length) : ptr(ptr), length(length) {}
void *ptr;
size_t length;
};
typedef std::list<AppMemory> AppMemoryList;
// Write a minidump to the filesystem. This function does not malloc nor use
// libc functions which may. Thus, it can be used in contexts where the state
// of the heap may be corrupt.
@ -65,10 +75,12 @@ bool WriteMinidump(const char* filename, pid_t crashing_process,
bool WriteMinidump(const char* filename, pid_t process,
pid_t process_blamed_thread);
// This overload also allows passing a list of known mappings.
// This overload also allows passing a list of known mappings and
// a list of additional memory regions to be included in the minidump.
bool WriteMinidump(const char* filename, pid_t crashing_process,
const void* blob, size_t blob_size,
const MappingList& mappings);
const MappingList& mappings,
const AppMemoryList& appdata);
} // namespace google_breakpad

View File

@ -133,9 +133,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;
@ -926,13 +924,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;
}
@ -1015,9 +1013,6 @@ bool ExceptionHandler::WriteMinidumpWithExceptionForProcess(
++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.
@ -1042,23 +1037,26 @@ bool ExceptionHandler::WriteMinidumpWithExceptionForProcess(
// 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);
context.finished = false;
callback.CallbackRoutine = MinidumpWriteDumpCallback;
callback.CallbackParam = reinterpret_cast<void*>(&context);
callback_pointer = &callback;
ULONG size = static_cast<ULONG>(end_of_range - base);
app_memory_info_.push_back(AppMemory(base, size));
}
}
MinidumpCallbackContext context;
context.iter = app_memory_info_.begin();
context.end = app_memory_info_.end();
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,
processId,
@ -1066,7 +1064,7 @@ bool ExceptionHandler::WriteMinidumpWithExceptionForProcess(
dump_type_,
exinfo ? &except_info : NULL,
&user_streams,
callback_pointer) == TRUE);
&callback) == TRUE);
CloseHandle(dump_file);
}
@ -1095,4 +1093,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
@ -222,6 +233,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;
@ -422,6 +438,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

View File

@ -1153,6 +1153,32 @@ bool GetAnnotation(const nsACString& key, nsACString& data)
return true;
}
nsresult RegisterAppMemory(void* ptr, size_t length)
{
if (!GetEnabled())
return NS_ERROR_NOT_INITIALIZED;
#if defined(XP_LINUX) || defined(XP_WIN32)
gExceptionHandler->RegisterAppMemory(ptr, length);
return NS_OK;
#else
return NS_ERROR_NOT_IMPLEMENTED;
#endif
}
nsresult UnregisterAppMemory(void* ptr)
{
if (!GetEnabled())
return NS_ERROR_NOT_INITIALIZED;
#if defined(XP_LINUX) || defined(XP_WIN32)
gExceptionHandler->UnregisterAppMemory(ptr);
return NS_OK;
#else
return NS_ERROR_NOT_IMPLEMENTED;
#endif
}
bool GetServerURL(nsACString& aServerURL)
{
if (!gExceptionHandler)
@ -1689,6 +1715,7 @@ OnChildProcessDumpRequested(void* aContext,
// have access to the library mappings.
MappingMap::const_iterator iter =
child_library_mappings.find(aClientInfo->pid_);
google_breakpad::AppMemoryList a;
if (iter == child_library_mappings.end()) {
NS_WARNING("No library mappings found for child, can't write minidump!");
return;
@ -1698,7 +1725,8 @@ OnChildProcessDumpRequested(void* aContext,
aClientInfo->pid_,
aClientInfo->crash_context,
aClientInfo->crash_context_size,
iter->second))
iter->second,
a))
return;
#endif

View File

@ -70,6 +70,10 @@ nsresult SetRestartArgs(int argc, char** argv);
nsresult SetupExtraData(nsILocalFile* aAppDataDirectory,
const nsACString& aBuildID);
// Registers an additional memory region to be included in the minidump
nsresult RegisterAppMemory(void* ptr, size_t length);
nsresult UnregisterAppMemory(void* ptr);
// Functions for working with minidumps and .extras
typedef nsDataHashtable<nsCStringHashKey, nsCString> AnnotationTable;