Home | History | Annotate | Download | only in unittests
      1 // Copyright 2009, Google Inc.
      2 // All rights reserved.
      3 //
      4 // Redistribution and use in source and binary forms, with or without
      5 // modification, are permitted provided that the following conditions are
      6 // met:
      7 //
      8 //     * Redistributions of source code must retain the above copyright
      9 // notice, this list of conditions and the following disclaimer.
     10 //     * Redistributions in binary form must reproduce the above
     11 // copyright notice, this list of conditions and the following disclaimer
     12 // in the documentation and/or other materials provided with the
     13 // distribution.
     14 //     * Neither the name of Google Inc. nor the names of its
     15 // contributors may be used to endorse or promote products derived from
     16 // this software without specific prior written permission.
     17 //
     18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29 
     30 #include "client/windows/unittests/exception_handler_test.h"
     31 
     32 #include <windows.h>
     33 #include <dbghelp.h>
     34 #include <strsafe.h>
     35 #include <objbase.h>
     36 #include <shellapi.h>
     37 
     38 #include <string>
     39 
     40 #include "breakpad_googletest_includes.h"
     41 #include "client/windows/crash_generation/crash_generation_server.h"
     42 #include "client/windows/handler/exception_handler.h"
     43 #include "client/windows/unittests/dump_analysis.h"  // NOLINT
     44 #include "common/windows/string_utils-inl.h"
     45 #include "google_breakpad/processor/minidump.h"
     46 
     47 namespace testing {
     48 
     49 DisableExceptionHandlerInScope::DisableExceptionHandlerInScope() {
     50   catch_exceptions_ = GTEST_FLAG(catch_exceptions);
     51   GTEST_FLAG(catch_exceptions) = false;
     52 }
     53 
     54 DisableExceptionHandlerInScope::~DisableExceptionHandlerInScope() {
     55   GTEST_FLAG(catch_exceptions) = catch_exceptions_;
     56 }
     57 
     58 }  // namespace testing
     59 
     60 namespace {
     61 
     62 using std::wstring;
     63 using namespace google_breakpad;
     64 
     65 const wchar_t kPipeName[] = L"\\\\.\\pipe\\BreakpadCrashTest\\TestCaseServer";
     66 const char kSuccessIndicator[] = "success";
     67 const char kFailureIndicator[] = "failure";
     68 
     69 const MINIDUMP_TYPE kFullDumpType = static_cast<MINIDUMP_TYPE>(
     70     MiniDumpWithFullMemory |  // Full memory from process.
     71     MiniDumpWithProcessThreadData |  // Get PEB and TEB.
     72     MiniDumpWithHandleData);  // Get all handle information.
     73 
     74 class ExceptionHandlerTest : public ::testing::Test {
     75  protected:
     76   // Member variable for each test that they can use
     77   // for temporary storage.
     78   TCHAR temp_path_[MAX_PATH];
     79 
     80   // Actually constructs a temp path name.
     81   virtual void SetUp();
     82 
     83   // Deletes temporary files.
     84   virtual void TearDown();
     85 
     86   void DoCrashInvalidParameter();
     87   void DoCrashPureVirtualCall();
     88 
     89   // Utility function to test for a path's existence.
     90   static BOOL DoesPathExist(const TCHAR *path_name);
     91 
     92   // Client callback.
     93   static void ClientDumpCallback(
     94       void *dump_context,
     95       const google_breakpad::ClientInfo *client_info,
     96       const std::wstring *dump_path);
     97 
     98   static bool DumpCallback(const wchar_t* dump_path,
     99                            const wchar_t* minidump_id,
    100                            void* context,
    101                            EXCEPTION_POINTERS* exinfo,
    102                            MDRawAssertionInfo* assertion,
    103                            bool succeeded);
    104 
    105   static std::wstring dump_file;
    106   static std::wstring full_dump_file;
    107 };
    108 
    109 std::wstring ExceptionHandlerTest::dump_file;
    110 std::wstring ExceptionHandlerTest::full_dump_file;
    111 
    112 void ExceptionHandlerTest::SetUp() {
    113   const ::testing::TestInfo* const test_info =
    114     ::testing::UnitTest::GetInstance()->current_test_info();
    115   TCHAR temp_path[MAX_PATH] = { '\0' };
    116   TCHAR test_name_wide[MAX_PATH] = { '\0' };
    117   // We want the temporary directory to be what the OS returns
    118   // to us, + the test case name.
    119   GetTempPath(MAX_PATH, temp_path);
    120   // THe test case name is exposed to use as a c-style string,
    121   // But we might be working in UNICODE here on Windows.
    122   int dwRet = MultiByteToWideChar(CP_ACP, 0, test_info->name(),
    123                                   strlen(test_info->name()),
    124                                   test_name_wide,
    125                                   MAX_PATH);
    126   if (!dwRet) {
    127     assert(false);
    128   }
    129   StringCchPrintfW(temp_path_, MAX_PATH, L"%s%s", temp_path, test_name_wide);
    130   CreateDirectory(temp_path_, NULL);
    131 }
    132 
    133 void ExceptionHandlerTest::TearDown() {
    134   if (!dump_file.empty()) {
    135     ::DeleteFile(dump_file.c_str());
    136     dump_file = L"";
    137   }
    138   if (!full_dump_file.empty()) {
    139     ::DeleteFile(full_dump_file.c_str());
    140     full_dump_file = L"";
    141   }
    142 }
    143 
    144 BOOL ExceptionHandlerTest::DoesPathExist(const TCHAR *path_name) {
    145   DWORD flags = GetFileAttributes(path_name);
    146   if (flags == INVALID_FILE_ATTRIBUTES) {
    147     return FALSE;
    148   }
    149   return TRUE;
    150 }
    151 
    152 // static
    153 void ExceptionHandlerTest::ClientDumpCallback(
    154     void *dump_context,
    155     const google_breakpad::ClientInfo *client_info,
    156     const wstring *dump_path) {
    157   dump_file = *dump_path;
    158   // Create the full dump file name from the dump path.
    159   full_dump_file = dump_file.substr(0, dump_file.length() - 4) + L"-full.dmp";
    160 }
    161 
    162 // static
    163 bool ExceptionHandlerTest::DumpCallback(const wchar_t* dump_path,
    164                     const wchar_t* minidump_id,
    165                     void* context,
    166                     EXCEPTION_POINTERS* exinfo,
    167                     MDRawAssertionInfo* assertion,
    168                     bool succeeded) {
    169   dump_file = dump_path;
    170   dump_file += L"\\";
    171   dump_file += minidump_id;
    172   dump_file += L".dmp";
    173     return succeeded;
    174 }
    175 
    176 void ExceptionHandlerTest::DoCrashInvalidParameter() {
    177   google_breakpad::ExceptionHandler *exc =
    178       new google_breakpad::ExceptionHandler(
    179           temp_path_, NULL, NULL, NULL,
    180           google_breakpad::ExceptionHandler::HANDLER_INVALID_PARAMETER,
    181           kFullDumpType, kPipeName, NULL);
    182 
    183   // Disable the message box for assertions
    184   _CrtSetReportMode(_CRT_ASSERT, 0);
    185 
    186   // Although this is executing in the child process of the death test,
    187   // if it's not true we'll still get an error rather than the crash
    188   // being expected.
    189   ASSERT_TRUE(exc->IsOutOfProcess());
    190   printf(NULL);
    191 }
    192 
    193 
    194 struct PureVirtualCallBase {
    195   PureVirtualCallBase() {
    196     // We have to reinterpret so the linker doesn't get confused because the
    197     // method isn't defined.
    198     reinterpret_cast<PureVirtualCallBase*>(this)->PureFunction();
    199   }
    200   virtual ~PureVirtualCallBase() {}
    201   virtual void PureFunction() const = 0;
    202 };
    203 struct PureVirtualCall : public PureVirtualCallBase {
    204   PureVirtualCall() { PureFunction(); }
    205   virtual void PureFunction() const {}
    206 };
    207 
    208 void ExceptionHandlerTest::DoCrashPureVirtualCall() {
    209   google_breakpad::ExceptionHandler *exc =
    210       new google_breakpad::ExceptionHandler(
    211           temp_path_, NULL, NULL, NULL,
    212           google_breakpad::ExceptionHandler::HANDLER_PURECALL,
    213           kFullDumpType, kPipeName, NULL);
    214 
    215   // Disable the message box for assertions
    216   _CrtSetReportMode(_CRT_ASSERT, 0);
    217 
    218   // Although this is executing in the child process of the death test,
    219   // if it's not true we'll still get an error rather than the crash
    220   // being expected.
    221   ASSERT_TRUE(exc->IsOutOfProcess());
    222 
    223   // Create a new frame to ensure PureVirtualCall is not optimized to some
    224   // other line in this function.
    225   {
    226     PureVirtualCall instance;
    227   }
    228 }
    229 
    230 // This test validates that the minidump is written correctly.
    231 TEST_F(ExceptionHandlerTest, InvalidParameterMiniDumpTest) {
    232   ASSERT_TRUE(DoesPathExist(temp_path_));
    233 
    234   // Call with a bad argument
    235   ASSERT_TRUE(DoesPathExist(temp_path_));
    236   wstring dump_path(temp_path_);
    237   google_breakpad::CrashGenerationServer server(
    238       kPipeName, NULL, NULL, NULL, ClientDumpCallback, NULL, NULL, NULL, NULL,
    239       NULL, true, &dump_path);
    240 
    241   ASSERT_TRUE(dump_file.empty() && full_dump_file.empty());
    242 
    243   // This HAS to be EXPECT_, because when this test case is executed in the
    244   // child process, the server registration will fail due to the named pipe
    245   // being the same.
    246   EXPECT_TRUE(server.Start());
    247   EXPECT_EXIT(DoCrashInvalidParameter(), ::testing::ExitedWithCode(0), "");
    248   ASSERT_TRUE(!dump_file.empty() && !full_dump_file.empty());
    249   ASSERT_TRUE(DoesPathExist(dump_file.c_str()));
    250 
    251   // Verify the dump for infos.
    252   DumpAnalysis mini(dump_file);
    253   DumpAnalysis full(full_dump_file);
    254 
    255   // The dump should have all of these streams.
    256   EXPECT_TRUE(mini.HasStream(ThreadListStream));
    257   EXPECT_TRUE(full.HasStream(ThreadListStream));
    258   EXPECT_TRUE(mini.HasStream(ModuleListStream));
    259   EXPECT_TRUE(full.HasStream(ModuleListStream));
    260   EXPECT_TRUE(mini.HasStream(ExceptionStream));
    261   EXPECT_TRUE(full.HasStream(ExceptionStream));
    262   EXPECT_TRUE(mini.HasStream(SystemInfoStream));
    263   EXPECT_TRUE(full.HasStream(SystemInfoStream));
    264   EXPECT_TRUE(mini.HasStream(MiscInfoStream));
    265   EXPECT_TRUE(full.HasStream(MiscInfoStream));
    266   EXPECT_TRUE(mini.HasStream(HandleDataStream));
    267   EXPECT_TRUE(full.HasStream(HandleDataStream));
    268 
    269   // We expect PEB and TEBs in this dump.
    270   EXPECT_TRUE(mini.HasTebs() || full.HasTebs());
    271   EXPECT_TRUE(mini.HasPeb() || full.HasPeb());
    272 
    273   // Minidump should have a memory listing, but no 64-bit memory.
    274   EXPECT_TRUE(mini.HasStream(MemoryListStream));
    275   EXPECT_FALSE(mini.HasStream(Memory64ListStream));
    276 
    277   EXPECT_FALSE(full.HasStream(MemoryListStream));
    278   EXPECT_TRUE(full.HasStream(Memory64ListStream));
    279 
    280   // This is the only place we don't use OR because we want both not
    281   // to have the streams.
    282   EXPECT_FALSE(mini.HasStream(ThreadExListStream));
    283   EXPECT_FALSE(full.HasStream(ThreadExListStream));
    284   EXPECT_FALSE(mini.HasStream(CommentStreamA));
    285   EXPECT_FALSE(full.HasStream(CommentStreamA));
    286   EXPECT_FALSE(mini.HasStream(CommentStreamW));
    287   EXPECT_FALSE(full.HasStream(CommentStreamW));
    288   EXPECT_FALSE(mini.HasStream(FunctionTableStream));
    289   EXPECT_FALSE(full.HasStream(FunctionTableStream));
    290   EXPECT_FALSE(mini.HasStream(MemoryInfoListStream));
    291   EXPECT_FALSE(full.HasStream(MemoryInfoListStream));
    292   EXPECT_FALSE(mini.HasStream(ThreadInfoListStream));
    293   EXPECT_FALSE(full.HasStream(ThreadInfoListStream));
    294   EXPECT_FALSE(mini.HasStream(HandleOperationListStream));
    295   EXPECT_FALSE(full.HasStream(HandleOperationListStream));
    296   EXPECT_FALSE(mini.HasStream(TokenStream));
    297   EXPECT_FALSE(full.HasStream(TokenStream));
    298 }
    299 
    300 
    301 // This test validates that the minidump is written correctly.
    302 TEST_F(ExceptionHandlerTest, PureVirtualCallMiniDumpTest) {
    303   ASSERT_TRUE(DoesPathExist(temp_path_));
    304 
    305   // Call with a bad argument
    306   ASSERT_TRUE(DoesPathExist(temp_path_));
    307   wstring dump_path(temp_path_);
    308   google_breakpad::CrashGenerationServer server(
    309       kPipeName, NULL, NULL, NULL, ClientDumpCallback, NULL, NULL, NULL, NULL,
    310       NULL, true, &dump_path);
    311 
    312   ASSERT_TRUE(dump_file.empty() && full_dump_file.empty());
    313 
    314   // This HAS to be EXPECT_, because when this test case is executed in the
    315   // child process, the server registration will fail due to the named pipe
    316   // being the same.
    317   EXPECT_TRUE(server.Start());
    318   EXPECT_EXIT(DoCrashPureVirtualCall(), ::testing::ExitedWithCode(0), "");
    319   ASSERT_TRUE(!dump_file.empty() && !full_dump_file.empty());
    320   ASSERT_TRUE(DoesPathExist(dump_file.c_str()));
    321 
    322   // Verify the dump for infos.
    323   DumpAnalysis mini(dump_file);
    324   DumpAnalysis full(full_dump_file);
    325 
    326   // The dump should have all of these streams.
    327   EXPECT_TRUE(mini.HasStream(ThreadListStream));
    328   EXPECT_TRUE(full.HasStream(ThreadListStream));
    329   EXPECT_TRUE(mini.HasStream(ModuleListStream));
    330   EXPECT_TRUE(full.HasStream(ModuleListStream));
    331   EXPECT_TRUE(mini.HasStream(ExceptionStream));
    332   EXPECT_TRUE(full.HasStream(ExceptionStream));
    333   EXPECT_TRUE(mini.HasStream(SystemInfoStream));
    334   EXPECT_TRUE(full.HasStream(SystemInfoStream));
    335   EXPECT_TRUE(mini.HasStream(MiscInfoStream));
    336   EXPECT_TRUE(full.HasStream(MiscInfoStream));
    337   EXPECT_TRUE(mini.HasStream(HandleDataStream));
    338   EXPECT_TRUE(full.HasStream(HandleDataStream));
    339 
    340   // We expect PEB and TEBs in this dump.
    341   EXPECT_TRUE(mini.HasTebs() || full.HasTebs());
    342   EXPECT_TRUE(mini.HasPeb() || full.HasPeb());
    343 
    344   // Minidump should have a memory listing, but no 64-bit memory.
    345   EXPECT_TRUE(mini.HasStream(MemoryListStream));
    346   EXPECT_FALSE(mini.HasStream(Memory64ListStream));
    347 
    348   EXPECT_FALSE(full.HasStream(MemoryListStream));
    349   EXPECT_TRUE(full.HasStream(Memory64ListStream));
    350 
    351   // This is the only place we don't use OR because we want both not
    352   // to have the streams.
    353   EXPECT_FALSE(mini.HasStream(ThreadExListStream));
    354   EXPECT_FALSE(full.HasStream(ThreadExListStream));
    355   EXPECT_FALSE(mini.HasStream(CommentStreamA));
    356   EXPECT_FALSE(full.HasStream(CommentStreamA));
    357   EXPECT_FALSE(mini.HasStream(CommentStreamW));
    358   EXPECT_FALSE(full.HasStream(CommentStreamW));
    359   EXPECT_FALSE(mini.HasStream(FunctionTableStream));
    360   EXPECT_FALSE(full.HasStream(FunctionTableStream));
    361   EXPECT_FALSE(mini.HasStream(MemoryInfoListStream));
    362   EXPECT_FALSE(full.HasStream(MemoryInfoListStream));
    363   EXPECT_FALSE(mini.HasStream(ThreadInfoListStream));
    364   EXPECT_FALSE(full.HasStream(ThreadInfoListStream));
    365   EXPECT_FALSE(mini.HasStream(HandleOperationListStream));
    366   EXPECT_FALSE(full.HasStream(HandleOperationListStream));
    367   EXPECT_FALSE(mini.HasStream(TokenStream));
    368   EXPECT_FALSE(full.HasStream(TokenStream));
    369 }
    370 
    371 // Test that writing a minidump produces a valid minidump containing
    372 // some expected structures.
    373 TEST_F(ExceptionHandlerTest, WriteMinidumpTest) {
    374   ExceptionHandler handler(temp_path_,
    375                            NULL,
    376                            DumpCallback,
    377                            NULL,
    378                            ExceptionHandler::HANDLER_ALL);
    379 
    380   // Disable GTest SEH handler
    381   testing::DisableExceptionHandlerInScope disable_exception_handler;
    382 
    383   ASSERT_TRUE(handler.WriteMinidump());
    384   ASSERT_FALSE(dump_file.empty());
    385 
    386   string minidump_filename;
    387   ASSERT_TRUE(WindowsStringUtils::safe_wcstombs(dump_file,
    388                                                 &minidump_filename));
    389 
    390   // Read the minidump and verify some info.
    391   Minidump minidump(minidump_filename);
    392   ASSERT_TRUE(minidump.Read());
    393   // TODO(ted): more comprehensive tests...
    394 }
    395 
    396 // Test that an additional memory region can be included in the minidump.
    397 TEST_F(ExceptionHandlerTest, AdditionalMemory) {
    398   SYSTEM_INFO si;
    399   GetSystemInfo(&si);
    400   const uint32_t kMemorySize = si.dwPageSize;
    401 
    402   // Get some heap memory.
    403   uint8_t* memory = new uint8_t[kMemorySize];
    404   const uintptr_t kMemoryAddress = reinterpret_cast<uintptr_t>(memory);
    405   ASSERT_TRUE(memory);
    406 
    407   // Stick some data into the memory so the contents can be verified.
    408   for (uint32_t i = 0; i < kMemorySize; ++i) {
    409     memory[i] = i % 255;
    410   }
    411 
    412   ExceptionHandler handler(temp_path_,
    413                            NULL,
    414                            DumpCallback,
    415                            NULL,
    416                            ExceptionHandler::HANDLER_ALL);
    417 
    418   // Disable GTest SEH handler
    419   testing::DisableExceptionHandlerInScope disable_exception_handler;
    420 
    421   // Add the memory region to the list of memory to be included.
    422   handler.RegisterAppMemory(memory, kMemorySize);
    423   ASSERT_TRUE(handler.WriteMinidump());
    424   ASSERT_FALSE(dump_file.empty());
    425 
    426   string minidump_filename;
    427   ASSERT_TRUE(WindowsStringUtils::safe_wcstombs(dump_file,
    428                                                 &minidump_filename));
    429 
    430   // Read the minidump. Ensure that the memory region is present
    431   Minidump minidump(minidump_filename);
    432   ASSERT_TRUE(minidump.Read());
    433 
    434   MinidumpMemoryList* dump_memory_list = minidump.GetMemoryList();
    435   ASSERT_TRUE(dump_memory_list);
    436   const MinidumpMemoryRegion* region =
    437     dump_memory_list->GetMemoryRegionForAddress(kMemoryAddress);
    438   ASSERT_TRUE(region);
    439 
    440   EXPECT_EQ(kMemoryAddress, region->GetBase());
    441   EXPECT_EQ(kMemorySize, region->GetSize());
    442 
    443   // Verify memory contents.
    444   EXPECT_EQ(0, memcmp(region->GetMemory(), memory, kMemorySize));
    445 
    446   delete[] memory;
    447 }
    448 
    449 // Test that a memory region that was previously registered
    450 // can be unregistered.
    451 TEST_F(ExceptionHandlerTest, AdditionalMemoryRemove) {
    452   SYSTEM_INFO si;
    453   GetSystemInfo(&si);
    454   const uint32_t kMemorySize = si.dwPageSize;
    455 
    456   // Get some heap memory.
    457   uint8_t* memory = new uint8_t[kMemorySize];
    458   const uintptr_t kMemoryAddress = reinterpret_cast<uintptr_t>(memory);
    459   ASSERT_TRUE(memory);
    460 
    461   // Stick some data into the memory so the contents can be verified.
    462   for (uint32_t i = 0; i < kMemorySize; ++i) {
    463     memory[i] = i % 255;
    464   }
    465 
    466   ExceptionHandler handler(temp_path_,
    467                            NULL,
    468                            DumpCallback,
    469                            NULL,
    470                            ExceptionHandler::HANDLER_ALL);
    471 
    472   // Disable GTest SEH handler
    473   testing::DisableExceptionHandlerInScope disable_exception_handler;
    474 
    475   // Add the memory region to the list of memory to be included.
    476   handler.RegisterAppMemory(memory, kMemorySize);
    477 
    478   // ...and then remove it
    479   handler.UnregisterAppMemory(memory);
    480 
    481   ASSERT_TRUE(handler.WriteMinidump());
    482   ASSERT_FALSE(dump_file.empty());
    483 
    484   string minidump_filename;
    485   ASSERT_TRUE(WindowsStringUtils::safe_wcstombs(dump_file,
    486                                                 &minidump_filename));
    487 
    488   // Read the minidump. Ensure that the memory region is not present.
    489   Minidump minidump(minidump_filename);
    490   ASSERT_TRUE(minidump.Read());
    491 
    492   MinidumpMemoryList* dump_memory_list = minidump.GetMemoryList();
    493   ASSERT_TRUE(dump_memory_list);
    494   const MinidumpMemoryRegion* region =
    495     dump_memory_list->GetMemoryRegionForAddress(kMemoryAddress);
    496   EXPECT_FALSE(region);
    497 
    498   delete[] memory;
    499 }
    500 
    501 }  // namespace
    502