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