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 static_cast<int>(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__anon655245ce0111::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__anon655245ce0111::PureVirtualCallBase260 virtual ~PureVirtualCallBase() {}
261 virtual void PureFunction() const = 0;
262 };
263 struct PureVirtualCall : public PureVirtualCallBase {
PureVirtualCall__anon655245ce0111::PureVirtualCall264 PureVirtualCall() { PureFunction(); }
PureFunction__anon655245ce0111::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 size_t extension_length = sizeof(extension) / sizeof(extension[0]) - 1;
297 const size_t 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 at least 128 bytes on either
361 // side of the instruction pointer from 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_LE(kMemorySize, region->GetSize());
383 const uint8_t* bytes = region->GetMemory();
384 ASSERT_TRUE(bytes);
385
386 uint64_t ip_offset = instruction_pointer - region->GetBase();
387 EXPECT_GE(region->GetSize() - kOffset, ip_offset);
388 EXPECT_LE(kOffset, ip_offset);
389
390 uint8_t prefix_bytes[kOffset];
391 uint8_t suffix_bytes[kMemorySize - kOffset - sizeof(instructions)];
392 memset(prefix_bytes, 0, sizeof(prefix_bytes));
393 memset(suffix_bytes, 0, sizeof(suffix_bytes));
394 EXPECT_EQ(0, memcmp(bytes + ip_offset - kOffset, prefix_bytes,
395 sizeof(prefix_bytes)));
396 EXPECT_EQ(0, memcmp(bytes + ip_offset, instructions, sizeof(instructions)));
397 EXPECT_EQ(0, memcmp(bytes + ip_offset + sizeof(instructions), suffix_bytes,
398 sizeof(suffix_bytes)));
399 }
400
401 DeleteFileW(minidump_filename_wide.c_str());
402 }
403
TEST_F(ExceptionHandlerDeathTest,InstructionPointerMemoryMinBound)404 TEST_F(ExceptionHandlerDeathTest, InstructionPointerMemoryMinBound) {
405 ASSERT_TRUE(DoesPathExist(temp_path_));
406 scoped_ptr<google_breakpad::ExceptionHandler> exc(
407 new google_breakpad::ExceptionHandler(
408 temp_path_,
409 NULL,
410 NULL,
411 NULL,
412 google_breakpad::ExceptionHandler::HANDLER_ALL));
413
414 // Disable GTest SEH handler
415 testing::DisableExceptionHandlerInScope disable_exception_handler;
416
417 SYSTEM_INFO sSysInfo; // Useful information about the system
418 GetSystemInfo(&sSysInfo); // Initialize the structure.
419
420 const uint32_t kMemorySize = 256; // bytes
421 const DWORD kPageSize = sSysInfo.dwPageSize;
422 const int kOffset = 0;
423 // This crashes with SIGILL on x86/x86-64/arm.
424 const unsigned char instructions[] = { 0xff, 0xff, 0xff, 0xff };
425 // Get some executable memory. Specifically, reserve two pages,
426 // but only commit the second.
427 char* all_memory = reinterpret_cast<char*>(VirtualAlloc(NULL,
428 kPageSize * 2,
429 MEM_RESERVE,
430 PAGE_NOACCESS));
431 ASSERT_TRUE(all_memory);
432 char* memory = all_memory + kPageSize;
433 ASSERT_TRUE(VirtualAlloc(memory, kPageSize,
434 MEM_COMMIT, PAGE_EXECUTE_READWRITE));
435
436 // Write some instructions that will crash. Put them
437 // in the middle of the block of memory, because the
438 // minidump should contain 128 bytes on either side of the
439 // instruction pointer.
440 memcpy(memory + kOffset, instructions, sizeof(instructions));
441
442 // Now execute the instructions, which should crash.
443 typedef void (*void_function)(void);
444 void_function memory_function =
445 reinterpret_cast<void_function>(memory + kOffset);
446 ASSERT_DEATH(memory_function(), "");
447
448 // free the memory.
449 VirtualFree(memory, 0, MEM_RELEASE);
450
451 // Verify that the resulting minidump contains the memory around the IP
452 wstring minidump_filename_wide = find_minidump_in_directory(temp_path_);
453 ASSERT_FALSE(minidump_filename_wide.empty());
454 string minidump_filename;
455 ASSERT_TRUE(WindowsStringUtils::safe_wcstombs(minidump_filename_wide,
456 &minidump_filename));
457
458 // Read the minidump. Locate the exception record and the
459 // memory list, and then ensure that there is a memory region
460 // in the memory list that covers the instruction pointer from
461 // the exception record.
462 {
463 Minidump minidump(minidump_filename);
464 ASSERT_TRUE(minidump.Read());
465
466 MinidumpException* exception = minidump.GetException();
467 MinidumpMemoryList* memory_list = minidump.GetMemoryList();
468 ASSERT_TRUE(exception);
469 ASSERT_TRUE(memory_list);
470 ASSERT_LT((unsigned)0, memory_list->region_count());
471
472 MinidumpContext* context = exception->GetContext();
473 ASSERT_TRUE(context);
474
475 uint64_t instruction_pointer;
476 ASSERT_TRUE(context->GetInstructionPointer(&instruction_pointer));
477
478 MinidumpMemoryRegion* region =
479 memory_list->GetMemoryRegionForAddress(instruction_pointer);
480 ASSERT_TRUE(region);
481
482 EXPECT_EQ(kMemorySize / 2, region->GetSize());
483 const uint8_t* bytes = region->GetMemory();
484 ASSERT_TRUE(bytes);
485
486 uint8_t suffix_bytes[kMemorySize / 2 - sizeof(instructions)];
487 memset(suffix_bytes, 0, sizeof(suffix_bytes));
488 EXPECT_TRUE(memcmp(bytes + kOffset,
489 instructions, sizeof(instructions)) == 0);
490 EXPECT_TRUE(memcmp(bytes + kOffset + sizeof(instructions),
491 suffix_bytes, sizeof(suffix_bytes)) == 0);
492 }
493
494 DeleteFileW(minidump_filename_wide.c_str());
495 }
496
TEST_F(ExceptionHandlerDeathTest,InstructionPointerMemoryMaxBound)497 TEST_F(ExceptionHandlerDeathTest, InstructionPointerMemoryMaxBound) {
498 ASSERT_TRUE(DoesPathExist(temp_path_));
499 scoped_ptr<google_breakpad::ExceptionHandler> exc(
500 new google_breakpad::ExceptionHandler(
501 temp_path_,
502 NULL,
503 NULL,
504 NULL,
505 google_breakpad::ExceptionHandler::HANDLER_ALL));
506
507 // Disable GTest SEH handler
508 testing::DisableExceptionHandlerInScope disable_exception_handler;
509
510 SYSTEM_INFO sSysInfo; // Useful information about the system
511 GetSystemInfo(&sSysInfo); // Initialize the structure.
512
513 const DWORD kPageSize = sSysInfo.dwPageSize;
514 // This crashes with SIGILL on x86/x86-64/arm.
515 const unsigned char instructions[] = { 0xff, 0xff, 0xff, 0xff };
516 const int kOffset = kPageSize - sizeof(instructions);
517 // Get some executable memory. Specifically, reserve two pages,
518 // but only commit the first.
519 char* memory = reinterpret_cast<char*>(VirtualAlloc(NULL,
520 kPageSize * 2,
521 MEM_RESERVE,
522 PAGE_NOACCESS));
523 ASSERT_TRUE(memory);
524 ASSERT_TRUE(VirtualAlloc(memory, kPageSize,
525 MEM_COMMIT, PAGE_EXECUTE_READWRITE));
526
527 // Write some instructions that will crash.
528 memcpy(memory + kOffset, instructions, sizeof(instructions));
529
530 // Now execute the instructions, which should crash.
531 typedef void (*void_function)(void);
532 void_function memory_function =
533 reinterpret_cast<void_function>(memory + kOffset);
534 ASSERT_DEATH(memory_function(), "");
535
536 // free the memory.
537 VirtualFree(memory, 0, MEM_RELEASE);
538
539 // Verify that the resulting minidump contains the memory around the IP
540 wstring minidump_filename_wide = find_minidump_in_directory(temp_path_);
541 ASSERT_FALSE(minidump_filename_wide.empty());
542 string minidump_filename;
543 ASSERT_TRUE(WindowsStringUtils::safe_wcstombs(minidump_filename_wide,
544 &minidump_filename));
545
546 // Read the minidump. Locate the exception record and the
547 // memory list, and then ensure that there is a memory region
548 // in the memory list that covers the instruction pointer from
549 // the exception record.
550 {
551 Minidump minidump(minidump_filename);
552 ASSERT_TRUE(minidump.Read());
553
554 MinidumpException* exception = minidump.GetException();
555 MinidumpMemoryList* memory_list = minidump.GetMemoryList();
556 ASSERT_TRUE(exception);
557 ASSERT_TRUE(memory_list);
558 ASSERT_LT((unsigned)0, memory_list->region_count());
559
560 MinidumpContext* context = exception->GetContext();
561 ASSERT_TRUE(context);
562
563 uint64_t instruction_pointer;
564 ASSERT_TRUE(context->GetInstructionPointer(&instruction_pointer));
565
566 MinidumpMemoryRegion* region =
567 memory_list->GetMemoryRegionForAddress(instruction_pointer);
568 ASSERT_TRUE(region);
569
570 const size_t kPrefixSize = 128; // bytes
571 EXPECT_EQ(kPrefixSize + sizeof(instructions), region->GetSize());
572 const uint8_t* bytes = region->GetMemory();
573 ASSERT_TRUE(bytes);
574
575 uint8_t prefix_bytes[kPrefixSize];
576 memset(prefix_bytes, 0, sizeof(prefix_bytes));
577 EXPECT_EQ(0, memcmp(bytes, prefix_bytes, sizeof(prefix_bytes)));
578 EXPECT_EQ(0, memcmp(bytes + kPrefixSize,
579 instructions, sizeof(instructions)));
580 }
581
582 DeleteFileW(minidump_filename_wide.c_str());
583 }
584
585 #endif // !ADDRESS_SANITIZER
586
587 } // namespace
588