1 // Copyright (c) 2010, 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 // exception_handler_test.cc: Unit tests for google_breakpad::ExceptionHandler
31
32 #include <pthread.h>
33 #include <sys/mman.h>
34 #include <sys/stat.h>
35 #include <unistd.h>
36
37 #include "breakpad_googletest_includes.h"
38 #include "client/mac/handler/exception_handler.h"
39 #include "common/mac/MachIPC.h"
40 #include "common/tests/auto_tempdir.h"
41 #include "google_breakpad/processor/minidump.h"
42
43 namespace google_breakpad {
44 // This acts as the log sink for INFO logging from the processor
45 // logging code. The logging output confuses XCode and makes it think
46 // there are unit test failures. testlogging.h handles the overriding.
47 std::ostringstream info_log;
48 }
49
50 namespace {
51 using std::string;
52 using google_breakpad::AutoTempDir;
53 using google_breakpad::ExceptionHandler;
54 using google_breakpad::MachPortSender;
55 using google_breakpad::MachReceiveMessage;
56 using google_breakpad::MachSendMessage;
57 using google_breakpad::Minidump;
58 using google_breakpad::MinidumpContext;
59 using google_breakpad::MinidumpException;
60 using google_breakpad::MinidumpMemoryList;
61 using google_breakpad::MinidumpMemoryRegion;
62 using google_breakpad::ReceivePort;
63 using testing::Test;
64
65 class ExceptionHandlerTest : public Test {
66 public:
67 void InProcessCrash(bool aborting);
68 AutoTempDir tempDir;
69 string lastDumpName;
70 };
71
Crasher()72 static void Crasher() {
73 int *a = (int*)0x42;
74
75 fprintf(stdout, "Going to crash...\n");
76 fprintf(stdout, "A = %d", *a);
77 }
78
AbortCrasher()79 static void AbortCrasher() {
80 fprintf(stdout, "Going to crash...\n");
81 abort();
82 }
83
SoonToCrash(void (* crasher)())84 static void SoonToCrash(void(*crasher)()) {
85 crasher();
86 }
87
MDCallback(const char * dump_dir,const char * file_name,void * context,bool success)88 static bool MDCallback(const char *dump_dir, const char *file_name,
89 void *context, bool success) {
90 string path(dump_dir);
91 path.append("/");
92 path.append(file_name);
93 path.append(".dmp");
94
95 int fd = *reinterpret_cast<int*>(context);
96 (void)write(fd, path.c_str(), path.length() + 1);
97 close(fd);
98 exit(0);
99 // not reached
100 return true;
101 }
102
InProcessCrash(bool aborting)103 void ExceptionHandlerTest::InProcessCrash(bool aborting) {
104 // Give the child process a pipe to report back on.
105 int fds[2];
106 ASSERT_EQ(0, pipe(fds));
107 // Fork off a child process so it can crash.
108 pid_t pid = fork();
109 if (pid == 0) {
110 // In the child process.
111 close(fds[0]);
112 ExceptionHandler eh(tempDir.path(), NULL, MDCallback, &fds[1], true, NULL);
113 // crash
114 SoonToCrash(aborting ? &AbortCrasher : &Crasher);
115 // not reached
116 exit(1);
117 }
118 // In the parent process.
119 ASSERT_NE(-1, pid);
120 // Wait for the background process to return the minidump file.
121 close(fds[1]);
122 char minidump_file[PATH_MAX];
123 ssize_t nbytes = read(fds[0], minidump_file, sizeof(minidump_file));
124 ASSERT_NE(0, nbytes);
125
126 Minidump minidump(minidump_file);
127 ASSERT_TRUE(minidump.Read());
128
129 MinidumpException* exception = minidump.GetException();
130 ASSERT_TRUE(exception);
131
132 const MDRawExceptionStream* raw_exception = exception->exception();
133 ASSERT_TRUE(raw_exception);
134
135 if (aborting) {
136 EXPECT_EQ(MD_EXCEPTION_MAC_SOFTWARE,
137 raw_exception->exception_record.exception_code);
138 EXPECT_EQ(MD_EXCEPTION_CODE_MAC_ABORT,
139 raw_exception->exception_record.exception_flags);
140 } else {
141 EXPECT_EQ(MD_EXCEPTION_MAC_BAD_ACCESS,
142 raw_exception->exception_record.exception_code);
143 #if defined(__x86_64__)
144 EXPECT_EQ(MD_EXCEPTION_CODE_MAC_INVALID_ADDRESS,
145 raw_exception->exception_record.exception_flags);
146 #elif defined(__i386__)
147 EXPECT_EQ(MD_EXCEPTION_CODE_MAC_PROTECTION_FAILURE,
148 raw_exception->exception_record.exception_flags);
149 #endif
150 }
151
152 const MinidumpContext* context = exception->GetContext();
153 ASSERT_TRUE(context);
154
155 uint64_t instruction_pointer;
156 ASSERT_TRUE(context->GetInstructionPointer(&instruction_pointer));
157
158 // Ideally would like to sanity check that abort() is on the stack
159 // but that's hard.
160 MinidumpMemoryList* memory_list = minidump.GetMemoryList();
161 ASSERT_TRUE(memory_list);
162 MinidumpMemoryRegion* region =
163 memory_list->GetMemoryRegionForAddress(instruction_pointer);
164 EXPECT_TRUE(region);
165
166 // Child process should have exited with a zero status.
167 int ret;
168 ASSERT_EQ(pid, waitpid(pid, &ret, 0));
169 EXPECT_NE(0, WIFEXITED(ret));
170 EXPECT_EQ(0, WEXITSTATUS(ret));
171 }
172
TEST_F(ExceptionHandlerTest,InProcess)173 TEST_F(ExceptionHandlerTest, InProcess) {
174 InProcessCrash(false);
175 }
176
TEST_F(ExceptionHandlerTest,InProcessAbort)177 TEST_F(ExceptionHandlerTest, InProcessAbort) {
178 InProcessCrash(true);
179 }
180
DumpNameMDCallback(const char * dump_dir,const char * file_name,void * context,bool success)181 static bool DumpNameMDCallback(const char *dump_dir, const char *file_name,
182 void *context, bool success) {
183 ExceptionHandlerTest *self = reinterpret_cast<ExceptionHandlerTest*>(context);
184 if (dump_dir && file_name) {
185 self->lastDumpName = dump_dir;
186 self->lastDumpName += "/";
187 self->lastDumpName += file_name;
188 self->lastDumpName += ".dmp";
189 }
190 return true;
191 }
192
TEST_F(ExceptionHandlerTest,WriteMinidump)193 TEST_F(ExceptionHandlerTest, WriteMinidump) {
194 ExceptionHandler eh(tempDir.path(), NULL, DumpNameMDCallback, this, true,
195 NULL);
196 ASSERT_TRUE(eh.WriteMinidump());
197
198 // Ensure that minidump file exists and is > 0 bytes.
199 ASSERT_FALSE(lastDumpName.empty());
200 struct stat st;
201 ASSERT_EQ(0, stat(lastDumpName.c_str(), &st));
202 ASSERT_LT(0, st.st_size);
203
204 // The minidump should not contain an exception stream.
205 Minidump minidump(lastDumpName);
206 ASSERT_TRUE(minidump.Read());
207
208 MinidumpException* exception = minidump.GetException();
209 EXPECT_FALSE(exception);
210 }
211
TEST_F(ExceptionHandlerTest,WriteMinidumpWithException)212 TEST_F(ExceptionHandlerTest, WriteMinidumpWithException) {
213 ExceptionHandler eh(tempDir.path(), NULL, DumpNameMDCallback, this, true,
214 NULL);
215 ASSERT_TRUE(eh.WriteMinidump(true));
216
217 // Ensure that minidump file exists and is > 0 bytes.
218 ASSERT_FALSE(lastDumpName.empty());
219 struct stat st;
220 ASSERT_EQ(0, stat(lastDumpName.c_str(), &st));
221 ASSERT_LT(0, st.st_size);
222
223 // The minidump should contain an exception stream.
224 Minidump minidump(lastDumpName);
225 ASSERT_TRUE(minidump.Read());
226
227 MinidumpException* exception = minidump.GetException();
228 ASSERT_TRUE(exception);
229 const MDRawExceptionStream* raw_exception = exception->exception();
230 ASSERT_TRUE(raw_exception);
231
232 EXPECT_EQ(MD_EXCEPTION_MAC_BREAKPOINT,
233 raw_exception->exception_record.exception_code);
234 }
235
TEST_F(ExceptionHandlerTest,DumpChildProcess)236 TEST_F(ExceptionHandlerTest, DumpChildProcess) {
237 const int kTimeoutMs = 2000;
238 // Create a mach port to receive the child task on.
239 char machPortName[128];
240 sprintf(machPortName, "ExceptionHandlerTest.%d", getpid());
241 ReceivePort parent_recv_port(machPortName);
242
243 // Give the child process a pipe to block on.
244 int fds[2];
245 ASSERT_EQ(0, pipe(fds));
246
247 // Fork off a child process to dump.
248 pid_t pid = fork();
249 if (pid == 0) {
250 // In the child process
251 close(fds[1]);
252
253 // Send parent process the task and thread ports.
254 MachSendMessage child_message(0);
255 child_message.AddDescriptor(mach_task_self());
256 child_message.AddDescriptor(mach_thread_self());
257
258 MachPortSender child_sender(machPortName);
259 if (child_sender.SendMessage(child_message, kTimeoutMs) != KERN_SUCCESS)
260 exit(1);
261
262 // Wait for the parent process.
263 uint8_t data;
264 read(fds[0], &data, 1);
265 exit(0);
266 }
267 // In the parent process.
268 ASSERT_NE(-1, pid);
269 close(fds[0]);
270
271 // Read the child's task and thread ports.
272 MachReceiveMessage child_message;
273 ASSERT_EQ(KERN_SUCCESS,
274 parent_recv_port.WaitForMessage(&child_message, kTimeoutMs));
275 mach_port_t child_task = child_message.GetTranslatedPort(0);
276 mach_port_t child_thread = child_message.GetTranslatedPort(1);
277 ASSERT_NE((mach_port_t)MACH_PORT_NULL, child_task);
278 ASSERT_NE((mach_port_t)MACH_PORT_NULL, child_thread);
279
280 // Write a minidump of the child process.
281 bool result = ExceptionHandler::WriteMinidumpForChild(child_task,
282 child_thread,
283 tempDir.path(),
284 DumpNameMDCallback,
285 this);
286 ASSERT_EQ(true, result);
287
288 // Ensure that minidump file exists and is > 0 bytes.
289 ASSERT_FALSE(lastDumpName.empty());
290 struct stat st;
291 ASSERT_EQ(0, stat(lastDumpName.c_str(), &st));
292 ASSERT_LT(0, st.st_size);
293
294 // Unblock child process
295 uint8_t data = 1;
296 (void)write(fds[1], &data, 1);
297
298 // Child process should have exited with a zero status.
299 int ret;
300 ASSERT_EQ(pid, waitpid(pid, &ret, 0));
301 EXPECT_NE(0, WIFEXITED(ret));
302 EXPECT_EQ(0, WEXITSTATUS(ret));
303 }
304
305 // Test that memory around the instruction pointer is written
306 // to the dump as a MinidumpMemoryRegion.
TEST_F(ExceptionHandlerTest,InstructionPointerMemory)307 TEST_F(ExceptionHandlerTest, InstructionPointerMemory) {
308 // Give the child process a pipe to report back on.
309 int fds[2];
310 ASSERT_EQ(0, pipe(fds));
311
312 // These are defined here so the parent can use them to check the
313 // data from the minidump afterwards.
314 const uint32_t kMemorySize = 256; // bytes
315 const int kOffset = kMemorySize / 2;
316 // This crashes with SIGILL on x86/x86-64/arm.
317 const unsigned char instructions[] = { 0xff, 0xff, 0xff, 0xff };
318
319 pid_t pid = fork();
320 if (pid == 0) {
321 close(fds[0]);
322 ExceptionHandler eh(tempDir.path(), NULL, MDCallback, &fds[1], true, NULL);
323 // Get some executable memory.
324 char* memory =
325 reinterpret_cast<char*>(mmap(NULL,
326 kMemorySize,
327 PROT_READ | PROT_WRITE | PROT_EXEC,
328 MAP_PRIVATE | MAP_ANON,
329 -1,
330 0));
331 if (!memory)
332 exit(0);
333
334 // Write some instructions that will crash. Put them in the middle
335 // of the block of memory, because the minidump should contain 128
336 // bytes on either side of the instruction pointer.
337 memcpy(memory + kOffset, instructions, sizeof(instructions));
338
339 // Now execute the instructions, which should crash.
340 typedef void (*void_function)(void);
341 void_function memory_function =
342 reinterpret_cast<void_function>(memory + kOffset);
343 memory_function();
344 // not reached
345 exit(1);
346 }
347 // In the parent process.
348 ASSERT_NE(-1, pid);
349 close(fds[1]);
350
351 // Wait for the background process to return the minidump file.
352 close(fds[1]);
353 char minidump_file[PATH_MAX];
354 ssize_t nbytes = read(fds[0], minidump_file, sizeof(minidump_file));
355 ASSERT_NE(0, nbytes);
356 // Ensure that minidump file exists and is > 0 bytes.
357 struct stat st;
358 ASSERT_EQ(0, stat(minidump_file, &st));
359 ASSERT_LT(0, st.st_size);
360
361 // Child process should have exited with a zero status.
362 int ret;
363 ASSERT_EQ(pid, waitpid(pid, &ret, 0));
364 EXPECT_NE(0, WIFEXITED(ret));
365 EXPECT_EQ(0, WEXITSTATUS(ret));
366
367 // Read the minidump. Locate the exception record and the
368 // memory list, and then ensure that there is a memory region
369 // in the memory list that covers the instruction pointer from
370 // the exception record.
371 Minidump minidump(minidump_file);
372 ASSERT_TRUE(minidump.Read());
373
374 MinidumpException* exception = minidump.GetException();
375 MinidumpMemoryList* memory_list = minidump.GetMemoryList();
376 ASSERT_TRUE(exception);
377 ASSERT_TRUE(memory_list);
378 ASSERT_NE((unsigned int)0, memory_list->region_count());
379
380 MinidumpContext* context = exception->GetContext();
381 ASSERT_TRUE(context);
382
383 uint64_t instruction_pointer;
384 ASSERT_TRUE(context->GetInstructionPointer(&instruction_pointer));
385
386 MinidumpMemoryRegion* region =
387 memory_list->GetMemoryRegionForAddress(instruction_pointer);
388 EXPECT_TRUE(region);
389
390 EXPECT_EQ(kMemorySize, region->GetSize());
391 const uint8_t* bytes = region->GetMemory();
392 ASSERT_TRUE(bytes);
393
394 uint8_t prefix_bytes[kOffset];
395 uint8_t suffix_bytes[kMemorySize - kOffset - sizeof(instructions)];
396 memset(prefix_bytes, 0, sizeof(prefix_bytes));
397 memset(suffix_bytes, 0, sizeof(suffix_bytes));
398 EXPECT_TRUE(memcmp(bytes, prefix_bytes, sizeof(prefix_bytes)) == 0);
399 EXPECT_TRUE(memcmp(bytes + kOffset, instructions, sizeof(instructions)) == 0);
400 EXPECT_TRUE(memcmp(bytes + kOffset + sizeof(instructions),
401 suffix_bytes, sizeof(suffix_bytes)) == 0);
402 }
403
404 // Test that the memory region around the instruction pointer is
405 // bounded correctly on the low end.
TEST_F(ExceptionHandlerTest,InstructionPointerMemoryMinBound)406 TEST_F(ExceptionHandlerTest, InstructionPointerMemoryMinBound) {
407 // Give the child process a pipe to report back on.
408 int fds[2];
409 ASSERT_EQ(0, pipe(fds));
410
411 // These are defined here so the parent can use them to check the
412 // data from the minidump afterwards.
413 const uint32_t kMemorySize = 256; // bytes
414 const int kOffset = 0;
415 // This crashes with SIGILL on x86/x86-64/arm.
416 const unsigned char instructions[] = { 0xff, 0xff, 0xff, 0xff };
417
418 pid_t pid = fork();
419 if (pid == 0) {
420 close(fds[0]);
421 ExceptionHandler eh(tempDir.path(), NULL, MDCallback, &fds[1], true, NULL);
422 // Get some executable memory.
423 char* memory =
424 reinterpret_cast<char*>(mmap(NULL,
425 kMemorySize,
426 PROT_READ | PROT_WRITE | PROT_EXEC,
427 MAP_PRIVATE | MAP_ANON,
428 -1,
429 0));
430 if (!memory)
431 exit(0);
432
433 // Write some instructions that will crash. Put them at the start
434 // of the block of memory, to ensure that the memory bounding
435 // works properly.
436 memcpy(memory + kOffset, instructions, sizeof(instructions));
437
438 // Now execute the instructions, which should crash.
439 typedef void (*void_function)(void);
440 void_function memory_function =
441 reinterpret_cast<void_function>(memory + kOffset);
442 memory_function();
443 // not reached
444 exit(1);
445 }
446 // In the parent process.
447 ASSERT_NE(-1, pid);
448 close(fds[1]);
449
450 // Wait for the background process to return the minidump file.
451 close(fds[1]);
452 char minidump_file[PATH_MAX];
453 ssize_t nbytes = read(fds[0], minidump_file, sizeof(minidump_file));
454 ASSERT_NE(0, nbytes);
455 // Ensure that minidump file exists and is > 0 bytes.
456 struct stat st;
457 ASSERT_EQ(0, stat(minidump_file, &st));
458 ASSERT_LT(0, st.st_size);
459
460 // Child process should have exited with a zero status.
461 int ret;
462 ASSERT_EQ(pid, waitpid(pid, &ret, 0));
463 EXPECT_NE(0, WIFEXITED(ret));
464 EXPECT_EQ(0, WEXITSTATUS(ret));
465
466 // Read the minidump. Locate the exception record and the
467 // memory list, and then ensure that there is a memory region
468 // in the memory list that covers the instruction pointer from
469 // the exception record.
470 Minidump minidump(minidump_file);
471 ASSERT_TRUE(minidump.Read());
472
473 MinidumpException* exception = minidump.GetException();
474 MinidumpMemoryList* memory_list = minidump.GetMemoryList();
475 ASSERT_TRUE(exception);
476 ASSERT_TRUE(memory_list);
477 ASSERT_NE((unsigned int)0, memory_list->region_count());
478
479 MinidumpContext* context = exception->GetContext();
480 ASSERT_TRUE(context);
481
482 uint64_t instruction_pointer;
483 ASSERT_TRUE(context->GetInstructionPointer(&instruction_pointer));
484
485 MinidumpMemoryRegion* region =
486 memory_list->GetMemoryRegionForAddress(instruction_pointer);
487 EXPECT_TRUE(region);
488
489 EXPECT_EQ(kMemorySize / 2, region->GetSize());
490 const uint8_t* bytes = region->GetMemory();
491 ASSERT_TRUE(bytes);
492
493 uint8_t suffix_bytes[kMemorySize / 2 - sizeof(instructions)];
494 memset(suffix_bytes, 0, sizeof(suffix_bytes));
495 EXPECT_TRUE(memcmp(bytes + kOffset, instructions, sizeof(instructions)) == 0);
496 EXPECT_TRUE(memcmp(bytes + kOffset + sizeof(instructions),
497 suffix_bytes, sizeof(suffix_bytes)) == 0);
498 }
499
500 // Test that the memory region around the instruction pointer is
501 // bounded correctly on the high end.
TEST_F(ExceptionHandlerTest,InstructionPointerMemoryMaxBound)502 TEST_F(ExceptionHandlerTest, InstructionPointerMemoryMaxBound) {
503 // Give the child process a pipe to report back on.
504 int fds[2];
505 ASSERT_EQ(0, pipe(fds));
506
507 // These are defined here so the parent can use them to check the
508 // data from the minidump afterwards.
509 // Use 4k here because the OS will hand out a single page even
510 // if a smaller size is requested, and this test wants to
511 // test the upper bound of the memory range.
512 const uint32_t kMemorySize = 4096; // bytes
513 // This crashes with SIGILL on x86/x86-64/arm.
514 const unsigned char instructions[] = { 0xff, 0xff, 0xff, 0xff };
515 const int kOffset = kMemorySize - sizeof(instructions);
516
517 pid_t pid = fork();
518 if (pid == 0) {
519 close(fds[0]);
520 ExceptionHandler eh(tempDir.path(), NULL, MDCallback, &fds[1], true, NULL);
521 // Get some executable memory.
522 char* memory =
523 reinterpret_cast<char*>(mmap(NULL,
524 kMemorySize,
525 PROT_READ | PROT_WRITE | PROT_EXEC,
526 MAP_PRIVATE | MAP_ANON,
527 -1,
528 0));
529 if (!memory)
530 exit(0);
531
532 // Write some instructions that will crash. Put them at the start
533 // of the block of memory, to ensure that the memory bounding
534 // works properly.
535 memcpy(memory + kOffset, instructions, sizeof(instructions));
536
537 // Now execute the instructions, which should crash.
538 typedef void (*void_function)(void);
539 void_function memory_function =
540 reinterpret_cast<void_function>(memory + kOffset);
541 memory_function();
542 // not reached
543 exit(1);
544 }
545 // In the parent process.
546 ASSERT_NE(-1, pid);
547 close(fds[1]);
548
549 // Wait for the background process to return the minidump file.
550 close(fds[1]);
551 char minidump_file[PATH_MAX];
552 ssize_t nbytes = read(fds[0], minidump_file, sizeof(minidump_file));
553 ASSERT_NE(0, nbytes);
554 // Ensure that minidump file exists and is > 0 bytes.
555 struct stat st;
556 ASSERT_EQ(0, stat(minidump_file, &st));
557 ASSERT_LT(0, st.st_size);
558
559 // Child process should have exited with a zero status.
560 int ret;
561 ASSERT_EQ(pid, waitpid(pid, &ret, 0));
562 EXPECT_NE(0, WIFEXITED(ret));
563 EXPECT_EQ(0, WEXITSTATUS(ret));
564
565 // Read the minidump. Locate the exception record and the
566 // memory list, and then ensure that there is a memory region
567 // in the memory list that covers the instruction pointer from
568 // the exception record.
569 Minidump minidump(minidump_file);
570 ASSERT_TRUE(minidump.Read());
571
572 MinidumpException* exception = minidump.GetException();
573 MinidumpMemoryList* memory_list = minidump.GetMemoryList();
574 ASSERT_TRUE(exception);
575 ASSERT_TRUE(memory_list);
576 ASSERT_NE((unsigned int)0, memory_list->region_count());
577
578 MinidumpContext* context = exception->GetContext();
579 ASSERT_TRUE(context);
580
581 uint64_t instruction_pointer;
582 ASSERT_TRUE(context->GetInstructionPointer(&instruction_pointer));
583
584 MinidumpMemoryRegion* region =
585 memory_list->GetMemoryRegionForAddress(instruction_pointer);
586 EXPECT_TRUE(region);
587
588 const size_t kPrefixSize = 128; // bytes
589 EXPECT_EQ(kPrefixSize + sizeof(instructions), region->GetSize());
590 const uint8_t* bytes = region->GetMemory();
591 ASSERT_TRUE(bytes);
592
593 uint8_t prefix_bytes[kPrefixSize];
594 memset(prefix_bytes, 0, sizeof(prefix_bytes));
595 EXPECT_TRUE(memcmp(bytes, prefix_bytes, sizeof(prefix_bytes)) == 0);
596 EXPECT_TRUE(memcmp(bytes + kPrefixSize,
597 instructions, sizeof(instructions)) == 0);
598 }
599
600 // Ensure that an extra memory block doesn't get added when the
601 // instruction pointer is not in mapped memory.
TEST_F(ExceptionHandlerTest,InstructionPointerMemoryNullPointer)602 TEST_F(ExceptionHandlerTest, InstructionPointerMemoryNullPointer) {
603 // Give the child process a pipe to report back on.
604 int fds[2];
605 ASSERT_EQ(0, pipe(fds));
606
607 pid_t pid = fork();
608 if (pid == 0) {
609 close(fds[0]);
610 ExceptionHandler eh(tempDir.path(), NULL, MDCallback, &fds[1], true, NULL);
611 // Try calling a NULL pointer.
612 typedef void (*void_function)(void);
613 void_function memory_function =
614 reinterpret_cast<void_function>(NULL);
615 memory_function();
616 // not reached
617 exit(1);
618 }
619 // In the parent process.
620 ASSERT_NE(-1, pid);
621 close(fds[1]);
622
623 // Wait for the background process to return the minidump file.
624 close(fds[1]);
625 char minidump_file[PATH_MAX];
626 ssize_t nbytes = read(fds[0], minidump_file, sizeof(minidump_file));
627 ASSERT_NE(0, nbytes);
628 // Ensure that minidump file exists and is > 0 bytes.
629 struct stat st;
630 ASSERT_EQ(0, stat(minidump_file, &st));
631 ASSERT_LT(0, st.st_size);
632
633 // Child process should have exited with a zero status.
634 int ret;
635 ASSERT_EQ(pid, waitpid(pid, &ret, 0));
636 EXPECT_NE(0, WIFEXITED(ret));
637 EXPECT_EQ(0, WEXITSTATUS(ret));
638
639 // Read the minidump. Locate the exception record and the
640 // memory list, and then ensure that there is only one memory region
641 // in the memory list (the thread memory from the single thread).
642 Minidump minidump(minidump_file);
643 ASSERT_TRUE(minidump.Read());
644
645 MinidumpException* exception = minidump.GetException();
646 MinidumpMemoryList* memory_list = minidump.GetMemoryList();
647 ASSERT_TRUE(exception);
648 ASSERT_TRUE(memory_list);
649 ASSERT_EQ((unsigned int)1, memory_list->region_count());
650 }
651
Junk(void *)652 static void *Junk(void *) {
653 sleep(1000000);
654 return NULL;
655 }
656
657 // Test that the memory list gets written correctly when multiple
658 // threads are running.
TEST_F(ExceptionHandlerTest,MemoryListMultipleThreads)659 TEST_F(ExceptionHandlerTest, MemoryListMultipleThreads) {
660 // Give the child process a pipe to report back on.
661 int fds[2];
662 ASSERT_EQ(0, pipe(fds));
663
664 pid_t pid = fork();
665 if (pid == 0) {
666 close(fds[0]);
667 ExceptionHandler eh(tempDir.path(), NULL, MDCallback, &fds[1], true, NULL);
668
669 // Run an extra thread so >2 memory regions will be written.
670 pthread_t junk_thread;
671 if (pthread_create(&junk_thread, NULL, Junk, NULL) == 0)
672 pthread_detach(junk_thread);
673
674 // Just crash.
675 Crasher();
676
677 // not reached
678 exit(1);
679 }
680 // In the parent process.
681 ASSERT_NE(-1, pid);
682 close(fds[1]);
683
684 // Wait for the background process to return the minidump file.
685 close(fds[1]);
686 char minidump_file[PATH_MAX];
687 ssize_t nbytes = read(fds[0], minidump_file, sizeof(minidump_file));
688 ASSERT_NE(0, nbytes);
689 // Ensure that minidump file exists and is > 0 bytes.
690 struct stat st;
691 ASSERT_EQ(0, stat(minidump_file, &st));
692 ASSERT_LT(0, st.st_size);
693
694 // Child process should have exited with a zero status.
695 int ret;
696 ASSERT_EQ(pid, waitpid(pid, &ret, 0));
697 EXPECT_NE(0, WIFEXITED(ret));
698 EXPECT_EQ(0, WEXITSTATUS(ret));
699
700 // Read the minidump, and verify that the memory list can be read.
701 Minidump minidump(minidump_file);
702 ASSERT_TRUE(minidump.Read());
703
704 MinidumpMemoryList* memory_list = minidump.GetMemoryList();
705 ASSERT_TRUE(memory_list);
706 // Verify that there are three memory regions:
707 // one per thread, and one for the instruction pointer memory.
708 ASSERT_EQ((unsigned int)3, memory_list->region_count());
709 }
710
711 }
712