mirror of
https://github.com/yuzu-emu/breakpad.git
synced 2025-07-06 15:50:49 +00:00
Use a MinidumpCallback to force minidumps on Windows to include memory around the faulting instruction pointer. Older versions of DbgHelp don't seem to do this correctly (on Windows XP, for example)
R=mark at http://breakpad.appspot.com/259001 git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@763 4c0a9323-5329-0410-9bdc-e9ce6186880e
This commit is contained in:
parent
9220e0baf1
commit
0df0555e75
|
@ -28,6 +28,8 @@
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
#include <ObjBase.h>
|
#include <ObjBase.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
|
||||||
|
@ -44,6 +46,13 @@ namespace google_breakpad {
|
||||||
static const int kWaitForHandlerThreadMs = 60000;
|
static const int kWaitForHandlerThreadMs = 60000;
|
||||||
static const int kExceptionHandlerThreadInitialStackSize = 64 * 1024;
|
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;
|
vector<ExceptionHandler*>* ExceptionHandler::handler_stack_ = NULL;
|
||||||
LONG ExceptionHandler::handler_stack_index_ = 0;
|
LONG ExceptionHandler::handler_stack_index_ = 0;
|
||||||
CRITICAL_SECTION ExceptionHandler::handler_stack_critical_section_;
|
CRITICAL_SECTION ExceptionHandler::handler_stack_critical_section_;
|
||||||
|
@ -757,6 +766,50 @@ bool ExceptionHandler::WriteMinidumpWithException(
|
||||||
++user_streams.UserStreamCount;
|
++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).
|
// The explicit comparison to TRUE avoids a warning (C4800).
|
||||||
success = (minidump_write_dump_(GetCurrentProcess(),
|
success = (minidump_write_dump_(GetCurrentProcess(),
|
||||||
GetCurrentProcessId(),
|
GetCurrentProcessId(),
|
||||||
|
@ -764,7 +817,7 @@ bool ExceptionHandler::WriteMinidumpWithException(
|
||||||
dump_type_,
|
dump_type_,
|
||||||
exinfo ? &except_info : NULL,
|
exinfo ? &except_info : NULL,
|
||||||
&user_streams,
|
&user_streams,
|
||||||
NULL) == TRUE);
|
callback_pointer) == TRUE);
|
||||||
|
|
||||||
CloseHandle(dump_file);
|
CloseHandle(dump_file);
|
||||||
}
|
}
|
||||||
|
@ -783,6 +836,45 @@ bool ExceptionHandler::WriteMinidumpWithException(
|
||||||
return 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;
|
||||||
|
}
|
||||||
|
|
||||||
void ExceptionHandler::UpdateNextID() {
|
void ExceptionHandler::UpdateNextID() {
|
||||||
assert(uuid_create_);
|
assert(uuid_create_);
|
||||||
UUID id = {0};
|
UUID id = {0};
|
||||||
|
|
|
@ -277,6 +277,13 @@ class ExceptionHandler {
|
||||||
EXCEPTION_POINTERS* exinfo,
|
EXCEPTION_POINTERS* exinfo,
|
||||||
MDRawAssertionInfo* assertion);
|
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);
|
||||||
|
|
||||||
// Generates a new ID and stores it in next_minidump_id_, and stores the
|
// 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_.
|
// path of the next minidump to be written in next_minidump_path_.
|
||||||
void UpdateNextID();
|
void UpdateNextID();
|
||||||
|
|
|
@ -33,11 +33,19 @@
|
||||||
#include <objbase.h>
|
#include <objbase.h>
|
||||||
#include <shellapi.h>
|
#include <shellapi.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#include "../../../breakpad_googletest_includes.h"
|
#include "../../../breakpad_googletest_includes.h"
|
||||||
|
#include "../../../../common/windows/string_utils-inl.h"
|
||||||
#include "../crash_generation/crash_generation_server.h"
|
#include "../crash_generation/crash_generation_server.h"
|
||||||
#include "../handler/exception_handler.h"
|
#include "../handler/exception_handler.h"
|
||||||
|
#include "../../../../google_breakpad/processor/minidump.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
using std::wstring;
|
||||||
|
using namespace google_breakpad;
|
||||||
|
|
||||||
const wchar_t kPipeName[] = L"\\\\.\\pipe\\BreakpadCrashTest\\TestCaseServer";
|
const wchar_t kPipeName[] = L"\\\\.\\pipe\\BreakpadCrashTest\\TestCaseServer";
|
||||||
const char kSuccessIndicator[] = "success";
|
const char kSuccessIndicator[] = "success";
|
||||||
const char kFailureIndicator[] = "failure";
|
const char kFailureIndicator[] = "failure";
|
||||||
|
@ -65,8 +73,8 @@ void ExceptionHandlerDeathTest::SetUp() {
|
||||||
// We want the temporary directory to be what the OS returns
|
// We want the temporary directory to be what the OS returns
|
||||||
// to us, + the test case name.
|
// to us, + the test case name.
|
||||||
GetTempPath(MAX_PATH, temp_path);
|
GetTempPath(MAX_PATH, temp_path);
|
||||||
// THe test case name is exposed to use as a c-style string,
|
// The test case name is exposed as a c-style string,
|
||||||
// But we might be working in UNICODE here on Windows.
|
// convert it to a wchar_t string.
|
||||||
int dwRet = MultiByteToWideChar(CP_ACP, 0, test_info->name(),
|
int dwRet = MultiByteToWideChar(CP_ACP, 0, test_info->name(),
|
||||||
strlen(test_info->name()),
|
strlen(test_info->name()),
|
||||||
test_name_wide,
|
test_name_wide,
|
||||||
|
@ -212,4 +220,310 @@ TEST_F(ExceptionHandlerDeathTest, PureVirtualCallTest) {
|
||||||
// Calls a pure virtual function.
|
// Calls a pure virtual function.
|
||||||
EXPECT_EXIT(DoCrashPureVirtualCall(), ::testing::ExitedWithCode(0), "");
|
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
|
||||||
|
|
Loading…
Reference in a new issue