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