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