mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-25 13:51:41 +00:00
Bug 662646 - Add API to register memory for inclusion in crash reports (r=ted)
This commit is contained in:
parent
725b795a38
commit
feabc1b293
@ -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
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user