xref: /aosp_15_r20/external/google-breakpad/src/client/mac/tests/exception_handler_test.cc (revision 9712c20fc9bbfbac4935993a2ca0b3958c5adad2)
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