• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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