mirror of
https://github.com/tomahawk-player/tomahawk.git
synced 2025-08-23 22:25:11 +02:00
* Added breakpad support for Linux.
This commit is contained in:
76
thirdparty/breakpad/client/windows/unittests/client_tests.gyp
vendored
Executable file
76
thirdparty/breakpad/client/windows/unittests/client_tests.gyp
vendored
Executable file
@@ -0,0 +1,76 @@
|
||||
# Copyright (c) 2010, Google Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following disclaimer
|
||||
# in the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of Google Inc. nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
{
|
||||
'includes': [
|
||||
'../build/common.gypi',
|
||||
],
|
||||
'targets': [
|
||||
{
|
||||
'target_name': 'client_tests',
|
||||
'type': 'executable',
|
||||
'sources': [
|
||||
'exception_handler_test.cc',
|
||||
'exception_handler_death_test.cc',
|
||||
'minidump_test.cc',
|
||||
'dump_analysis.cc',
|
||||
'dump_analysis.h',
|
||||
'crash_generation_server_test.cc'
|
||||
],
|
||||
'dependencies': [
|
||||
'testing.gyp:gtest',
|
||||
'testing.gyp:gmock',
|
||||
'../breakpad_client.gyp:common',
|
||||
'../crash_generation/crash_generation.gyp:crash_generation_server',
|
||||
'../crash_generation/crash_generation.gyp:crash_generation_client',
|
||||
'../handler/exception_handler.gyp:exception_handler',
|
||||
'processor_bits',
|
||||
]
|
||||
},
|
||||
{
|
||||
'target_name': 'processor_bits',
|
||||
'type': 'static_library',
|
||||
'include_dirs': [
|
||||
'<(DEPTH)',
|
||||
],
|
||||
'direct_dependent_settings': {
|
||||
'include_dirs': [
|
||||
'<(DEPTH)',
|
||||
]
|
||||
},
|
||||
'sources': [
|
||||
'<(DEPTH)/common/string_conversion.cc',
|
||||
'<(DEPTH)/processor/basic_code_modules.cc',
|
||||
'<(DEPTH)/processor/logging.cc',
|
||||
'<(DEPTH)/processor/minidump.cc',
|
||||
'<(DEPTH)/processor/pathname_stripper.cc',
|
||||
]
|
||||
}
|
||||
],
|
||||
}
|
298
thirdparty/breakpad/client/windows/unittests/crash_generation_server_test.cc
vendored
Normal file
298
thirdparty/breakpad/client/windows/unittests/crash_generation_server_test.cc
vendored
Normal file
@@ -0,0 +1,298 @@
|
||||
// Copyright 2010, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
#include "testing/include/gmock/gmock.h"
|
||||
|
||||
#include "client/windows/crash_generation/crash_generation_server.h"
|
||||
#include "client/windows/common/ipc_protocol.h"
|
||||
|
||||
using testing::_;
|
||||
|
||||
namespace {
|
||||
|
||||
const wchar_t kPipeName[] =
|
||||
L"\\\\.\\pipe\\CrashGenerationServerTest\\TestCaseServer";
|
||||
|
||||
const DWORD kPipeDesiredAccess = FILE_READ_DATA |
|
||||
FILE_WRITE_DATA |
|
||||
FILE_WRITE_ATTRIBUTES;
|
||||
|
||||
const DWORD kPipeFlagsAndAttributes = SECURITY_IDENTIFICATION |
|
||||
SECURITY_SQOS_PRESENT;
|
||||
|
||||
const DWORD kPipeMode = PIPE_READMODE_MESSAGE;
|
||||
|
||||
int kCustomInfoCount = 2;
|
||||
|
||||
google_breakpad::CustomInfoEntry kCustomInfoEntries[] = {
|
||||
google_breakpad::CustomInfoEntry(L"prod", L"CrashGenerationServerTest"),
|
||||
google_breakpad::CustomInfoEntry(L"ver", L"1.0"),
|
||||
};
|
||||
|
||||
class CrashGenerationServerTest : public ::testing::Test {
|
||||
public:
|
||||
CrashGenerationServerTest()
|
||||
: crash_generation_server_(kPipeName,
|
||||
NULL,
|
||||
CallOnClientConnected, &mock_callbacks_,
|
||||
CallOnClientDumpRequested, &mock_callbacks_,
|
||||
CallOnClientExited, &mock_callbacks_,
|
||||
false,
|
||||
NULL),
|
||||
thread_id_(0),
|
||||
exception_pointers_(NULL) {
|
||||
memset(&assert_info_, 0, sizeof(assert_info_));
|
||||
}
|
||||
|
||||
protected:
|
||||
class MockCrashGenerationServerCallbacks {
|
||||
public:
|
||||
MOCK_METHOD1(OnClientConnected,
|
||||
void(const google_breakpad::ClientInfo* client_info));
|
||||
MOCK_METHOD2(OnClientDumpRequested,
|
||||
void(const google_breakpad::ClientInfo* client_info,
|
||||
const std::wstring* file_path));
|
||||
MOCK_METHOD1(OnClientExited,
|
||||
void(const google_breakpad::ClientInfo* client_info));
|
||||
};
|
||||
|
||||
enum ClientFault {
|
||||
NO_FAULT,
|
||||
CLOSE_AFTER_CONNECT,
|
||||
SEND_INVALID_REGISTRATION,
|
||||
TRUNCATE_REGISTRATION,
|
||||
CLOSE_AFTER_REGISTRATION,
|
||||
RESPONSE_BUFFER_TOO_SMALL,
|
||||
CLOSE_AFTER_RESPONSE,
|
||||
SEND_INVALID_ACK
|
||||
};
|
||||
|
||||
void SetUp() {
|
||||
ASSERT_TRUE(crash_generation_server_.Start());
|
||||
}
|
||||
|
||||
void FaultyClient(ClientFault fault_type) {
|
||||
HANDLE pipe = CreateFile(kPipeName,
|
||||
kPipeDesiredAccess,
|
||||
0,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
kPipeFlagsAndAttributes,
|
||||
NULL);
|
||||
|
||||
if (pipe == INVALID_HANDLE_VALUE) {
|
||||
ASSERT_EQ(ERROR_PIPE_BUSY, GetLastError());
|
||||
|
||||
// Cannot continue retrying if wait on pipe fails.
|
||||
ASSERT_TRUE(WaitNamedPipe(kPipeName, 500));
|
||||
|
||||
pipe = CreateFile(kPipeName,
|
||||
kPipeDesiredAccess,
|
||||
0,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
kPipeFlagsAndAttributes,
|
||||
NULL);
|
||||
}
|
||||
|
||||
ASSERT_NE(pipe, INVALID_HANDLE_VALUE);
|
||||
|
||||
DWORD mode = kPipeMode;
|
||||
ASSERT_TRUE(SetNamedPipeHandleState(pipe, &mode, NULL, NULL));
|
||||
|
||||
DoFaultyClient(fault_type, pipe);
|
||||
|
||||
CloseHandle(pipe);
|
||||
}
|
||||
|
||||
void DoTestFault(ClientFault fault) {
|
||||
EXPECT_CALL(mock_callbacks_, OnClientConnected(_)).Times(0);
|
||||
ASSERT_NO_FATAL_FAILURE(FaultyClient(fault));
|
||||
ASSERT_NO_FATAL_FAILURE(FaultyClient(fault));
|
||||
ASSERT_NO_FATAL_FAILURE(FaultyClient(fault));
|
||||
|
||||
EXPECT_CALL(mock_callbacks_, OnClientConnected(_));
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(FaultyClient(NO_FAULT));
|
||||
|
||||
// Slight hack. The OnClientConnected is only invoked after the ack is
|
||||
// received by the server. At that point, the FaultyClient call has already
|
||||
// returned. The best way to wait until the server is done handling that is
|
||||
// to send one more ping, whose processing will be blocked by delivery of
|
||||
// the OnClientConnected message.
|
||||
ASSERT_NO_FATAL_FAILURE(FaultyClient(CLOSE_AFTER_CONNECT));
|
||||
}
|
||||
|
||||
MockCrashGenerationServerCallbacks mock_callbacks_;
|
||||
|
||||
private:
|
||||
// Depends on the caller to successfully open the pipe before invocation and
|
||||
// to close it immediately afterwards.
|
||||
void DoFaultyClient(ClientFault fault_type, HANDLE pipe) {
|
||||
if (fault_type == CLOSE_AFTER_CONNECT) {
|
||||
return;
|
||||
}
|
||||
|
||||
google_breakpad::CustomClientInfo custom_info = {kCustomInfoEntries,
|
||||
kCustomInfoCount};
|
||||
|
||||
google_breakpad::ProtocolMessage msg(
|
||||
fault_type == SEND_INVALID_REGISTRATION ?
|
||||
google_breakpad::MESSAGE_TAG_NONE :
|
||||
google_breakpad::MESSAGE_TAG_REGISTRATION_REQUEST,
|
||||
GetCurrentProcessId(),
|
||||
MiniDumpNormal,
|
||||
&thread_id_,
|
||||
&exception_pointers_,
|
||||
&assert_info_,
|
||||
custom_info,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL);
|
||||
|
||||
DWORD bytes_count = 0;
|
||||
|
||||
ASSERT_TRUE(WriteFile(pipe,
|
||||
&msg,
|
||||
fault_type == TRUNCATE_REGISTRATION ?
|
||||
sizeof(msg) / 2 : sizeof(msg),
|
||||
&bytes_count,
|
||||
NULL));
|
||||
|
||||
if (fault_type == CLOSE_AFTER_REGISTRATION) {
|
||||
return;
|
||||
}
|
||||
|
||||
google_breakpad::ProtocolMessage reply;
|
||||
|
||||
if (!ReadFile(pipe,
|
||||
&reply,
|
||||
fault_type == RESPONSE_BUFFER_TOO_SMALL ?
|
||||
sizeof(google_breakpad::ProtocolMessage) / 2 :
|
||||
sizeof(google_breakpad::ProtocolMessage),
|
||||
&bytes_count,
|
||||
NULL)) {
|
||||
switch (fault_type) {
|
||||
case TRUNCATE_REGISTRATION:
|
||||
case RESPONSE_BUFFER_TOO_SMALL:
|
||||
case SEND_INVALID_REGISTRATION:
|
||||
return;
|
||||
|
||||
default:
|
||||
FAIL() << "Unexpectedly failed to register.";
|
||||
}
|
||||
}
|
||||
|
||||
if (fault_type == CLOSE_AFTER_RESPONSE) {
|
||||
return;
|
||||
}
|
||||
|
||||
google_breakpad::ProtocolMessage ack_msg;
|
||||
ack_msg.tag = google_breakpad::MESSAGE_TAG_REGISTRATION_ACK;
|
||||
|
||||
ASSERT_TRUE(WriteFile(pipe,
|
||||
&ack_msg,
|
||||
SEND_INVALID_ACK ?
|
||||
sizeof(ack_msg) : sizeof(ack_msg) / 2,
|
||||
&bytes_count,
|
||||
NULL));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static void CallOnClientConnected(
|
||||
void* context, const google_breakpad::ClientInfo* client_info) {
|
||||
static_cast<MockCrashGenerationServerCallbacks*>(context)->
|
||||
OnClientConnected(client_info);
|
||||
}
|
||||
|
||||
static void CallOnClientDumpRequested(
|
||||
void* context,
|
||||
const google_breakpad::ClientInfo* client_info,
|
||||
const std::wstring* file_path) {
|
||||
static_cast<MockCrashGenerationServerCallbacks*>(context)->
|
||||
OnClientDumpRequested(client_info, file_path);
|
||||
}
|
||||
|
||||
static void CallOnClientExited(
|
||||
void* context, const google_breakpad::ClientInfo* client_info) {
|
||||
static_cast<MockCrashGenerationServerCallbacks*>(context)->
|
||||
OnClientExited(client_info);
|
||||
}
|
||||
|
||||
DWORD thread_id_;
|
||||
EXCEPTION_POINTERS* exception_pointers_;
|
||||
MDRawAssertionInfo assert_info_;
|
||||
|
||||
google_breakpad::CrashGenerationServer crash_generation_server_;
|
||||
};
|
||||
|
||||
TEST_F(CrashGenerationServerTest, PingServerTest) {
|
||||
DoTestFault(CLOSE_AFTER_CONNECT);
|
||||
}
|
||||
|
||||
TEST_F(CrashGenerationServerTest, InvalidRegistration) {
|
||||
DoTestFault(SEND_INVALID_REGISTRATION);
|
||||
}
|
||||
|
||||
TEST_F(CrashGenerationServerTest, TruncateRegistration) {
|
||||
DoTestFault(TRUNCATE_REGISTRATION);
|
||||
}
|
||||
|
||||
TEST_F(CrashGenerationServerTest, CloseAfterRegistration) {
|
||||
DoTestFault(CLOSE_AFTER_REGISTRATION);
|
||||
}
|
||||
|
||||
TEST_F(CrashGenerationServerTest, ResponseBufferTooSmall) {
|
||||
DoTestFault(RESPONSE_BUFFER_TOO_SMALL);
|
||||
}
|
||||
|
||||
TEST_F(CrashGenerationServerTest, CloseAfterResponse) {
|
||||
DoTestFault(CLOSE_AFTER_RESPONSE);
|
||||
}
|
||||
|
||||
// It turns out that, as long as you send one byte, the ACK is accepted and
|
||||
// registration succeeds.
|
||||
TEST_F(CrashGenerationServerTest, SendInvalidAck) {
|
||||
EXPECT_CALL(mock_callbacks_, OnClientConnected(_));
|
||||
ASSERT_NO_FATAL_FAILURE(FaultyClient(SEND_INVALID_ACK));
|
||||
|
||||
// See DoTestFault for an explanation of this line
|
||||
ASSERT_NO_FATAL_FAILURE(FaultyClient(CLOSE_AFTER_CONNECT));
|
||||
|
||||
EXPECT_CALL(mock_callbacks_, OnClientConnected(_));
|
||||
ASSERT_NO_FATAL_FAILURE(FaultyClient(NO_FAULT));
|
||||
|
||||
// See DoTestFault for an explanation of this line
|
||||
ASSERT_NO_FATAL_FAILURE(FaultyClient(CLOSE_AFTER_CONNECT));
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
184
thirdparty/breakpad/client/windows/unittests/dump_analysis.cc
vendored
Executable file
184
thirdparty/breakpad/client/windows/unittests/dump_analysis.cc
vendored
Executable file
@@ -0,0 +1,184 @@
|
||||
// Copyright (c) 2010, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <windows.h>
|
||||
#include <objbase.h>
|
||||
#include <dbghelp.h>
|
||||
|
||||
#include "dump_analysis.h" // NOLINT
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
DumpAnalysis::~DumpAnalysis() {
|
||||
if (dump_file_view_ != NULL) {
|
||||
EXPECT_TRUE(::UnmapViewOfFile(dump_file_view_));
|
||||
::CloseHandle(dump_file_mapping_);
|
||||
dump_file_mapping_ = NULL;
|
||||
}
|
||||
|
||||
if (dump_file_handle_ != NULL) {
|
||||
::CloseHandle(dump_file_handle_);
|
||||
dump_file_handle_ = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void DumpAnalysis::EnsureDumpMapped() {
|
||||
if (dump_file_view_ == NULL) {
|
||||
dump_file_handle_ = ::CreateFile(dump_file_.c_str(),
|
||||
GENERIC_READ,
|
||||
0,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
0,
|
||||
NULL);
|
||||
ASSERT_TRUE(dump_file_handle_ != NULL);
|
||||
ASSERT_TRUE(dump_file_mapping_ == NULL);
|
||||
|
||||
dump_file_mapping_ = ::CreateFileMapping(dump_file_handle_,
|
||||
NULL,
|
||||
PAGE_READONLY,
|
||||
0,
|
||||
0,
|
||||
NULL);
|
||||
ASSERT_TRUE(dump_file_mapping_ != NULL);
|
||||
|
||||
dump_file_view_ = ::MapViewOfFile(dump_file_mapping_,
|
||||
FILE_MAP_READ,
|
||||
0,
|
||||
0,
|
||||
0);
|
||||
ASSERT_TRUE(dump_file_view_ != NULL);
|
||||
}
|
||||
}
|
||||
|
||||
bool DumpAnalysis::HasTebs() const {
|
||||
MINIDUMP_THREAD_LIST* thread_list = NULL;
|
||||
size_t thread_list_size = GetStream(ThreadListStream, &thread_list);
|
||||
|
||||
if (thread_list_size > 0 && thread_list != NULL) {
|
||||
for (ULONG i = 0; i < thread_list->NumberOfThreads; ++i) {
|
||||
if (!HasMemory(thread_list->Threads[i].Teb))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// No thread list, no TEB info.
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DumpAnalysis::HasPeb() const {
|
||||
MINIDUMP_THREAD_LIST* thread_list = NULL;
|
||||
size_t thread_list_size = GetStream(ThreadListStream, &thread_list);
|
||||
|
||||
if (thread_list_size > 0 && thread_list != NULL &&
|
||||
thread_list->NumberOfThreads > 0) {
|
||||
FakeTEB* teb = NULL;
|
||||
if (!HasMemory(thread_list->Threads[0].Teb, &teb))
|
||||
return false;
|
||||
|
||||
return HasMemory(teb->peb);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DumpAnalysis::HasStream(ULONG stream_number) const {
|
||||
void* stream = NULL;
|
||||
size_t stream_size = GetStreamImpl(stream_number, &stream);
|
||||
return stream_size > 0 && stream != NULL;
|
||||
}
|
||||
|
||||
size_t DumpAnalysis::GetStreamImpl(ULONG stream_number, void** stream) const {
|
||||
MINIDUMP_DIRECTORY* directory = NULL;
|
||||
ULONG memory_list_size = 0;
|
||||
BOOL ret = ::MiniDumpReadDumpStream(dump_file_view_,
|
||||
stream_number,
|
||||
&directory,
|
||||
stream,
|
||||
&memory_list_size);
|
||||
|
||||
return ret ? memory_list_size : 0;
|
||||
}
|
||||
|
||||
bool DumpAnalysis::HasMemoryImpl(const void *addr_in, size_t structuresize,
|
||||
void **structure) const {
|
||||
uintptr_t address = reinterpret_cast<uintptr_t>(addr_in);
|
||||
MINIDUMP_MEMORY_LIST* memory_list = NULL;
|
||||
size_t memory_list_size = GetStream(MemoryListStream, &memory_list);
|
||||
if (memory_list_size > 0 && memory_list != NULL) {
|
||||
for (ULONG i = 0; i < memory_list->NumberOfMemoryRanges; ++i) {
|
||||
MINIDUMP_MEMORY_DESCRIPTOR& descr = memory_list->MemoryRanges[i];
|
||||
const uintptr_t range_start =
|
||||
static_cast<uintptr_t>(descr.StartOfMemoryRange);
|
||||
uintptr_t range_end = range_start + descr.Memory.DataSize;
|
||||
|
||||
if (address >= range_start &&
|
||||
address + structuresize < range_end) {
|
||||
// The start address falls in the range, and the end address is
|
||||
// in bounds, return a pointer to the structure if requested.
|
||||
if (structure != NULL)
|
||||
*structure = RVA_TO_ADDR(dump_file_view_, descr.Memory.Rva);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We didn't find the range in a MINIDUMP_MEMORY_LIST, so maybe this
|
||||
// is a full dump using MINIDUMP_MEMORY64_LIST with all the memory at the
|
||||
// end of the dump file.
|
||||
MINIDUMP_MEMORY64_LIST* memory64_list = NULL;
|
||||
memory_list_size = GetStream(Memory64ListStream, &memory64_list);
|
||||
if (memory_list_size > 0 && memory64_list != NULL) {
|
||||
// Keep track of where the current descriptor maps to.
|
||||
RVA64 curr_rva = memory64_list->BaseRva;
|
||||
for (ULONG i = 0; i < memory64_list->NumberOfMemoryRanges; ++i) {
|
||||
MINIDUMP_MEMORY_DESCRIPTOR64& descr = memory64_list->MemoryRanges[i];
|
||||
uintptr_t range_start =
|
||||
static_cast<uintptr_t>(descr.StartOfMemoryRange);
|
||||
uintptr_t range_end = range_start + static_cast<size_t>(descr.DataSize);
|
||||
|
||||
if (address >= range_start &&
|
||||
address + structuresize < range_end) {
|
||||
// The start address falls in the range, and the end address is
|
||||
// in bounds, return a pointer to the structure if requested.
|
||||
if (structure != NULL)
|
||||
*structure = RVA_TO_ADDR(dump_file_view_, curr_rva);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Advance the current RVA.
|
||||
curr_rva += descr.DataSize;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
102
thirdparty/breakpad/client/windows/unittests/dump_analysis.h
vendored
Executable file
102
thirdparty/breakpad/client/windows/unittests/dump_analysis.h
vendored
Executable file
@@ -0,0 +1,102 @@
|
||||
// Copyright (c) 2010, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef CLIENT_WINDOWS_UNITTESTS_DUMP_ANALYSIS_H_
|
||||
#define CLIENT_WINDOWS_UNITTESTS_DUMP_ANALYSIS_H_
|
||||
|
||||
#include "../crash_generation/minidump_generator.h"
|
||||
|
||||
// Convenience to get to the PEB pointer in a TEB.
|
||||
struct FakeTEB {
|
||||
char dummy[0x30];
|
||||
void* peb;
|
||||
};
|
||||
|
||||
class DumpAnalysis {
|
||||
public:
|
||||
explicit DumpAnalysis(const std::wstring& file_path)
|
||||
: dump_file_(file_path), dump_file_view_(NULL), dump_file_mapping_(NULL),
|
||||
dump_file_handle_(NULL) {
|
||||
EnsureDumpMapped();
|
||||
}
|
||||
~DumpAnalysis();
|
||||
|
||||
bool HasStream(ULONG stream_number) const;
|
||||
|
||||
// This is template to keep type safety in the front, but we end up casting
|
||||
// to void** inside the implementation to pass the pointer to Win32. So
|
||||
// casting here is considered safe.
|
||||
template <class StreamType>
|
||||
size_t GetStream(ULONG stream_number, StreamType** stream) const {
|
||||
return GetStreamImpl(stream_number, reinterpret_cast<void**>(stream));
|
||||
}
|
||||
|
||||
bool HasTebs() const;
|
||||
bool HasPeb() const;
|
||||
bool HasMemory(ULONG64 address) const {
|
||||
return HasMemory<BYTE>(address, NULL);
|
||||
}
|
||||
|
||||
bool HasMemory(const void* address) const {
|
||||
return HasMemory<BYTE>(address, NULL);
|
||||
}
|
||||
|
||||
template <class StructureType>
|
||||
bool HasMemory(ULONG64 address, StructureType** structure = NULL) const {
|
||||
// We can't cope with 64 bit addresses for now.
|
||||
if (address > 0xFFFFFFFFUL)
|
||||
return false;
|
||||
|
||||
return HasMemory(reinterpret_cast<void*>(address), structure);
|
||||
}
|
||||
|
||||
template <class StructureType>
|
||||
bool HasMemory(const void* addr_in, StructureType** structure = NULL) const {
|
||||
return HasMemoryImpl(addr_in, sizeof(StructureType),
|
||||
reinterpret_cast<void**>(structure));
|
||||
}
|
||||
|
||||
protected:
|
||||
void EnsureDumpMapped();
|
||||
|
||||
HANDLE dump_file_mapping_;
|
||||
HANDLE dump_file_handle_;
|
||||
void* dump_file_view_;
|
||||
std::wstring dump_file_;
|
||||
|
||||
private:
|
||||
// This is the implementation of GetStream<>.
|
||||
size_t GetStreamImpl(ULONG stream_number, void** stream) const;
|
||||
|
||||
// This is the implementation of HasMemory<>.
|
||||
bool HasMemoryImpl(const void* addr_in, size_t pointersize,
|
||||
void** structure) const;
|
||||
};
|
||||
|
||||
#endif // CLIENT_WINDOWS_UNITTESTS_DUMP_ANALYSIS_H_
|
529
thirdparty/breakpad/client/windows/unittests/exception_handler_death_test.cc
vendored
Executable file
529
thirdparty/breakpad/client/windows/unittests/exception_handler_death_test.cc
vendored
Executable file
@@ -0,0 +1,529 @@
|
||||
// Copyright 2009, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// 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 {
|
||||
protected:
|
||||
// Member variable for each test that they can use
|
||||
// for temporary storage.
|
||||
TCHAR temp_path_[MAX_PATH];
|
||||
// Actually constructs a temp path name.
|
||||
virtual void SetUp();
|
||||
// A helper method that tests can use to crash.
|
||||
void DoCrashAccessViolation();
|
||||
void DoCrashPureVirtualCall();
|
||||
};
|
||||
|
||||
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 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);
|
||||
CreateDirectory(temp_path_, NULL);
|
||||
}
|
||||
|
||||
BOOL DoesPathExist(const TCHAR *path_name) {
|
||||
DWORD flags = GetFileAttributes(path_name);
|
||||
if (flags == INVALID_FILE_ATTRIBUTES) {
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
bool MinidumpWrittenCallback(const wchar_t* dump_path,
|
||||
const wchar_t* minidump_id,
|
||||
void* context,
|
||||
EXCEPTION_POINTERS* exinfo,
|
||||
MDRawAssertionInfo* assertion,
|
||||
bool succeeded) {
|
||||
if (succeeded && DoesPathExist(dump_path)) {
|
||||
fprintf(stderr, kSuccessIndicator);
|
||||
} else {
|
||||
fprintf(stderr, kFailureIndicator);
|
||||
}
|
||||
// If we don't flush, the output doesn't get sent before
|
||||
// this process dies.
|
||||
fflush(stderr);
|
||||
return succeeded;
|
||||
}
|
||||
|
||||
TEST_F(ExceptionHandlerDeathTest, InProcTest) {
|
||||
// For the in-proc test, we just need to instantiate an exception
|
||||
// handler in in-proc mode, and crash. Since the entire test is
|
||||
// reexecuted in the child process, we don't have to worry about
|
||||
// the semantics of the exception handler being inherited/not
|
||||
// inherited across CreateProcess().
|
||||
ASSERT_TRUE(DoesPathExist(temp_path_));
|
||||
google_breakpad::ExceptionHandler *exc =
|
||||
new google_breakpad::ExceptionHandler(
|
||||
temp_path_, NULL, &MinidumpWrittenCallback, NULL,
|
||||
google_breakpad::ExceptionHandler::HANDLER_ALL);
|
||||
int *i = NULL;
|
||||
ASSERT_DEATH((*i)++, kSuccessIndicator);
|
||||
delete exc;
|
||||
}
|
||||
|
||||
static bool gDumpCallbackCalled = false;
|
||||
|
||||
void clientDumpCallback(void *dump_context,
|
||||
const google_breakpad::ClientInfo *client_info,
|
||||
const std::wstring *dump_path) {
|
||||
gDumpCallbackCalled = true;
|
||||
}
|
||||
|
||||
void ExceptionHandlerDeathTest::DoCrashAccessViolation() {
|
||||
google_breakpad::ExceptionHandler *exc =
|
||||
new google_breakpad::ExceptionHandler(
|
||||
temp_path_, NULL, NULL, NULL,
|
||||
google_breakpad::ExceptionHandler::HANDLER_ALL, MiniDumpNormal, kPipeName,
|
||||
NULL);
|
||||
// Although this is executing in the child process of the death test,
|
||||
// if it's not true we'll still get an error rather than the crash
|
||||
// being expected.
|
||||
ASSERT_TRUE(exc->IsOutOfProcess());
|
||||
int *i = NULL;
|
||||
printf("%d\n", (*i)++);
|
||||
}
|
||||
|
||||
TEST_F(ExceptionHandlerDeathTest, OutOfProcTest) {
|
||||
// We can take advantage of a detail of google test here to save some
|
||||
// complexity in testing: when you do a death test, it actually forks.
|
||||
// So we can make the main test harness the crash generation server,
|
||||
// and call ASSERT_DEATH on a NULL dereference, it to expecting test
|
||||
// the out of process scenario, since it's happening in a different
|
||||
// process! This is different from the above because, above, we pass
|
||||
// a NULL pipe name, and we also don't start a crash generation server.
|
||||
|
||||
ASSERT_TRUE(DoesPathExist(temp_path_));
|
||||
std::wstring dump_path(temp_path_);
|
||||
google_breakpad::CrashGenerationServer server(
|
||||
kPipeName, NULL, NULL, NULL, &clientDumpCallback, NULL, NULL, NULL, true,
|
||||
&dump_path);
|
||||
|
||||
// This HAS to be EXPECT_, because when this test case is executed in the
|
||||
// child process, the server registration will fail due to the named pipe
|
||||
// being the same.
|
||||
EXPECT_TRUE(server.Start());
|
||||
EXPECT_FALSE(gDumpCallbackCalled);
|
||||
ASSERT_DEATH(this->DoCrashAccessViolation(), "");
|
||||
EXPECT_TRUE(gDumpCallbackCalled);
|
||||
}
|
||||
|
||||
TEST_F(ExceptionHandlerDeathTest, InvalidParameterTest) {
|
||||
using google_breakpad::ExceptionHandler;
|
||||
|
||||
ASSERT_TRUE(DoesPathExist(temp_path_));
|
||||
ExceptionHandler handler(temp_path_, NULL, NULL, NULL,
|
||||
ExceptionHandler::HANDLER_INVALID_PARAMETER);
|
||||
|
||||
// Disable the message box for assertions
|
||||
_CrtSetReportMode(_CRT_ASSERT, 0);
|
||||
|
||||
// Call with a bad argument. The invalid parameter will be swallowed
|
||||
// and a dump will be generated, the process will exit(0).
|
||||
ASSERT_EXIT(printf(NULL), ::testing::ExitedWithCode(0), "");
|
||||
}
|
||||
|
||||
|
||||
struct PureVirtualCallBase {
|
||||
PureVirtualCallBase() {
|
||||
// We have to reinterpret so the linker doesn't get confused because the
|
||||
// method isn't defined.
|
||||
reinterpret_cast<PureVirtualCallBase*>(this)->PureFunction();
|
||||
}
|
||||
virtual ~PureVirtualCallBase() {}
|
||||
virtual void PureFunction() const = 0;
|
||||
};
|
||||
struct PureVirtualCall : public PureVirtualCallBase {
|
||||
PureVirtualCall() { PureFunction(); }
|
||||
virtual void PureFunction() const {}
|
||||
};
|
||||
|
||||
void ExceptionHandlerDeathTest::DoCrashPureVirtualCall() {
|
||||
PureVirtualCall instance;
|
||||
}
|
||||
|
||||
TEST_F(ExceptionHandlerDeathTest, PureVirtualCallTest) {
|
||||
using google_breakpad::ExceptionHandler;
|
||||
|
||||
ASSERT_TRUE(DoesPathExist(temp_path_));
|
||||
ExceptionHandler handler(temp_path_, NULL, NULL, NULL,
|
||||
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
|
377
thirdparty/breakpad/client/windows/unittests/exception_handler_test.cc
vendored
Executable file
377
thirdparty/breakpad/client/windows/unittests/exception_handler_test.cc
vendored
Executable file
@@ -0,0 +1,377 @@
|
||||
// Copyright 2009, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// 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 "../../../../google_breakpad/processor/minidump.h"
|
||||
#include "../crash_generation/crash_generation_server.h"
|
||||
#include "../handler/exception_handler.h"
|
||||
#include "dump_analysis.h" // NOLINT
|
||||
|
||||
namespace {
|
||||
|
||||
using std::wstring;
|
||||
using namespace google_breakpad;
|
||||
|
||||
const wchar_t kPipeName[] = L"\\\\.\\pipe\\BreakpadCrashTest\\TestCaseServer";
|
||||
const char kSuccessIndicator[] = "success";
|
||||
const char kFailureIndicator[] = "failure";
|
||||
|
||||
const MINIDUMP_TYPE kFullDumpType = static_cast<MINIDUMP_TYPE>(
|
||||
MiniDumpWithFullMemory | // Full memory from process.
|
||||
MiniDumpWithProcessThreadData | // Get PEB and TEB.
|
||||
MiniDumpWithHandleData); // Get all handle information.
|
||||
|
||||
class ExceptionHandlerTest : public ::testing::Test {
|
||||
protected:
|
||||
// Member variable for each test that they can use
|
||||
// for temporary storage.
|
||||
TCHAR temp_path_[MAX_PATH];
|
||||
|
||||
// Actually constructs a temp path name.
|
||||
virtual void SetUp();
|
||||
|
||||
// Deletes temporary files.
|
||||
virtual void TearDown();
|
||||
|
||||
void DoCrashInvalidParameter();
|
||||
void DoCrashPureVirtualCall();
|
||||
|
||||
// Utility function to test for a path's existence.
|
||||
static BOOL DoesPathExist(const TCHAR *path_name);
|
||||
|
||||
// Client callback.
|
||||
static void ClientDumpCallback(
|
||||
void *dump_context,
|
||||
const google_breakpad::ClientInfo *client_info,
|
||||
const std::wstring *dump_path);
|
||||
|
||||
static bool DumpCallback(const wchar_t* dump_path,
|
||||
const wchar_t* minidump_id,
|
||||
void* context,
|
||||
EXCEPTION_POINTERS* exinfo,
|
||||
MDRawAssertionInfo* assertion,
|
||||
bool succeeded);
|
||||
|
||||
static std::wstring dump_file;
|
||||
static std::wstring full_dump_file;
|
||||
};
|
||||
|
||||
std::wstring ExceptionHandlerTest::dump_file;
|
||||
std::wstring ExceptionHandlerTest::full_dump_file;
|
||||
|
||||
void ExceptionHandlerTest::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.
|
||||
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);
|
||||
CreateDirectory(temp_path_, NULL);
|
||||
}
|
||||
|
||||
void ExceptionHandlerTest::TearDown() {
|
||||
if (!dump_file.empty()) {
|
||||
::DeleteFile(dump_file.c_str());
|
||||
dump_file = L"";
|
||||
}
|
||||
if (!full_dump_file.empty()) {
|
||||
::DeleteFile(full_dump_file.c_str());
|
||||
full_dump_file = L"";
|
||||
}
|
||||
}
|
||||
|
||||
BOOL ExceptionHandlerTest::DoesPathExist(const TCHAR *path_name) {
|
||||
DWORD flags = GetFileAttributes(path_name);
|
||||
if (flags == INVALID_FILE_ATTRIBUTES) {
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// static
|
||||
void ExceptionHandlerTest::ClientDumpCallback(
|
||||
void *dump_context,
|
||||
const google_breakpad::ClientInfo *client_info,
|
||||
const wstring *dump_path) {
|
||||
dump_file = *dump_path;
|
||||
// Create the full dump file name from the dump path.
|
||||
full_dump_file = dump_file.substr(0, dump_file.length() - 4) + L"-full.dmp";
|
||||
}
|
||||
|
||||
// static
|
||||
bool ExceptionHandlerTest::DumpCallback(const wchar_t* dump_path,
|
||||
const wchar_t* minidump_id,
|
||||
void* context,
|
||||
EXCEPTION_POINTERS* exinfo,
|
||||
MDRawAssertionInfo* assertion,
|
||||
bool succeeded) {
|
||||
dump_file = dump_path;
|
||||
dump_file += L"\\";
|
||||
dump_file += minidump_id;
|
||||
dump_file += L".dmp";
|
||||
return succeeded;
|
||||
}
|
||||
|
||||
void ExceptionHandlerTest::DoCrashInvalidParameter() {
|
||||
google_breakpad::ExceptionHandler *exc =
|
||||
new google_breakpad::ExceptionHandler(
|
||||
temp_path_, NULL, NULL, NULL,
|
||||
google_breakpad::ExceptionHandler::HANDLER_INVALID_PARAMETER,
|
||||
kFullDumpType, kPipeName, NULL);
|
||||
|
||||
// Disable the message box for assertions
|
||||
_CrtSetReportMode(_CRT_ASSERT, 0);
|
||||
|
||||
// Although this is executing in the child process of the death test,
|
||||
// if it's not true we'll still get an error rather than the crash
|
||||
// being expected.
|
||||
ASSERT_TRUE(exc->IsOutOfProcess());
|
||||
printf(NULL);
|
||||
}
|
||||
|
||||
|
||||
struct PureVirtualCallBase {
|
||||
PureVirtualCallBase() {
|
||||
// We have to reinterpret so the linker doesn't get confused because the
|
||||
// method isn't defined.
|
||||
reinterpret_cast<PureVirtualCallBase*>(this)->PureFunction();
|
||||
}
|
||||
virtual ~PureVirtualCallBase() {}
|
||||
virtual void PureFunction() const = 0;
|
||||
};
|
||||
struct PureVirtualCall : public PureVirtualCallBase {
|
||||
PureVirtualCall() { PureFunction(); }
|
||||
virtual void PureFunction() const {}
|
||||
};
|
||||
|
||||
void ExceptionHandlerTest::DoCrashPureVirtualCall() {
|
||||
google_breakpad::ExceptionHandler *exc =
|
||||
new google_breakpad::ExceptionHandler(
|
||||
temp_path_, NULL, NULL, NULL,
|
||||
google_breakpad::ExceptionHandler::HANDLER_PURECALL,
|
||||
kFullDumpType, kPipeName, NULL);
|
||||
|
||||
// Disable the message box for assertions
|
||||
_CrtSetReportMode(_CRT_ASSERT, 0);
|
||||
|
||||
// Although this is executing in the child process of the death test,
|
||||
// if it's not true we'll still get an error rather than the crash
|
||||
// being expected.
|
||||
ASSERT_TRUE(exc->IsOutOfProcess());
|
||||
|
||||
// Create a new frame to ensure PureVirtualCall is not optimized to some
|
||||
// other line in this function.
|
||||
{
|
||||
PureVirtualCall instance;
|
||||
}
|
||||
}
|
||||
|
||||
// This test validates that the minidump is written correctly.
|
||||
TEST_F(ExceptionHandlerTest, InvalidParameterMiniDumpTest) {
|
||||
ASSERT_TRUE(DoesPathExist(temp_path_));
|
||||
|
||||
// Call with a bad argument
|
||||
ASSERT_TRUE(DoesPathExist(temp_path_));
|
||||
wstring dump_path(temp_path_);
|
||||
google_breakpad::CrashGenerationServer server(
|
||||
kPipeName, NULL, NULL, NULL, ClientDumpCallback, NULL, NULL, NULL, true,
|
||||
&dump_path);
|
||||
|
||||
ASSERT_TRUE(dump_file.empty() && full_dump_file.empty());
|
||||
|
||||
// This HAS to be EXPECT_, because when this test case is executed in the
|
||||
// child process, the server registration will fail due to the named pipe
|
||||
// being the same.
|
||||
EXPECT_TRUE(server.Start());
|
||||
EXPECT_EXIT(DoCrashInvalidParameter(), ::testing::ExitedWithCode(0), "");
|
||||
ASSERT_TRUE(!dump_file.empty() && !full_dump_file.empty());
|
||||
ASSERT_TRUE(DoesPathExist(dump_file.c_str()));
|
||||
|
||||
// Verify the dump for infos.
|
||||
DumpAnalysis mini(dump_file);
|
||||
DumpAnalysis full(full_dump_file);
|
||||
|
||||
// The dump should have all of these streams.
|
||||
EXPECT_TRUE(mini.HasStream(ThreadListStream));
|
||||
EXPECT_TRUE(full.HasStream(ThreadListStream));
|
||||
EXPECT_TRUE(mini.HasStream(ModuleListStream));
|
||||
EXPECT_TRUE(full.HasStream(ModuleListStream));
|
||||
EXPECT_TRUE(mini.HasStream(ExceptionStream));
|
||||
EXPECT_TRUE(full.HasStream(ExceptionStream));
|
||||
EXPECT_TRUE(mini.HasStream(SystemInfoStream));
|
||||
EXPECT_TRUE(full.HasStream(SystemInfoStream));
|
||||
EXPECT_TRUE(mini.HasStream(MiscInfoStream));
|
||||
EXPECT_TRUE(full.HasStream(MiscInfoStream));
|
||||
EXPECT_TRUE(mini.HasStream(HandleDataStream));
|
||||
EXPECT_TRUE(full.HasStream(HandleDataStream));
|
||||
|
||||
// We expect PEB and TEBs in this dump.
|
||||
EXPECT_TRUE(mini.HasTebs() || full.HasTebs());
|
||||
EXPECT_TRUE(mini.HasPeb() || full.HasPeb());
|
||||
|
||||
// Minidump should have a memory listing, but no 64-bit memory.
|
||||
EXPECT_TRUE(mini.HasStream(MemoryListStream));
|
||||
EXPECT_FALSE(mini.HasStream(Memory64ListStream));
|
||||
|
||||
EXPECT_FALSE(full.HasStream(MemoryListStream));
|
||||
EXPECT_TRUE(full.HasStream(Memory64ListStream));
|
||||
|
||||
// This is the only place we don't use OR because we want both not
|
||||
// to have the streams.
|
||||
EXPECT_FALSE(mini.HasStream(ThreadExListStream));
|
||||
EXPECT_FALSE(full.HasStream(ThreadExListStream));
|
||||
EXPECT_FALSE(mini.HasStream(CommentStreamA));
|
||||
EXPECT_FALSE(full.HasStream(CommentStreamA));
|
||||
EXPECT_FALSE(mini.HasStream(CommentStreamW));
|
||||
EXPECT_FALSE(full.HasStream(CommentStreamW));
|
||||
EXPECT_FALSE(mini.HasStream(FunctionTableStream));
|
||||
EXPECT_FALSE(full.HasStream(FunctionTableStream));
|
||||
EXPECT_FALSE(mini.HasStream(MemoryInfoListStream));
|
||||
EXPECT_FALSE(full.HasStream(MemoryInfoListStream));
|
||||
EXPECT_FALSE(mini.HasStream(ThreadInfoListStream));
|
||||
EXPECT_FALSE(full.HasStream(ThreadInfoListStream));
|
||||
EXPECT_FALSE(mini.HasStream(HandleOperationListStream));
|
||||
EXPECT_FALSE(full.HasStream(HandleOperationListStream));
|
||||
EXPECT_FALSE(mini.HasStream(TokenStream));
|
||||
EXPECT_FALSE(full.HasStream(TokenStream));
|
||||
}
|
||||
|
||||
|
||||
// This test validates that the minidump is written correctly.
|
||||
TEST_F(ExceptionHandlerTest, PureVirtualCallMiniDumpTest) {
|
||||
ASSERT_TRUE(DoesPathExist(temp_path_));
|
||||
|
||||
// Call with a bad argument
|
||||
ASSERT_TRUE(DoesPathExist(temp_path_));
|
||||
wstring dump_path(temp_path_);
|
||||
google_breakpad::CrashGenerationServer server(
|
||||
kPipeName, NULL, NULL, NULL, ClientDumpCallback, NULL, NULL, NULL, true,
|
||||
&dump_path);
|
||||
|
||||
ASSERT_TRUE(dump_file.empty() && full_dump_file.empty());
|
||||
|
||||
// This HAS to be EXPECT_, because when this test case is executed in the
|
||||
// child process, the server registration will fail due to the named pipe
|
||||
// being the same.
|
||||
EXPECT_TRUE(server.Start());
|
||||
EXPECT_EXIT(DoCrashPureVirtualCall(), ::testing::ExitedWithCode(0), "");
|
||||
ASSERT_TRUE(!dump_file.empty() && !full_dump_file.empty());
|
||||
ASSERT_TRUE(DoesPathExist(dump_file.c_str()));
|
||||
|
||||
// Verify the dump for infos.
|
||||
DumpAnalysis mini(dump_file);
|
||||
DumpAnalysis full(full_dump_file);
|
||||
|
||||
// The dump should have all of these streams.
|
||||
EXPECT_TRUE(mini.HasStream(ThreadListStream));
|
||||
EXPECT_TRUE(full.HasStream(ThreadListStream));
|
||||
EXPECT_TRUE(mini.HasStream(ModuleListStream));
|
||||
EXPECT_TRUE(full.HasStream(ModuleListStream));
|
||||
EXPECT_TRUE(mini.HasStream(ExceptionStream));
|
||||
EXPECT_TRUE(full.HasStream(ExceptionStream));
|
||||
EXPECT_TRUE(mini.HasStream(SystemInfoStream));
|
||||
EXPECT_TRUE(full.HasStream(SystemInfoStream));
|
||||
EXPECT_TRUE(mini.HasStream(MiscInfoStream));
|
||||
EXPECT_TRUE(full.HasStream(MiscInfoStream));
|
||||
EXPECT_TRUE(mini.HasStream(HandleDataStream));
|
||||
EXPECT_TRUE(full.HasStream(HandleDataStream));
|
||||
|
||||
// We expect PEB and TEBs in this dump.
|
||||
EXPECT_TRUE(mini.HasTebs() || full.HasTebs());
|
||||
EXPECT_TRUE(mini.HasPeb() || full.HasPeb());
|
||||
|
||||
// Minidump should have a memory listing, but no 64-bit memory.
|
||||
EXPECT_TRUE(mini.HasStream(MemoryListStream));
|
||||
EXPECT_FALSE(mini.HasStream(Memory64ListStream));
|
||||
|
||||
EXPECT_FALSE(full.HasStream(MemoryListStream));
|
||||
EXPECT_TRUE(full.HasStream(Memory64ListStream));
|
||||
|
||||
// This is the only place we don't use OR because we want both not
|
||||
// to have the streams.
|
||||
EXPECT_FALSE(mini.HasStream(ThreadExListStream));
|
||||
EXPECT_FALSE(full.HasStream(ThreadExListStream));
|
||||
EXPECT_FALSE(mini.HasStream(CommentStreamA));
|
||||
EXPECT_FALSE(full.HasStream(CommentStreamA));
|
||||
EXPECT_FALSE(mini.HasStream(CommentStreamW));
|
||||
EXPECT_FALSE(full.HasStream(CommentStreamW));
|
||||
EXPECT_FALSE(mini.HasStream(FunctionTableStream));
|
||||
EXPECT_FALSE(full.HasStream(FunctionTableStream));
|
||||
EXPECT_FALSE(mini.HasStream(MemoryInfoListStream));
|
||||
EXPECT_FALSE(full.HasStream(MemoryInfoListStream));
|
||||
EXPECT_FALSE(mini.HasStream(ThreadInfoListStream));
|
||||
EXPECT_FALSE(full.HasStream(ThreadInfoListStream));
|
||||
EXPECT_FALSE(mini.HasStream(HandleOperationListStream));
|
||||
EXPECT_FALSE(full.HasStream(HandleOperationListStream));
|
||||
EXPECT_FALSE(mini.HasStream(TokenStream));
|
||||
EXPECT_FALSE(full.HasStream(TokenStream));
|
||||
}
|
||||
|
||||
// Test that writing a minidump produces a valid minidump containing
|
||||
// some expected structures.
|
||||
TEST_F(ExceptionHandlerTest, WriteMinidumpTest) {
|
||||
ExceptionHandler handler(temp_path_,
|
||||
NULL,
|
||||
DumpCallback,
|
||||
NULL,
|
||||
ExceptionHandler::HANDLER_ALL);
|
||||
ASSERT_TRUE(handler.WriteMinidump());
|
||||
ASSERT_FALSE(dump_file.empty());
|
||||
|
||||
string minidump_filename;
|
||||
ASSERT_TRUE(WindowsStringUtils::safe_wcstombs(dump_file,
|
||||
&minidump_filename));
|
||||
|
||||
// Read the minidump and verify some info.
|
||||
Minidump minidump(minidump_filename);
|
||||
ASSERT_TRUE(minidump.Read());
|
||||
//TODO(ted): more comprehensive tests...
|
||||
}
|
||||
|
||||
} // namespace
|
332
thirdparty/breakpad/client/windows/unittests/minidump_test.cc
vendored
Executable file
332
thirdparty/breakpad/client/windows/unittests/minidump_test.cc
vendored
Executable file
@@ -0,0 +1,332 @@
|
||||
// Copyright (c) 2010, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <windows.h>
|
||||
#include <objbase.h>
|
||||
#include <dbghelp.h>
|
||||
|
||||
#include "../crash_generation/minidump_generator.h"
|
||||
#include "dump_analysis.h" // NOLINT
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace {
|
||||
|
||||
// Minidump with stacks, PEB, TEB, and unloaded module list.
|
||||
const MINIDUMP_TYPE kSmallDumpType = static_cast<MINIDUMP_TYPE>(
|
||||
MiniDumpWithProcessThreadData | // Get PEB and TEB.
|
||||
MiniDumpWithUnloadedModules); // Get unloaded modules when available.
|
||||
|
||||
// Minidump with all of the above, plus memory referenced from stack.
|
||||
const MINIDUMP_TYPE kLargerDumpType = static_cast<MINIDUMP_TYPE>(
|
||||
MiniDumpWithProcessThreadData | // Get PEB and TEB.
|
||||
MiniDumpWithUnloadedModules | // Get unloaded modules when available.
|
||||
MiniDumpWithIndirectlyReferencedMemory); // Get memory referenced by stack.
|
||||
|
||||
// Large dump with all process memory.
|
||||
const MINIDUMP_TYPE kFullDumpType = static_cast<MINIDUMP_TYPE>(
|
||||
MiniDumpWithFullMemory | // Full memory from process.
|
||||
MiniDumpWithProcessThreadData | // Get PEB and TEB.
|
||||
MiniDumpWithHandleData | // Get all handle information.
|
||||
MiniDumpWithUnloadedModules); // Get unloaded modules when available.
|
||||
|
||||
class MinidumpTest: public testing::Test {
|
||||
public:
|
||||
MinidumpTest() {
|
||||
wchar_t temp_dir_path[ MAX_PATH ] = {0};
|
||||
::GetTempPath(MAX_PATH, temp_dir_path);
|
||||
dump_path_ = temp_dir_path;
|
||||
}
|
||||
|
||||
virtual void SetUp() {
|
||||
// Make sure URLMon isn't loaded into our process.
|
||||
ASSERT_EQ(NULL, ::GetModuleHandle(L"urlmon.dll"));
|
||||
|
||||
// Then load and unload it to ensure we have something to
|
||||
// stock the unloaded module list with.
|
||||
HMODULE urlmon = ::LoadLibrary(L"urlmon.dll");
|
||||
ASSERT_TRUE(urlmon != NULL);
|
||||
ASSERT_TRUE(::FreeLibrary(urlmon));
|
||||
}
|
||||
|
||||
virtual void TearDown() {
|
||||
if (!dump_file_.empty()) {
|
||||
::DeleteFile(dump_file_.c_str());
|
||||
dump_file_ = L"";
|
||||
}
|
||||
if (!full_dump_file_.empty()) {
|
||||
::DeleteFile(full_dump_file_.c_str());
|
||||
full_dump_file_ = L"";
|
||||
}
|
||||
}
|
||||
|
||||
bool WriteDump(ULONG flags) {
|
||||
using google_breakpad::MinidumpGenerator;
|
||||
|
||||
// Fake exception is access violation on write to this.
|
||||
EXCEPTION_RECORD ex_record = {
|
||||
STATUS_ACCESS_VIOLATION, // ExceptionCode
|
||||
0, // ExceptionFlags
|
||||
NULL, // ExceptionRecord;
|
||||
reinterpret_cast<void*>(0xCAFEBABE), // ExceptionAddress;
|
||||
2, // NumberParameters;
|
||||
{ EXCEPTION_WRITE_FAULT, reinterpret_cast<ULONG_PTR>(this) }
|
||||
};
|
||||
CONTEXT ctx_record = {};
|
||||
EXCEPTION_POINTERS ex_ptrs = {
|
||||
&ex_record,
|
||||
&ctx_record,
|
||||
};
|
||||
|
||||
MinidumpGenerator generator(dump_path_);
|
||||
|
||||
// And write a dump
|
||||
bool result = generator.WriteMinidump(::GetCurrentProcess(),
|
||||
::GetCurrentProcessId(),
|
||||
::GetCurrentThreadId(),
|
||||
::GetCurrentThreadId(),
|
||||
&ex_ptrs,
|
||||
NULL,
|
||||
static_cast<MINIDUMP_TYPE>(flags),
|
||||
TRUE,
|
||||
&dump_file_,
|
||||
&full_dump_file_);
|
||||
return result == TRUE;
|
||||
}
|
||||
|
||||
protected:
|
||||
std::wstring dump_file_;
|
||||
std::wstring full_dump_file_;
|
||||
|
||||
std::wstring dump_path_;
|
||||
};
|
||||
|
||||
// We need to be able to get file information from Windows
|
||||
bool HasFileInfo(const std::wstring& file_path) {
|
||||
DWORD dummy;
|
||||
const wchar_t* path = file_path.c_str();
|
||||
DWORD length = ::GetFileVersionInfoSize(path, &dummy);
|
||||
if (length == 0)
|
||||
return NULL;
|
||||
|
||||
void* data = calloc(length, 1);
|
||||
if (!data)
|
||||
return false;
|
||||
|
||||
if (!::GetFileVersionInfo(path, dummy, length, data)) {
|
||||
free(data);
|
||||
return false;
|
||||
}
|
||||
|
||||
void* translate = NULL;
|
||||
UINT page_count;
|
||||
BOOL query_result = VerQueryValue(
|
||||
data,
|
||||
L"\\VarFileInfo\\Translation",
|
||||
static_cast<void**>(&translate),
|
||||
&page_count);
|
||||
|
||||
free(data);
|
||||
if (query_result && translate) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(MinidumpTest, Version) {
|
||||
API_VERSION* version = ::ImagehlpApiVersion();
|
||||
|
||||
HMODULE dbg_help = ::GetModuleHandle(L"dbghelp.dll");
|
||||
ASSERT_TRUE(dbg_help != NULL);
|
||||
|
||||
wchar_t dbg_help_file[1024] = {};
|
||||
ASSERT_TRUE(::GetModuleFileName(dbg_help,
|
||||
dbg_help_file,
|
||||
sizeof(dbg_help_file) /
|
||||
sizeof(*dbg_help_file)));
|
||||
ASSERT_TRUE(HasFileInfo(std::wstring(dbg_help_file)) != NULL);
|
||||
|
||||
// LOG(INFO) << "DbgHelp.dll version: " << file_info->file_version();
|
||||
}
|
||||
|
||||
TEST_F(MinidumpTest, Normal) {
|
||||
EXPECT_TRUE(WriteDump(MiniDumpNormal));
|
||||
DumpAnalysis mini(dump_file_);
|
||||
|
||||
// We expect threads, modules and some memory.
|
||||
EXPECT_TRUE(mini.HasStream(ThreadListStream));
|
||||
EXPECT_TRUE(mini.HasStream(ModuleListStream));
|
||||
EXPECT_TRUE(mini.HasStream(MemoryListStream));
|
||||
EXPECT_TRUE(mini.HasStream(ExceptionStream));
|
||||
EXPECT_TRUE(mini.HasStream(SystemInfoStream));
|
||||
EXPECT_TRUE(mini.HasStream(MiscInfoStream));
|
||||
|
||||
EXPECT_FALSE(mini.HasStream(ThreadExListStream));
|
||||
EXPECT_FALSE(mini.HasStream(Memory64ListStream));
|
||||
EXPECT_FALSE(mini.HasStream(CommentStreamA));
|
||||
EXPECT_FALSE(mini.HasStream(CommentStreamW));
|
||||
EXPECT_FALSE(mini.HasStream(HandleDataStream));
|
||||
EXPECT_FALSE(mini.HasStream(FunctionTableStream));
|
||||
EXPECT_FALSE(mini.HasStream(UnloadedModuleListStream));
|
||||
EXPECT_FALSE(mini.HasStream(MemoryInfoListStream));
|
||||
EXPECT_FALSE(mini.HasStream(ThreadInfoListStream));
|
||||
EXPECT_FALSE(mini.HasStream(HandleOperationListStream));
|
||||
EXPECT_FALSE(mini.HasStream(TokenStream));
|
||||
|
||||
// We expect no PEB nor TEBs in this dump.
|
||||
EXPECT_FALSE(mini.HasTebs());
|
||||
EXPECT_FALSE(mini.HasPeb());
|
||||
|
||||
// We expect no off-stack memory in this dump.
|
||||
EXPECT_FALSE(mini.HasMemory(this));
|
||||
}
|
||||
|
||||
TEST_F(MinidumpTest, SmallDump) {
|
||||
ASSERT_TRUE(WriteDump(kSmallDumpType));
|
||||
DumpAnalysis mini(dump_file_);
|
||||
|
||||
EXPECT_TRUE(mini.HasStream(ThreadListStream));
|
||||
EXPECT_TRUE(mini.HasStream(ModuleListStream));
|
||||
EXPECT_TRUE(mini.HasStream(MemoryListStream));
|
||||
EXPECT_TRUE(mini.HasStream(ExceptionStream));
|
||||
EXPECT_TRUE(mini.HasStream(SystemInfoStream));
|
||||
EXPECT_TRUE(mini.HasStream(UnloadedModuleListStream));
|
||||
EXPECT_TRUE(mini.HasStream(MiscInfoStream));
|
||||
|
||||
// We expect PEB and TEBs in this dump.
|
||||
EXPECT_TRUE(mini.HasTebs());
|
||||
EXPECT_TRUE(mini.HasPeb());
|
||||
|
||||
EXPECT_FALSE(mini.HasStream(ThreadExListStream));
|
||||
EXPECT_FALSE(mini.HasStream(Memory64ListStream));
|
||||
EXPECT_FALSE(mini.HasStream(CommentStreamA));
|
||||
EXPECT_FALSE(mini.HasStream(CommentStreamW));
|
||||
EXPECT_FALSE(mini.HasStream(HandleDataStream));
|
||||
EXPECT_FALSE(mini.HasStream(FunctionTableStream));
|
||||
EXPECT_FALSE(mini.HasStream(MemoryInfoListStream));
|
||||
EXPECT_FALSE(mini.HasStream(ThreadInfoListStream));
|
||||
EXPECT_FALSE(mini.HasStream(HandleOperationListStream));
|
||||
EXPECT_FALSE(mini.HasStream(TokenStream));
|
||||
|
||||
// We expect no off-stack memory in this dump.
|
||||
EXPECT_FALSE(mini.HasMemory(this));
|
||||
}
|
||||
|
||||
TEST_F(MinidumpTest, LargerDump) {
|
||||
ASSERT_TRUE(WriteDump(kLargerDumpType));
|
||||
DumpAnalysis mini(dump_file_);
|
||||
|
||||
// The dump should have all of these streams.
|
||||
EXPECT_TRUE(mini.HasStream(ThreadListStream));
|
||||
EXPECT_TRUE(mini.HasStream(ModuleListStream));
|
||||
EXPECT_TRUE(mini.HasStream(MemoryListStream));
|
||||
EXPECT_TRUE(mini.HasStream(ExceptionStream));
|
||||
EXPECT_TRUE(mini.HasStream(SystemInfoStream));
|
||||
EXPECT_TRUE(mini.HasStream(UnloadedModuleListStream));
|
||||
EXPECT_TRUE(mini.HasStream(MiscInfoStream));
|
||||
|
||||
// We expect memory referenced by stack in this dump.
|
||||
EXPECT_TRUE(mini.HasMemory(this));
|
||||
|
||||
// We expect PEB and TEBs in this dump.
|
||||
EXPECT_TRUE(mini.HasTebs());
|
||||
EXPECT_TRUE(mini.HasPeb());
|
||||
|
||||
EXPECT_FALSE(mini.HasStream(ThreadExListStream));
|
||||
EXPECT_FALSE(mini.HasStream(Memory64ListStream));
|
||||
EXPECT_FALSE(mini.HasStream(CommentStreamA));
|
||||
EXPECT_FALSE(mini.HasStream(CommentStreamW));
|
||||
EXPECT_FALSE(mini.HasStream(HandleDataStream));
|
||||
EXPECT_FALSE(mini.HasStream(FunctionTableStream));
|
||||
EXPECT_FALSE(mini.HasStream(MemoryInfoListStream));
|
||||
EXPECT_FALSE(mini.HasStream(ThreadInfoListStream));
|
||||
EXPECT_FALSE(mini.HasStream(HandleOperationListStream));
|
||||
EXPECT_FALSE(mini.HasStream(TokenStream));
|
||||
}
|
||||
|
||||
TEST_F(MinidumpTest, FullDump) {
|
||||
ASSERT_TRUE(WriteDump(kFullDumpType));
|
||||
ASSERT_TRUE(dump_file_ != L"");
|
||||
ASSERT_TRUE(full_dump_file_ != L"");
|
||||
DumpAnalysis mini(dump_file_);
|
||||
DumpAnalysis full(full_dump_file_);
|
||||
|
||||
// Either dumps can contain part of the information.
|
||||
|
||||
// The dump should have all of these streams.
|
||||
EXPECT_TRUE(mini.HasStream(ThreadListStream));
|
||||
EXPECT_TRUE(full.HasStream(ThreadListStream));
|
||||
EXPECT_TRUE(mini.HasStream(ModuleListStream));
|
||||
EXPECT_TRUE(full.HasStream(ModuleListStream));
|
||||
EXPECT_TRUE(mini.HasStream(ExceptionStream));
|
||||
EXPECT_TRUE(full.HasStream(ExceptionStream));
|
||||
EXPECT_TRUE(mini.HasStream(SystemInfoStream));
|
||||
EXPECT_TRUE(full.HasStream(SystemInfoStream));
|
||||
EXPECT_TRUE(mini.HasStream(UnloadedModuleListStream));
|
||||
EXPECT_TRUE(full.HasStream(UnloadedModuleListStream));
|
||||
EXPECT_TRUE(mini.HasStream(MiscInfoStream));
|
||||
EXPECT_TRUE(full.HasStream(MiscInfoStream));
|
||||
EXPECT_TRUE(mini.HasStream(HandleDataStream));
|
||||
EXPECT_TRUE(full.HasStream(HandleDataStream));
|
||||
|
||||
// We expect memory referenced by stack in this dump.
|
||||
EXPECT_FALSE(mini.HasMemory(this));
|
||||
EXPECT_TRUE(full.HasMemory(this));
|
||||
|
||||
// We expect PEB and TEBs in this dump.
|
||||
EXPECT_TRUE(mini.HasTebs() || full.HasTebs());
|
||||
EXPECT_TRUE(mini.HasPeb() || full.HasPeb());
|
||||
|
||||
EXPECT_TRUE(mini.HasStream(MemoryListStream));
|
||||
EXPECT_TRUE(full.HasStream(Memory64ListStream));
|
||||
EXPECT_FALSE(mini.HasStream(Memory64ListStream));
|
||||
EXPECT_FALSE(full.HasStream(MemoryListStream));
|
||||
|
||||
// This is the only place we don't use OR because we want both not
|
||||
// to have the streams.
|
||||
EXPECT_FALSE(mini.HasStream(ThreadExListStream));
|
||||
EXPECT_FALSE(full.HasStream(ThreadExListStream));
|
||||
EXPECT_FALSE(mini.HasStream(CommentStreamA));
|
||||
EXPECT_FALSE(full.HasStream(CommentStreamA));
|
||||
EXPECT_FALSE(mini.HasStream(CommentStreamW));
|
||||
EXPECT_FALSE(full.HasStream(CommentStreamW));
|
||||
EXPECT_FALSE(mini.HasStream(FunctionTableStream));
|
||||
EXPECT_FALSE(full.HasStream(FunctionTableStream));
|
||||
EXPECT_FALSE(mini.HasStream(MemoryInfoListStream));
|
||||
EXPECT_FALSE(full.HasStream(MemoryInfoListStream));
|
||||
EXPECT_FALSE(mini.HasStream(ThreadInfoListStream));
|
||||
EXPECT_FALSE(full.HasStream(ThreadInfoListStream));
|
||||
EXPECT_FALSE(mini.HasStream(HandleOperationListStream));
|
||||
EXPECT_FALSE(full.HasStream(HandleOperationListStream));
|
||||
EXPECT_FALSE(mini.HasStream(TokenStream));
|
||||
EXPECT_FALSE(full.HasStream(TokenStream));
|
||||
}
|
||||
|
||||
} // namespace
|
77
thirdparty/breakpad/client/windows/unittests/testing.gyp
vendored
Normal file
77
thirdparty/breakpad/client/windows/unittests/testing.gyp
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
# Copyright (c) 2010, Google Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following disclaimer
|
||||
# in the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of Google Inc. nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
{
|
||||
'includes': [
|
||||
'../build/common.gypi',
|
||||
],
|
||||
'target_defaults': {
|
||||
},
|
||||
'targets': [
|
||||
{
|
||||
'target_name': 'gtest',
|
||||
'type': '<(library)',
|
||||
'include_dirs': [
|
||||
'<(DEPTH)/testing/include',
|
||||
'<(DEPTH)/testing/gtest',
|
||||
'<(DEPTH)/testing/gtest/include',
|
||||
],
|
||||
'sources': [
|
||||
'<(DEPTH)/testing/gtest/src/gtest-all.cc',
|
||||
],
|
||||
'direct_dependent_settings': {
|
||||
'include_dirs': [
|
||||
'<(DEPTH)/testing/include',
|
||||
'<(DEPTH)/testing/gtest/include',
|
||||
]
|
||||
},
|
||||
},
|
||||
{
|
||||
'target_name': 'gmock',
|
||||
'type': '<(library)',
|
||||
'include_dirs': [
|
||||
'<(DEPTH)/testing/include',
|
||||
'<(DEPTH)/testing/',
|
||||
'<(DEPTH)/testing/gtest',
|
||||
'<(DEPTH)/testing/gtest/include',
|
||||
],
|
||||
'sources': [
|
||||
'<(DEPTH)/testing/src/gmock-all.cc',
|
||||
'<(DEPTH)/testing/src/gmock_main.cc',
|
||||
],
|
||||
'direct_dependent_settings': {
|
||||
'include_dirs': [
|
||||
'<(DEPTH)/testing/include',
|
||||
'<(DEPTH)/testing/gtest/include',
|
||||
]
|
||||
},
|
||||
},
|
||||
|
||||
],
|
||||
}
|
Reference in New Issue
Block a user