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
43namespace 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.
47std::ostringstream info_log;
48}
49
50namespace {
51using std::string;
52using google_breakpad::AutoTempDir;
53using google_breakpad::ExceptionHandler;
54using google_breakpad::MachPortSender;
55using google_breakpad::MachReceiveMessage;
56using google_breakpad::MachSendMessage;
57using google_breakpad::Minidump;
58using google_breakpad::MinidumpContext;
59using google_breakpad::MinidumpException;
60using google_breakpad::MinidumpMemoryList;
61using google_breakpad::MinidumpMemoryRegion;
62using google_breakpad::ReceivePort;
63using testing::Test;
64
65class ExceptionHandlerTest : public Test {
66 public:
67  void InProcessCrash(bool aborting);
68  AutoTempDir tempDir;
69  string lastDumpName;
70};
71
72static void Crasher() {
73  int *a = (int*)0x42;
74
75  fprintf(stdout, "Going to crash...\n");
76  fprintf(stdout, "A = %d", *a);
77}
78
79static void AbortCrasher() {
80  fprintf(stdout, "Going to crash...\n");
81  abort();
82}
83
84static void SoonToCrash(void(*crasher)()) {
85  crasher();
86}
87
88static 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
103void 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
173TEST_F(ExceptionHandlerTest, InProcess) {
174  InProcessCrash(false);
175}
176
177TEST_F(ExceptionHandlerTest, InProcessAbort) {
178  InProcessCrash(true);
179}
180
181static 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
193TEST_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
212TEST_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
236TEST_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.
307TEST_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.
406TEST_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.
502TEST_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.
602TEST_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
652static 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.
659TEST_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