• 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/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