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 <windows.h> 31 #include <dbghelp.h> 32 #include <strsafe.h> 33 #include <objbase.h> 34 #include <shellapi.h> 35 36 #include <string> 37 38 #include "breakpad_googletest_includes.h" 39 #include "client/windows/crash_generation/crash_generation_server.h" 40 #include "client/windows/handler/exception_handler.h" 41 #include "client/windows/unittests/exception_handler_test.h" 42 #include "common/windows/string_utils-inl.h" 43 #include "google_breakpad/processor/minidump.h" 44 45 namespace { 46 47 using std::wstring; 48 using namespace google_breakpad; 49 50 const wchar_t kPipeName[] = L"\\\\.\\pipe\\BreakpadCrashTest\\TestCaseServer"; 51 const char kSuccessIndicator[] = "success"; 52 const char kFailureIndicator[] = "failure"; 53 54 // Utility function to test for a path's existence. 55 BOOL DoesPathExist(const TCHAR *path_name); 56 57 enum OutOfProcGuarantee { 58 OUT_OF_PROC_GUARANTEED, 59 OUT_OF_PROC_BEST_EFFORT, 60 }; 61 62 class ExceptionHandlerDeathTest : public ::testing::Test { 63 protected: 64 // Member variable for each test that they can use 65 // for temporary storage. 66 TCHAR temp_path_[MAX_PATH]; 67 // Actually constructs a temp path name. 68 virtual void SetUp(); 69 // A helper method that tests can use to crash. 70 void DoCrashAccessViolation(const OutOfProcGuarantee out_of_proc_guarantee); 71 void DoCrashPureVirtualCall(); 72 }; 73 74 void ExceptionHandlerDeathTest::SetUp() { 75 const ::testing::TestInfo* const test_info = 76 ::testing::UnitTest::GetInstance()->current_test_info(); 77 TCHAR temp_path[MAX_PATH] = { '\0' }; 78 TCHAR test_name_wide[MAX_PATH] = { '\0' }; 79 // We want the temporary directory to be what the OS returns 80 // to us, + the test case name. 81 GetTempPath(MAX_PATH, temp_path); 82 // The test case name is exposed as a c-style string, 83 // convert it to a wchar_t string. 84 int dwRet = MultiByteToWideChar(CP_ACP, 0, test_info->name(), 85 strlen(test_info->name()), 86 test_name_wide, 87 MAX_PATH); 88 if (!dwRet) { 89 assert(false); 90 } 91 StringCchPrintfW(temp_path_, MAX_PATH, L"%s%s", temp_path, test_name_wide); 92 CreateDirectory(temp_path_, NULL); 93 } 94 95 BOOL DoesPathExist(const TCHAR *path_name) { 96 DWORD flags = GetFileAttributes(path_name); 97 if (flags == INVALID_FILE_ATTRIBUTES) { 98 return FALSE; 99 } 100 return TRUE; 101 } 102 103 bool MinidumpWrittenCallback(const wchar_t* dump_path, 104 const wchar_t* minidump_id, 105 void* context, 106 EXCEPTION_POINTERS* exinfo, 107 MDRawAssertionInfo* assertion, 108 bool succeeded) { 109 if (succeeded && DoesPathExist(dump_path)) { 110 fprintf(stderr, kSuccessIndicator); 111 } else { 112 fprintf(stderr, kFailureIndicator); 113 } 114 // If we don't flush, the output doesn't get sent before 115 // this process dies. 116 fflush(stderr); 117 return succeeded; 118 } 119 120 TEST_F(ExceptionHandlerDeathTest, InProcTest) { 121 // For the in-proc test, we just need to instantiate an exception 122 // handler in in-proc mode, and crash. Since the entire test is 123 // reexecuted in the child process, we don't have to worry about 124 // the semantics of the exception handler being inherited/not 125 // inherited across CreateProcess(). 126 ASSERT_TRUE(DoesPathExist(temp_path_)); 127 scoped_ptr<google_breakpad::ExceptionHandler> exc( 128 new google_breakpad::ExceptionHandler( 129 temp_path_, 130 NULL, 131 &MinidumpWrittenCallback, 132 NULL, 133 google_breakpad::ExceptionHandler::HANDLER_ALL)); 134 135 // Disable GTest SEH handler 136 testing::DisableExceptionHandlerInScope disable_exception_handler; 137 138 int *i = NULL; 139 ASSERT_DEATH((*i)++, kSuccessIndicator); 140 } 141 142 static bool gDumpCallbackCalled = false; 143 144 void clientDumpCallback(void *dump_context, 145 const google_breakpad::ClientInfo *client_info, 146 const std::wstring *dump_path) { 147 gDumpCallbackCalled = true; 148 } 149 150 void ExceptionHandlerDeathTest::DoCrashAccessViolation( 151 const OutOfProcGuarantee out_of_proc_guarantee) { 152 scoped_ptr<google_breakpad::ExceptionHandler> exc; 153 154 if (out_of_proc_guarantee == OUT_OF_PROC_GUARANTEED) { 155 google_breakpad::CrashGenerationClient *client = 156 new google_breakpad::CrashGenerationClient(kPipeName, 157 MiniDumpNormal, 158 NULL); // custom_info 159 ASSERT_TRUE(client->Register()); 160 exc.reset(new google_breakpad::ExceptionHandler( 161 temp_path_, 162 NULL, // filter 163 NULL, // callback 164 NULL, // callback_context 165 google_breakpad::ExceptionHandler::HANDLER_ALL, 166 client)); 167 } else { 168 ASSERT_TRUE(out_of_proc_guarantee == OUT_OF_PROC_BEST_EFFORT); 169 exc.reset(new google_breakpad::ExceptionHandler( 170 temp_path_, 171 NULL, // filter 172 NULL, // callback 173 NULL, // callback_context 174 google_breakpad::ExceptionHandler::HANDLER_ALL, 175 MiniDumpNormal, 176 kPipeName, 177 NULL)); // custom_info 178 } 179 180 // Disable GTest SEH handler 181 testing::DisableExceptionHandlerInScope disable_exception_handler; 182 183 // Although this is executing in the child process of the death test, 184 // if it's not true we'll still get an error rather than the crash 185 // being expected. 186 ASSERT_TRUE(exc->IsOutOfProcess()); 187 int *i = NULL; 188 printf("%d\n", (*i)++); 189 } 190 191 TEST_F(ExceptionHandlerDeathTest, OutOfProcTest) { 192 // We can take advantage of a detail of google test here to save some 193 // complexity in testing: when you do a death test, it actually forks. 194 // So we can make the main test harness the crash generation server, 195 // and call ASSERT_DEATH on a NULL dereference, it to expecting test 196 // the out of process scenario, since it's happening in a different 197 // process! This is different from the above because, above, we pass 198 // a NULL pipe name, and we also don't start a crash generation server. 199 200 ASSERT_TRUE(DoesPathExist(temp_path_)); 201 std::wstring dump_path(temp_path_); 202 google_breakpad::CrashGenerationServer server( 203 kPipeName, NULL, NULL, NULL, &clientDumpCallback, NULL, NULL, NULL, NULL, 204 NULL, true, &dump_path); 205 206 // This HAS to be EXPECT_, because when this test case is executed in the 207 // child process, the server registration will fail due to the named pipe 208 // being the same. 209 EXPECT_TRUE(server.Start()); 210 gDumpCallbackCalled = false; 211 ASSERT_DEATH(this->DoCrashAccessViolation(OUT_OF_PROC_BEST_EFFORT), ""); 212 EXPECT_TRUE(gDumpCallbackCalled); 213 } 214 215 TEST_F(ExceptionHandlerDeathTest, OutOfProcGuaranteedTest) { 216 // This is similar to the previous test (OutOfProcTest). The only difference 217 // is that in this test, the crash generation client is created and registered 218 // with the crash generation server outside of the ExceptionHandler 219 // constructor which allows breakpad users to opt out of the default 220 // in-process dump generation when the registration with the crash generation 221 // server fails. 222 223 ASSERT_TRUE(DoesPathExist(temp_path_)); 224 std::wstring dump_path(temp_path_); 225 google_breakpad::CrashGenerationServer server( 226 kPipeName, NULL, NULL, NULL, &clientDumpCallback, NULL, NULL, NULL, NULL, 227 NULL, true, &dump_path); 228 229 // This HAS to be EXPECT_, because when this test case is executed in the 230 // child process, the server registration will fail due to the named pipe 231 // being the same. 232 EXPECT_TRUE(server.Start()); 233 gDumpCallbackCalled = false; 234 ASSERT_DEATH(this->DoCrashAccessViolation(OUT_OF_PROC_GUARANTEED), ""); 235 EXPECT_TRUE(gDumpCallbackCalled); 236 } 237 238 TEST_F(ExceptionHandlerDeathTest, InvalidParameterTest) { 239 using google_breakpad::ExceptionHandler; 240 241 ASSERT_TRUE(DoesPathExist(temp_path_)); 242 ExceptionHandler handler(temp_path_, NULL, NULL, NULL, 243 ExceptionHandler::HANDLER_INVALID_PARAMETER); 244 245 // Disable the message box for assertions 246 _CrtSetReportMode(_CRT_ASSERT, 0); 247 248 // Call with a bad argument. The invalid parameter will be swallowed 249 // and a dump will be generated, the process will exit(0). 250 ASSERT_EXIT(printf(NULL), ::testing::ExitedWithCode(0), ""); 251 } 252 253 254 struct PureVirtualCallBase { 255 PureVirtualCallBase() { 256 // We have to reinterpret so the linker doesn't get confused because the 257 // method isn't defined. 258 reinterpret_cast<PureVirtualCallBase*>(this)->PureFunction(); 259 } 260 virtual ~PureVirtualCallBase() {} 261 virtual void PureFunction() const = 0; 262 }; 263 struct PureVirtualCall : public PureVirtualCallBase { 264 PureVirtualCall() { PureFunction(); } 265 virtual void PureFunction() const {} 266 }; 267 268 void ExceptionHandlerDeathTest::DoCrashPureVirtualCall() { 269 PureVirtualCall instance; 270 } 271 272 TEST_F(ExceptionHandlerDeathTest, PureVirtualCallTest) { 273 using google_breakpad::ExceptionHandler; 274 275 ASSERT_TRUE(DoesPathExist(temp_path_)); 276 ExceptionHandler handler(temp_path_, NULL, NULL, NULL, 277 ExceptionHandler::HANDLER_PURECALL); 278 279 // Disable the message box for assertions 280 _CrtSetReportMode(_CRT_ASSERT, 0); 281 282 // Calls a pure virtual function. 283 EXPECT_EXIT(DoCrashPureVirtualCall(), ::testing::ExitedWithCode(0), ""); 284 } 285 286 wstring find_minidump_in_directory(const wstring &directory) { 287 wstring search_path = directory + L"\\*"; 288 WIN32_FIND_DATA find_data; 289 HANDLE find_handle = FindFirstFileW(search_path.c_str(), &find_data); 290 if (find_handle == INVALID_HANDLE_VALUE) 291 return wstring(); 292 293 wstring filename; 294 do { 295 const wchar_t extension[] = L".dmp"; 296 const int extension_length = sizeof(extension) / sizeof(extension[0]) - 1; 297 const int filename_length = wcslen(find_data.cFileName); 298 if (filename_length > extension_length && 299 wcsncmp(extension, 300 find_data.cFileName + filename_length - extension_length, 301 extension_length) == 0) { 302 filename = directory + L"\\" + find_data.cFileName; 303 break; 304 } 305 } while (FindNextFile(find_handle, &find_data)); 306 FindClose(find_handle); 307 return filename; 308 } 309 310 #ifndef ADDRESS_SANITIZER 311 312 TEST_F(ExceptionHandlerDeathTest, InstructionPointerMemory) { 313 ASSERT_TRUE(DoesPathExist(temp_path_)); 314 scoped_ptr<google_breakpad::ExceptionHandler> exc( 315 new google_breakpad::ExceptionHandler( 316 temp_path_, 317 NULL, 318 NULL, 319 NULL, 320 google_breakpad::ExceptionHandler::HANDLER_ALL)); 321 322 // Disable GTest SEH handler 323 testing::DisableExceptionHandlerInScope disable_exception_handler; 324 325 // Get some executable memory. 326 const uint32_t kMemorySize = 256; // bytes 327 const int kOffset = kMemorySize / 2; 328 // This crashes with SIGILL on x86/x86-64/arm. 329 const unsigned char instructions[] = { 0xff, 0xff, 0xff, 0xff }; 330 char* memory = reinterpret_cast<char*>(VirtualAlloc(NULL, 331 kMemorySize, 332 MEM_COMMIT | MEM_RESERVE, 333 PAGE_EXECUTE_READWRITE)); 334 ASSERT_TRUE(memory); 335 336 // Write some instructions that will crash. Put them 337 // in the middle of the block of memory, because the 338 // minidump should contain 128 bytes on either side of the 339 // instruction pointer. 340 memcpy(memory + kOffset, instructions, sizeof(instructions)); 341 342 // Now execute the instructions, which should crash. 343 typedef void (*void_function)(void); 344 void_function memory_function = 345 reinterpret_cast<void_function>(memory + kOffset); 346 ASSERT_DEATH(memory_function(), ""); 347 348 // free the memory. 349 VirtualFree(memory, 0, MEM_RELEASE); 350 351 // Verify that the resulting minidump contains the memory around the IP 352 wstring minidump_filename_wide = find_minidump_in_directory(temp_path_); 353 ASSERT_FALSE(minidump_filename_wide.empty()); 354 string minidump_filename; 355 ASSERT_TRUE(WindowsStringUtils::safe_wcstombs(minidump_filename_wide, 356 &minidump_filename)); 357 358 // Read the minidump. Locate the exception record and the 359 // memory list, and then ensure that there is a memory region 360 // in the memory list that covers the instruction pointer from 361 // the exception record. 362 { 363 Minidump minidump(minidump_filename); 364 ASSERT_TRUE(minidump.Read()); 365 366 MinidumpException* exception = minidump.GetException(); 367 MinidumpMemoryList* memory_list = minidump.GetMemoryList(); 368 ASSERT_TRUE(exception); 369 ASSERT_TRUE(memory_list); 370 ASSERT_LT((unsigned)0, memory_list->region_count()); 371 372 MinidumpContext* context = exception->GetContext(); 373 ASSERT_TRUE(context); 374 375 uint64_t instruction_pointer; 376 ASSERT_TRUE(context->GetInstructionPointer(&instruction_pointer)); 377 378 MinidumpMemoryRegion* region = 379 memory_list->GetMemoryRegionForAddress(instruction_pointer); 380 ASSERT_TRUE(region); 381 382 EXPECT_EQ(kMemorySize, region->GetSize()); 383 const uint8_t* bytes = region->GetMemory(); 384 ASSERT_TRUE(bytes); 385 386 uint8_t prefix_bytes[kOffset]; 387 uint8_t suffix_bytes[kMemorySize - kOffset - sizeof(instructions)]; 388 memset(prefix_bytes, 0, sizeof(prefix_bytes)); 389 memset(suffix_bytes, 0, sizeof(suffix_bytes)); 390 EXPECT_EQ(0, memcmp(bytes, prefix_bytes, sizeof(prefix_bytes))); 391 EXPECT_EQ(0, memcmp(bytes + kOffset, instructions, sizeof(instructions))); 392 EXPECT_EQ(0, memcmp(bytes + kOffset + sizeof(instructions), 393 suffix_bytes, sizeof(suffix_bytes))); 394 } 395 396 DeleteFileW(minidump_filename_wide.c_str()); 397 } 398 399 TEST_F(ExceptionHandlerDeathTest, InstructionPointerMemoryMinBound) { 400 ASSERT_TRUE(DoesPathExist(temp_path_)); 401 scoped_ptr<google_breakpad::ExceptionHandler> exc( 402 new google_breakpad::ExceptionHandler( 403 temp_path_, 404 NULL, 405 NULL, 406 NULL, 407 google_breakpad::ExceptionHandler::HANDLER_ALL)); 408 409 // Disable GTest SEH handler 410 testing::DisableExceptionHandlerInScope disable_exception_handler; 411 412 SYSTEM_INFO sSysInfo; // Useful information about the system 413 GetSystemInfo(&sSysInfo); // Initialize the structure. 414 415 const uint32_t kMemorySize = 256; // bytes 416 const DWORD kPageSize = sSysInfo.dwPageSize; 417 const int kOffset = 0; 418 // This crashes with SIGILL on x86/x86-64/arm. 419 const unsigned char instructions[] = { 0xff, 0xff, 0xff, 0xff }; 420 // Get some executable memory. Specifically, reserve two pages, 421 // but only commit the second. 422 char* all_memory = reinterpret_cast<char*>(VirtualAlloc(NULL, 423 kPageSize * 2, 424 MEM_RESERVE, 425 PAGE_NOACCESS)); 426 ASSERT_TRUE(all_memory); 427 char* memory = all_memory + kPageSize; 428 ASSERT_TRUE(VirtualAlloc(memory, kPageSize, 429 MEM_COMMIT, PAGE_EXECUTE_READWRITE)); 430 431 // Write some instructions that will crash. Put them 432 // in the middle of the block of memory, because the 433 // minidump should contain 128 bytes on either side of the 434 // instruction pointer. 435 memcpy(memory + kOffset, instructions, sizeof(instructions)); 436 437 // Now execute the instructions, which should crash. 438 typedef void (*void_function)(void); 439 void_function memory_function = 440 reinterpret_cast<void_function>(memory + kOffset); 441 ASSERT_DEATH(memory_function(), ""); 442 443 // free the memory. 444 VirtualFree(memory, 0, MEM_RELEASE); 445 446 // Verify that the resulting minidump contains the memory around the IP 447 wstring minidump_filename_wide = find_minidump_in_directory(temp_path_); 448 ASSERT_FALSE(minidump_filename_wide.empty()); 449 string minidump_filename; 450 ASSERT_TRUE(WindowsStringUtils::safe_wcstombs(minidump_filename_wide, 451 &minidump_filename)); 452 453 // Read the minidump. Locate the exception record and the 454 // memory list, and then ensure that there is a memory region 455 // in the memory list that covers the instruction pointer from 456 // the exception record. 457 { 458 Minidump minidump(minidump_filename); 459 ASSERT_TRUE(minidump.Read()); 460 461 MinidumpException* exception = minidump.GetException(); 462 MinidumpMemoryList* memory_list = minidump.GetMemoryList(); 463 ASSERT_TRUE(exception); 464 ASSERT_TRUE(memory_list); 465 ASSERT_LT((unsigned)0, memory_list->region_count()); 466 467 MinidumpContext* context = exception->GetContext(); 468 ASSERT_TRUE(context); 469 470 uint64_t instruction_pointer; 471 ASSERT_TRUE(context->GetInstructionPointer(&instruction_pointer)); 472 473 MinidumpMemoryRegion* region = 474 memory_list->GetMemoryRegionForAddress(instruction_pointer); 475 ASSERT_TRUE(region); 476 477 EXPECT_EQ(kMemorySize / 2, region->GetSize()); 478 const uint8_t* bytes = region->GetMemory(); 479 ASSERT_TRUE(bytes); 480 481 uint8_t suffix_bytes[kMemorySize / 2 - sizeof(instructions)]; 482 memset(suffix_bytes, 0, sizeof(suffix_bytes)); 483 EXPECT_TRUE(memcmp(bytes + kOffset, 484 instructions, sizeof(instructions)) == 0); 485 EXPECT_TRUE(memcmp(bytes + kOffset + sizeof(instructions), 486 suffix_bytes, sizeof(suffix_bytes)) == 0); 487 } 488 489 DeleteFileW(minidump_filename_wide.c_str()); 490 } 491 492 TEST_F(ExceptionHandlerDeathTest, InstructionPointerMemoryMaxBound) { 493 ASSERT_TRUE(DoesPathExist(temp_path_)); 494 scoped_ptr<google_breakpad::ExceptionHandler> exc( 495 new google_breakpad::ExceptionHandler( 496 temp_path_, 497 NULL, 498 NULL, 499 NULL, 500 google_breakpad::ExceptionHandler::HANDLER_ALL)); 501 502 // Disable GTest SEH handler 503 testing::DisableExceptionHandlerInScope disable_exception_handler; 504 505 SYSTEM_INFO sSysInfo; // Useful information about the system 506 GetSystemInfo(&sSysInfo); // Initialize the structure. 507 508 const DWORD kPageSize = sSysInfo.dwPageSize; 509 // This crashes with SIGILL on x86/x86-64/arm. 510 const unsigned char instructions[] = { 0xff, 0xff, 0xff, 0xff }; 511 const int kOffset = kPageSize - sizeof(instructions); 512 // Get some executable memory. Specifically, reserve two pages, 513 // but only commit the first. 514 char* memory = reinterpret_cast<char*>(VirtualAlloc(NULL, 515 kPageSize * 2, 516 MEM_RESERVE, 517 PAGE_NOACCESS)); 518 ASSERT_TRUE(memory); 519 ASSERT_TRUE(VirtualAlloc(memory, kPageSize, 520 MEM_COMMIT, PAGE_EXECUTE_READWRITE)); 521 522 // Write some instructions that will crash. 523 memcpy(memory + kOffset, instructions, sizeof(instructions)); 524 525 // Now execute the instructions, which should crash. 526 typedef void (*void_function)(void); 527 void_function memory_function = 528 reinterpret_cast<void_function>(memory + kOffset); 529 ASSERT_DEATH(memory_function(), ""); 530 531 // free the memory. 532 VirtualFree(memory, 0, MEM_RELEASE); 533 534 // Verify that the resulting minidump contains the memory around the IP 535 wstring minidump_filename_wide = find_minidump_in_directory(temp_path_); 536 ASSERT_FALSE(minidump_filename_wide.empty()); 537 string minidump_filename; 538 ASSERT_TRUE(WindowsStringUtils::safe_wcstombs(minidump_filename_wide, 539 &minidump_filename)); 540 541 // Read the minidump. Locate the exception record and the 542 // memory list, and then ensure that there is a memory region 543 // in the memory list that covers the instruction pointer from 544 // the exception record. 545 { 546 Minidump minidump(minidump_filename); 547 ASSERT_TRUE(minidump.Read()); 548 549 MinidumpException* exception = minidump.GetException(); 550 MinidumpMemoryList* memory_list = minidump.GetMemoryList(); 551 ASSERT_TRUE(exception); 552 ASSERT_TRUE(memory_list); 553 ASSERT_LT((unsigned)0, memory_list->region_count()); 554 555 MinidumpContext* context = exception->GetContext(); 556 ASSERT_TRUE(context); 557 558 uint64_t instruction_pointer; 559 ASSERT_TRUE(context->GetInstructionPointer(&instruction_pointer)); 560 561 MinidumpMemoryRegion* region = 562 memory_list->GetMemoryRegionForAddress(instruction_pointer); 563 ASSERT_TRUE(region); 564 565 const size_t kPrefixSize = 128; // bytes 566 EXPECT_EQ(kPrefixSize + sizeof(instructions), region->GetSize()); 567 const uint8_t* bytes = region->GetMemory(); 568 ASSERT_TRUE(bytes); 569 570 uint8_t prefix_bytes[kPrefixSize]; 571 memset(prefix_bytes, 0, sizeof(prefix_bytes)); 572 EXPECT_EQ(0, memcmp(bytes, prefix_bytes, sizeof(prefix_bytes))); 573 EXPECT_EQ(0, memcmp(bytes + kPrefixSize, 574 instructions, sizeof(instructions))); 575 } 576 577 DeleteFileW(minidump_filename_wide.c_str()); 578 } 579 580 #endif // !ADDRESS_SANITIZER 581 582 } // namespace 583