// 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 #include #include #include #include #include "../../../breakpad_googletest_includes.h" #include "../crash_generation/crash_generation_server.h" #include "../handler/exception_handler.h" #include "dump_analysis.h" // NOLINT namespace { const wchar_t kPipeName[] = L"\\\\.\\pipe\\BreakpadCrashTest\\TestCaseServer"; const char kSuccessIndicator[] = "success"; const char kFailureIndicator[] = "failure"; const MINIDUMP_TYPE kFullDumpType = static_cast( 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 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; } void ExceptionHandlerTest::ClientDumpCallback( void *dump_context, const google_breakpad::ClientInfo *client_info, const std::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"; } 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(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_)); std::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_)); std::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)); } }