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
SetUp()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
DoesPathExist(const TCHAR * path_name)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
MinidumpWrittenCallback(const wchar_t * dump_path,const wchar_t * minidump_id,void * context,EXCEPTION_POINTERS * exinfo,MDRawAssertionInfo * assertion,bool succeeded)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
TEST_F(ExceptionHandlerDeathTest,InProcTest)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
clientDumpCallback(void * dump_context,const google_breakpad::ClientInfo * client_info,const std::wstring * dump_path)144 void clientDumpCallback(void *dump_context,
145 const google_breakpad::ClientInfo *client_info,
146 const std::wstring *dump_path) {
147 gDumpCallbackCalled = true;
148 }
149
DoCrashAccessViolation(const OutOfProcGuarantee out_of_proc_guarantee)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
TEST_F(ExceptionHandlerDeathTest,OutOfProcTest)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
TEST_F(ExceptionHandlerDeathTest,OutOfProcGuaranteedTest)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
TEST_F(ExceptionHandlerDeathTest,InvalidParameterTest)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 {
PureVirtualCallBase__anon419f23880111::PureVirtualCallBase255 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 }
~PureVirtualCallBase__anon419f23880111::PureVirtualCallBase260 virtual ~PureVirtualCallBase() {}
261 virtual void PureFunction() const = 0;
262 };
263 struct PureVirtualCall : public PureVirtualCallBase {
PureVirtualCall__anon419f23880111::PureVirtualCall264 PureVirtualCall() { PureFunction(); }
PureFunction__anon419f23880111::PureVirtualCall265 virtual void PureFunction() const {}
266 };
267
DoCrashPureVirtualCall()268 void ExceptionHandlerDeathTest::DoCrashPureVirtualCall() {
269 PureVirtualCall instance;
270 }
271
TEST_F(ExceptionHandlerDeathTest,PureVirtualCallTest)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
find_minidump_in_directory(const wstring & directory)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
TEST_F(ExceptionHandlerDeathTest,InstructionPointerMemory)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
TEST_F(ExceptionHandlerDeathTest,InstructionPointerMemoryMinBound)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
TEST_F(ExceptionHandlerDeathTest,InstructionPointerMemoryMaxBound)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