/ src / client / mac / tests / exception_handler_test.cc
exception_handler_test.cc
  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  
 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  
 83  static void AbortCrasher() {
 84    fprintf(stdout, "Going to crash...\n");
 85    abort();
 86  }
 87  
 88  static void SoonToCrash(void(*crasher)()) {
 89    crasher();
 90  }
 91  
 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  
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  
177  TEST_F(ExceptionHandlerTest, InProcess) {
178    InProcessCrash(false);
179  }
180  
181  TEST_F(ExceptionHandlerTest, InProcessAbort) {
182    InProcessCrash(true);
183  }
184  
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  
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  
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  
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.
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.
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.
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.
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  
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.
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  }