| // 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 "breakpad_googletest_includes.h" |
| #include "client/windows/crash_generation/minidump_generator.h" |
| #include "client/windows/unittests/dump_analysis.h" // NOLINT |
| |
| 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*>(static_cast<uintptr_t>(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_, |
| ::GetCurrentProcess(), |
| ::GetCurrentProcessId(), |
| ::GetCurrentThreadId(), |
| ::GetCurrentThreadId(), |
| &ex_ptrs, |
| NULL, |
| static_cast<MINIDUMP_TYPE>(flags), |
| TRUE); |
| generator.GenerateDumpFile(&dump_file_); |
| generator.GenerateFullDumpFile(&full_dump_file_); |
| // And write a dump |
| bool result = generator.WriteMinidump(); |
| 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) { |
| // Loads DbgHelp.dll in process |
| 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 |