bug 599301 - Make Breakpad include memory around instruction pointer in minidumps on older versions of Windows. r=mento, a=beltzner
--- a/toolkit/crashreporter/google-breakpad/src/client/windows/handler/Makefile.in
+++ b/toolkit/crashreporter/google-breakpad/src/client/windows/handler/Makefile.in
@@ -41,17 +41,17 @@ VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
MODULE = handler
LIBRARY_NAME = exception_handler_s
XPI_NAME = crashreporter
LOCAL_INCLUDES = -I$(topsrcdir)/toolkit/crashreporter/google-breakpad/src
-DEFINES += -DUNICODE -D_UNICODE -DBREAKPAD_NO_TERMINATE_THREAD
+DEFINES += -DUNICODE -D_UNICODE -DBREAKPAD_NO_TERMINATE_THREAD -DNOMINMAX
CPPSRCS = \
exception_handler.cc \
$(NULL)
# need static lib
FORCE_STATIC_LIB = 1
--- a/toolkit/crashreporter/google-breakpad/src/client/windows/handler/exception_handler.cc
+++ b/toolkit/crashreporter/google-breakpad/src/client/windows/handler/exception_handler.cc
@@ -28,16 +28,18 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assert.h>
#include <ObjBase.h>
#include <psapi.h>
#include <stdio.h>
#include <winternl.h>
+#include <algorithm>
+
#include "common/windows/string_utils-inl.h"
#include "client/windows/common/ipc_protocol.h"
#include "client/windows/handler/exception_handler.h"
#include "common/windows/guid_string.h"
typedef VOID (WINAPI *RtlCaptureContextPtr) (PCONTEXT pContextRecord);
@@ -124,16 +126,23 @@ DWORD GetProcId(HANDLE process) {
} // namespace
namespace google_breakpad {
static const int kWaitForHandlerThreadMs = 60000;
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;
+} MinidumpCallbackContext;
+
vector<ExceptionHandler*>* ExceptionHandler::handler_stack_ = NULL;
LONG ExceptionHandler::handler_stack_index_ = 0;
CRITICAL_SECTION ExceptionHandler::handler_stack_critical_section_;
volatile LONG ExceptionHandler::instance_count_ = 0;
ExceptionHandler::ExceptionHandler(const wstring& dump_path,
FilterCallback filter,
MinidumpCallback callback,
@@ -903,16 +912,55 @@ bool ExceptionHandler::WriteMinidumpWith
// id so they are not known to the client.
success = callback_(dump_path_c_, next_minidump_id_c_, callback_context_,
exinfo, assertion, success);
}
return success;
}
+// static
+BOOL CALLBACK ExceptionHandler::MinidumpWriteDumpCallback(
+ PVOID context,
+ const PMINIDUMP_CALLBACK_INPUT callback_input,
+ PMINIDUMP_CALLBACK_OUTPUT callback_output) {
+ switch (callback_input->CallbackType) {
+ case MemoryCallback: {
+ MinidumpCallbackContext* callback_context =
+ reinterpret_cast<MinidumpCallbackContext*>(context);
+ if (callback_context->finished)
+ 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;
+ return TRUE;
+ }
+
+ // Include all modules.
+ case IncludeModuleCallback:
+ case ModuleCallback:
+ return TRUE;
+
+ // Include all threads.
+ case IncludeThreadCallback:
+ case ThreadCallback:
+ return TRUE;
+
+ // Stop receiving cancel callbacks.
+ case CancelCallback:
+ callback_output->CheckCancel = FALSE;
+ callback_output->Cancel = FALSE;
+ return TRUE;
+ }
+ // Ignore other callback types.
+ return FALSE;
+}
+
bool ExceptionHandler::WriteMinidumpWithExceptionForProcess(
DWORD requesting_thread_id,
EXCEPTION_POINTERS* exinfo,
MDRawAssertionInfo* assertion,
HANDLE process,
DWORD processId,
bool write_requester_stream) {
bool success = false;
@@ -962,24 +1010,68 @@ bool ExceptionHandler::WriteMinidumpWith
if (assertion) {
int idx = user_streams.UserStreamCount;
user_stream_array[idx].Type = MD_ASSERTION_INFO_STREAM;
user_stream_array[idx].BufferSize = sizeof(MDRawAssertionInfo);
user_stream_array[idx].Buffer = assertion;
++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.
+ 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;
+ context.memory_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;
+ }
+ }
+
// The explicit comparison to TRUE avoids a warning (C4800).
success = (minidump_write_dump_(process,
processId,
dump_file,
dump_type_,
exinfo ? &except_info : NULL,
&user_streams,
- NULL) == TRUE);
+ callback_pointer) == TRUE);
CloseHandle(dump_file);
}
}
return success;
}
--- a/toolkit/crashreporter/google-breakpad/src/client/windows/handler/exception_handler.h
+++ b/toolkit/crashreporter/google-breakpad/src/client/windows/handler/exception_handler.h
@@ -290,16 +290,23 @@ class ExceptionHandler {
// 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.
bool WriteMinidumpWithException(DWORD requesting_thread_id,
EXCEPTION_POINTERS* exinfo,
MDRawAssertionInfo* assertion);
+ // This function is used as a callback when calling MinidumpWriteDump,
+ // in order to add additional memory regions to the dump.
+ static BOOL CALLBACK MinidumpWriteDumpCallback(
+ PVOID context,
+ 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|.
--- a/toolkit/crashreporter/google-breakpad/src/client/windows/unittests/exception_handler_death_test.cc
+++ b/toolkit/crashreporter/google-breakpad/src/client/windows/unittests/exception_handler_death_test.cc
@@ -28,21 +28,29 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <windows.h>
#include <dbghelp.h>
#include <strsafe.h>
#include <objbase.h>
#include <shellapi.h>
+#include <string>
+
#include "../../../breakpad_googletest_includes.h"
+#include "../../../../common/windows/string_utils-inl.h"
#include "../crash_generation/crash_generation_server.h"
#include "../handler/exception_handler.h"
+#include "../../../../google_breakpad/processor/minidump.h"
namespace {
+
+using std::wstring;
+using namespace google_breakpad;
+
const wchar_t kPipeName[] = L"\\\\.\\pipe\\BreakpadCrashTest\\TestCaseServer";
const char kSuccessIndicator[] = "success";
const char kFailureIndicator[] = "failure";
// Utility function to test for a path's existence.
BOOL DoesPathExist(const TCHAR *path_name);
class ExceptionHandlerDeathTest : public ::testing::Test {
@@ -60,18 +68,18 @@ class ExceptionHandlerDeathTest : public
void ExceptionHandlerDeathTest::SetUp() {
const ::testing::TestInfo* const test_info =
::testing::UnitTest::GetInstance()->current_test_info();
TCHAR temp_path[MAX_PATH] = { '\0' };
TCHAR test_name_wide[MAX_PATH] = { '\0' };
// We want the temporary directory to be what the OS returns
// to us, + the test case name.
GetTempPath(MAX_PATH, temp_path);
- // THe test case name is exposed to use as a c-style string,
- // But we might be working in UNICODE here on Windows.
+ // The test case name is exposed as a c-style string,
+ // convert it to a wchar_t string.
int dwRet = MultiByteToWideChar(CP_ACP, 0, test_info->name(),
strlen(test_info->name()),
test_name_wide,
MAX_PATH);
if (!dwRet) {
assert(false);
}
StringCchPrintfW(temp_path_, MAX_PATH, L"%s%s", temp_path, test_name_wide);
@@ -207,9 +215,315 @@ TEST_F(ExceptionHandlerDeathTest, PureVi
ExceptionHandler::HANDLER_PURECALL);
// Disable the message box for assertions
_CrtSetReportMode(_CRT_ASSERT, 0);
// Calls a pure virtual function.
EXPECT_EXIT(DoCrashPureVirtualCall(), ::testing::ExitedWithCode(0), "");
}
+
+wstring find_minidump_in_directory(const wstring &directory) {
+ wstring search_path = directory + L"\\*";
+ WIN32_FIND_DATA find_data;
+ HANDLE find_handle = FindFirstFileW(search_path.c_str(), &find_data);
+ if (find_handle == INVALID_HANDLE_VALUE)
+ return wstring();
+
+ wstring filename;
+ do {
+ const wchar_t extension[] = L".dmp";
+ const int extension_length = sizeof(extension) / sizeof(extension[0]) - 1;
+ const int filename_length = wcslen(find_data.cFileName);
+ if (filename_length > extension_length &&
+ wcsncmp(extension,
+ find_data.cFileName + filename_length - extension_length,
+ extension_length) == 0) {
+ filename = directory + L"\\" + find_data.cFileName;
+ break;
+ }
+ } while(FindNextFile(find_handle, &find_data));
+ FindClose(find_handle);
+ return filename;
}
+
+TEST_F(ExceptionHandlerDeathTest, InstructionPointerMemory) {
+ ASSERT_TRUE(DoesPathExist(temp_path_));
+ google_breakpad::ExceptionHandler *exc =
+ new google_breakpad::ExceptionHandler(
+ temp_path_, NULL, NULL, NULL,
+ google_breakpad::ExceptionHandler::HANDLER_ALL);
+
+ // Get some executable memory.
+ const u_int32_t kMemorySize = 256; // bytes
+ const int kOffset = kMemorySize / 2;
+ // This crashes with SIGILL on x86/x86-64/arm.
+ const unsigned char instructions[] = { 0xff, 0xff, 0xff, 0xff };
+ char* memory = reinterpret_cast<char*>(VirtualAlloc(NULL,
+ kMemorySize,
+ MEM_COMMIT | MEM_RESERVE,
+ PAGE_EXECUTE_READWRITE));
+ ASSERT_TRUE(memory);
+
+ // Write some instructions that will crash. Put them
+ // in the middle of the block of memory, because the
+ // minidump should contain 128 bytes on either side of the
+ // instruction pointer.
+ memcpy(memory + kOffset, instructions, sizeof(instructions));
+
+ // Now execute the instructions, which should crash.
+ typedef void (*void_function)(void);
+ void_function memory_function =
+ reinterpret_cast<void_function>(memory + kOffset);
+ ASSERT_DEATH(memory_function(), "");
+
+ // free the memory.
+ VirtualFree(memory, 0, MEM_RELEASE);
+
+ // Verify that the resulting minidump contains the memory around the IP
+ wstring minidump_filename_wide = find_minidump_in_directory(temp_path_);
+ ASSERT_FALSE(minidump_filename_wide.empty());
+ string minidump_filename;
+ ASSERT_TRUE(WindowsStringUtils::safe_wcstombs(minidump_filename_wide,
+ &minidump_filename));
+
+ // Read the minidump. Locate the exception record and the
+ // memory list, and then ensure that there is a memory region
+ // in the memory list that covers the instruction pointer from
+ // the exception record.
+ {
+ Minidump minidump(minidump_filename);
+ ASSERT_TRUE(minidump.Read());
+
+ MinidumpException* exception = minidump.GetException();
+ MinidumpMemoryList* memory_list = minidump.GetMemoryList();
+ ASSERT_TRUE(exception);
+ ASSERT_TRUE(memory_list);
+ ASSERT_LT((unsigned)0, memory_list->region_count());
+
+ MinidumpContext* context = exception->GetContext();
+ ASSERT_TRUE(context);
+
+ u_int64_t instruction_pointer;
+ switch (context->GetContextCPU()) {
+ case MD_CONTEXT_X86:
+ instruction_pointer = context->GetContextX86()->eip;
+ break;
+ case MD_CONTEXT_AMD64:
+ instruction_pointer = context->GetContextAMD64()->rip;
+ break;
+ default:
+ FAIL() << "Unknown context CPU: " << context->GetContextCPU();
+ break;
+ }
+
+ MinidumpMemoryRegion* region =
+ memory_list->GetMemoryRegionForAddress(instruction_pointer);
+ ASSERT_TRUE(region);
+
+ EXPECT_EQ(kMemorySize, region->GetSize());
+ const u_int8_t* bytes = region->GetMemory();
+ ASSERT_TRUE(bytes);
+
+ u_int8_t prefix_bytes[kOffset];
+ u_int8_t suffix_bytes[kMemorySize - kOffset - sizeof(instructions)];
+ memset(prefix_bytes, 0, sizeof(prefix_bytes));
+ memset(suffix_bytes, 0, sizeof(suffix_bytes));
+ EXPECT_TRUE(memcmp(bytes, prefix_bytes, sizeof(prefix_bytes)) == 0);
+ EXPECT_TRUE(memcmp(bytes + kOffset, instructions,
+ sizeof(instructions)) == 0);
+ EXPECT_TRUE(memcmp(bytes + kOffset + sizeof(instructions),
+ suffix_bytes, sizeof(suffix_bytes)) == 0);
+ }
+
+ DeleteFileW(minidump_filename_wide.c_str());
+}
+
+TEST_F(ExceptionHandlerDeathTest, InstructionPointerMemoryMinBound) {
+ ASSERT_TRUE(DoesPathExist(temp_path_));
+ google_breakpad::ExceptionHandler *exc =
+ new google_breakpad::ExceptionHandler(
+ temp_path_, NULL, NULL, NULL,
+ google_breakpad::ExceptionHandler::HANDLER_ALL);
+
+ SYSTEM_INFO sSysInfo; // Useful information about the system
+ GetSystemInfo(&sSysInfo); // Initialize the structure.
+
+ const u_int32_t kMemorySize = 256; // bytes
+ const DWORD kPageSize = sSysInfo.dwPageSize;
+ const int kOffset = 0;
+ // This crashes with SIGILL on x86/x86-64/arm.
+ const unsigned char instructions[] = { 0xff, 0xff, 0xff, 0xff };
+ // Get some executable memory. Specifically, reserve two pages,
+ // but only commit the second.
+ char* all_memory = reinterpret_cast<char*>(VirtualAlloc(NULL,
+ kPageSize * 2,
+ MEM_RESERVE,
+ PAGE_NOACCESS));
+ ASSERT_TRUE(all_memory);
+ char* memory = all_memory + kPageSize;
+ ASSERT_TRUE(VirtualAlloc(memory, kPageSize,
+ MEM_COMMIT, PAGE_EXECUTE_READWRITE));
+
+ // Write some instructions that will crash. Put them
+ // in the middle of the block of memory, because the
+ // minidump should contain 128 bytes on either side of the
+ // instruction pointer.
+ memcpy(memory + kOffset, instructions, sizeof(instructions));
+
+ // Now execute the instructions, which should crash.
+ typedef void (*void_function)(void);
+ void_function memory_function =
+ reinterpret_cast<void_function>(memory + kOffset);
+ ASSERT_DEATH(memory_function(), "");
+
+ // free the memory.
+ VirtualFree(memory, 0, MEM_RELEASE);
+
+ // Verify that the resulting minidump contains the memory around the IP
+ wstring minidump_filename_wide = find_minidump_in_directory(temp_path_);
+ ASSERT_FALSE(minidump_filename_wide.empty());
+ string minidump_filename;
+ ASSERT_TRUE(WindowsStringUtils::safe_wcstombs(minidump_filename_wide,
+ &minidump_filename));
+
+ // Read the minidump. Locate the exception record and the
+ // memory list, and then ensure that there is a memory region
+ // in the memory list that covers the instruction pointer from
+ // the exception record.
+ {
+ Minidump minidump(minidump_filename);
+ ASSERT_TRUE(minidump.Read());
+
+ MinidumpException* exception = minidump.GetException();
+ MinidumpMemoryList* memory_list = minidump.GetMemoryList();
+ ASSERT_TRUE(exception);
+ ASSERT_TRUE(memory_list);
+ ASSERT_LT((unsigned)0, memory_list->region_count());
+
+ MinidumpContext* context = exception->GetContext();
+ ASSERT_TRUE(context);
+
+ u_int64_t instruction_pointer;
+ switch (context->GetContextCPU()) {
+ case MD_CONTEXT_X86:
+ instruction_pointer = context->GetContextX86()->eip;
+ break;
+ case MD_CONTEXT_AMD64:
+ instruction_pointer = context->GetContextAMD64()->rip;
+ break;
+ default:
+ FAIL() << "Unknown context CPU: " << context->GetContextCPU();
+ break;
+ }
+
+ MinidumpMemoryRegion* region =
+ memory_list->GetMemoryRegionForAddress(instruction_pointer);
+ ASSERT_TRUE(region);
+
+ EXPECT_EQ(kMemorySize / 2, region->GetSize());
+ const u_int8_t* bytes = region->GetMemory();
+ ASSERT_TRUE(bytes);
+
+ u_int8_t suffix_bytes[kMemorySize / 2 - sizeof(instructions)];
+ memset(suffix_bytes, 0, sizeof(suffix_bytes));
+ EXPECT_TRUE(memcmp(bytes + kOffset,
+ instructions, sizeof(instructions)) == 0);
+ EXPECT_TRUE(memcmp(bytes + kOffset + sizeof(instructions),
+ suffix_bytes, sizeof(suffix_bytes)) == 0);
+ }
+
+ DeleteFileW(minidump_filename_wide.c_str());
+}
+
+TEST_F(ExceptionHandlerDeathTest, InstructionPointerMemoryMaxBound) {
+ ASSERT_TRUE(DoesPathExist(temp_path_));
+ google_breakpad::ExceptionHandler *exc =
+ new google_breakpad::ExceptionHandler(
+ temp_path_, NULL, NULL, NULL,
+ google_breakpad::ExceptionHandler::HANDLER_ALL);
+
+ SYSTEM_INFO sSysInfo; // Useful information about the system
+ GetSystemInfo(&sSysInfo); // Initialize the structure.
+
+ const DWORD kPageSize = sSysInfo.dwPageSize;
+ // This crashes with SIGILL on x86/x86-64/arm.
+ const unsigned char instructions[] = { 0xff, 0xff, 0xff, 0xff };
+ const int kOffset = kPageSize - sizeof(instructions);
+ // Get some executable memory. Specifically, reserve two pages,
+ // but only commit the first.
+ char* memory = reinterpret_cast<char*>(VirtualAlloc(NULL,
+ kPageSize * 2,
+ MEM_RESERVE,
+ PAGE_NOACCESS));
+ ASSERT_TRUE(memory);
+ ASSERT_TRUE(VirtualAlloc(memory, kPageSize,
+ MEM_COMMIT, PAGE_EXECUTE_READWRITE));
+
+ // Write some instructions that will crash.
+ memcpy(memory + kOffset, instructions, sizeof(instructions));
+
+ // Now execute the instructions, which should crash.
+ typedef void (*void_function)(void);
+ void_function memory_function =
+ reinterpret_cast<void_function>(memory + kOffset);
+ ASSERT_DEATH(memory_function(), "");
+
+ // free the memory.
+ VirtualFree(memory, 0, MEM_RELEASE);
+
+ // Verify that the resulting minidump contains the memory around the IP
+ wstring minidump_filename_wide = find_minidump_in_directory(temp_path_);
+ ASSERT_FALSE(minidump_filename_wide.empty());
+ string minidump_filename;
+ ASSERT_TRUE(WindowsStringUtils::safe_wcstombs(minidump_filename_wide,
+ &minidump_filename));
+
+ // Read the minidump. Locate the exception record and the
+ // memory list, and then ensure that there is a memory region
+ // in the memory list that covers the instruction pointer from
+ // the exception record.
+ {
+ Minidump minidump(minidump_filename);
+ ASSERT_TRUE(minidump.Read());
+
+ MinidumpException* exception = minidump.GetException();
+ MinidumpMemoryList* memory_list = minidump.GetMemoryList();
+ ASSERT_TRUE(exception);
+ ASSERT_TRUE(memory_list);
+ ASSERT_LT((unsigned)0, memory_list->region_count());
+
+ MinidumpContext* context = exception->GetContext();
+ ASSERT_TRUE(context);
+
+ u_int64_t instruction_pointer;
+ switch (context->GetContextCPU()) {
+ case MD_CONTEXT_X86:
+ instruction_pointer = context->GetContextX86()->eip;
+ break;
+ case MD_CONTEXT_AMD64:
+ instruction_pointer = context->GetContextAMD64()->rip;
+ break;
+ default:
+ FAIL() << "Unknown context CPU: " << context->GetContextCPU();
+ break;
+ }
+
+ MinidumpMemoryRegion* region =
+ memory_list->GetMemoryRegionForAddress(instruction_pointer);
+ ASSERT_TRUE(region);
+
+ const size_t kPrefixSize = 128; // bytes
+ EXPECT_EQ(kPrefixSize + sizeof(instructions), region->GetSize());
+ const u_int8_t* bytes = region->GetMemory();
+ ASSERT_TRUE(bytes);
+
+ u_int8_t prefix_bytes[kPrefixSize];
+ memset(prefix_bytes, 0, sizeof(prefix_bytes));
+ EXPECT_TRUE(memcmp(bytes, prefix_bytes, sizeof(prefix_bytes)) == 0);
+ EXPECT_TRUE(memcmp(bytes + kPrefixSize,
+ instructions, sizeof(instructions)) == 0);
+ }
+
+ DeleteFileW(minidump_filename_wide.c_str());
+}
+
+} // namespace
--- a/toolkit/crashreporter/test/CrashTestUtils.jsm
+++ b/toolkit/crashreporter/test/CrashTestUtils.jsm
@@ -1,15 +1,16 @@
var EXPORTED_SYMBOLS = ["CrashTestUtils"];
let CrashTestUtils = {
// These will be defined using ctypes APIs below.
crash: null,
lockDir: null,
dumpHasStream: null,
+ dumpHasInstructionPointerMemory: null,
// Constants for crash()
// Keep these in sync with nsTestCrasher.cpp!
CRASH_INVALID_POINTER_DEREF: 0,
CRASH_PURE_VIRTUAL_CALL: 1,
CRASH_RUNTIMEABORT: 2,
// Constants for dumpHasStream()
@@ -35,8 +36,14 @@ CrashTestUtils.lockDir = lib.declare("Lo
ctypes.voidptr_t); // nsISupports*
CrashTestUtils.dumpHasStream = lib.declare("DumpHasStream",
ctypes.default_abi,
ctypes.bool,
ctypes.char.ptr,
ctypes.uint32_t);
+
+CrashTestUtils.dumpHasInstructionPointerMemory =
+ lib.declare("DumpHasInstructionPointerMemory",
+ ctypes.default_abi,
+ ctypes.bool,
+ ctypes.char.ptr);
--- a/toolkit/crashreporter/test/dumputils.cpp
+++ b/toolkit/crashreporter/test/dumputils.cpp
@@ -1,19 +1,60 @@
#include "google_breakpad/processor/minidump.h"
#include "nscore.h"
using namespace google_breakpad;
+// Return true if the specified minidump contains a stream of |stream_type|.
extern "C"
NS_EXPORT bool
DumpHasStream(const char* dump_file, u_int32_t stream_type)
{
Minidump dump(dump_file);
if (!dump.Read())
return false;
u_int32_t length;
if (!dump.SeekToStreamType(stream_type, &length) || length == 0)
return false;
return true;
}
+
+// Return true if the specified minidump contains a memory region
+// that contains the instruction pointer from the exception record.
+extern "C"
+NS_EXPORT bool
+DumpHasInstructionPointerMemory(const char* dump_file)
+{
+ Minidump minidump(dump_file);
+ if (!minidump.Read())
+ return false;
+
+ MinidumpException* exception = minidump.GetException();
+ MinidumpMemoryList* memory_list = minidump.GetMemoryList();
+ if (!exception || !memory_list) {
+ return false;
+ }
+
+ MinidumpContext* context = exception->GetContext();
+ if (!context)
+ return false;
+
+ u_int64_t instruction_pointer;
+ switch (context->GetContextCPU()) {
+ case MD_CONTEXT_X86:
+ instruction_pointer = context->GetContextX86()->eip;
+ break;
+ case MD_CONTEXT_AMD64:
+ instruction_pointer = context->GetContextAMD64()->rip;
+ break;
+ case MD_CONTEXT_ARM:
+ instruction_pointer = context->GetContextARM()->iregs[15];
+ break;
+ default:
+ return false;
+ }
+
+ MinidumpMemoryRegion* region =
+ memory_list->GetMemoryRegionForAddress(instruction_pointer);
+ return region != NULL;
+}
--- a/toolkit/crashreporter/test/unit/test_crashreporter_crash.js
+++ b/toolkit/crashreporter/test/unit/test_crashreporter_crash.js
@@ -16,16 +16,17 @@ function run_test()
// try a basic crash
do_crash(null, function(mdump, extra) {
do_check_true(mdump.exists());
do_check_true(mdump.fileSize > 0);
do_check_true('StartupTime' in extra);
do_check_true('CrashTime' in extra);
do_check_true(CrashTestUtils.dumpHasStream(mdump.path, CrashTestUtils.MD_THREAD_LIST_STREAM));
+ do_check_true(CrashTestUtils.dumpHasInstructionPointerMemory(mdump.path));
if (is_win7_or_newer)
do_check_true(CrashTestUtils.dumpHasStream(mdump.path, CrashTestUtils.MD_MEMORY_INFO_LIST_STREAM));
});
// check setting some basic data
do_crash(function() {
crashReporter.annotateCrashReport("TestKey", "TestValue");
crashReporter.appendAppNotesToCrashReport("Junk");