mirror of
https://github.com/tomahawk-player/tomahawk.git
synced 2025-08-10 16:14:40 +02:00
* Added breakpad support for Linux.
This commit is contained in:
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
|
Reference in New Issue
Block a user