PageRenderTime 77ms CodeModel.GetById 16ms app.highlight 54ms RepoModel.GetById 1ms app.codeStats 0ms

/thirdparty/breakpad/client/mac/tests/exception_handler_test.cc

http://github.com/tomahawk-player/tomahawk
C++ | 716 lines | 474 code | 96 blank | 146 comment | 33 complexity | c02635f07421bb5d778fa709fc6b3631 MD5 | raw file
  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  // Ensure that minidump file exists and is > 0 bytes.
126  struct stat st;
127  ASSERT_EQ(0, stat(minidump_file, &st));
128  ASSERT_LT(0, st.st_size);
129
130  // Child process should have exited with a zero status.
131  int ret;
132  ASSERT_EQ(pid, waitpid(pid, &ret, 0));
133  EXPECT_NE(0, WIFEXITED(ret));
134  EXPECT_EQ(0, WEXITSTATUS(ret));
135}
136
137TEST_F(ExceptionHandlerTest, InProcess) {
138  InProcessCrash(false);
139}
140
141#if TARGET_OS_IPHONE
142TEST_F(ExceptionHandlerTest, InProcessAbort) {
143  InProcessCrash(true);
144}
145#endif
146
147static bool DumpNameMDCallback(const char *dump_dir, const char *file_name,
148                               void *context, bool success) {
149  ExceptionHandlerTest *self = reinterpret_cast<ExceptionHandlerTest*>(context);
150  if (dump_dir && file_name) {
151    self->lastDumpName = dump_dir;
152    self->lastDumpName += "/";
153    self->lastDumpName += file_name;
154    self->lastDumpName += ".dmp";
155  }
156  return true;
157}
158
159TEST_F(ExceptionHandlerTest, WriteMinidump) {
160  ExceptionHandler eh(tempDir.path(), NULL, DumpNameMDCallback, this, true,
161                      NULL);
162  ASSERT_TRUE(eh.WriteMinidump());
163
164  // Ensure that minidump file exists and is > 0 bytes.
165  ASSERT_FALSE(lastDumpName.empty());
166  struct stat st;
167  ASSERT_EQ(0, stat(lastDumpName.c_str(), &st));
168  ASSERT_LT(0, st.st_size);
169
170  // The minidump should not contain an exception stream.
171  Minidump minidump(lastDumpName);
172  ASSERT_TRUE(minidump.Read());
173
174  MinidumpException* exception = minidump.GetException();
175  EXPECT_FALSE(exception);
176}
177
178TEST_F(ExceptionHandlerTest, WriteMinidumpWithException) {
179  ExceptionHandler eh(tempDir.path(), NULL, DumpNameMDCallback, this, true,
180                      NULL);
181  ASSERT_TRUE(eh.WriteMinidump(true));
182
183  // Ensure that minidump file exists and is > 0 bytes.
184  ASSERT_FALSE(lastDumpName.empty());
185  struct stat st;
186  ASSERT_EQ(0, stat(lastDumpName.c_str(), &st));
187  ASSERT_LT(0, st.st_size);
188
189  // The minidump should contain an exception stream.
190  Minidump minidump(lastDumpName);
191  ASSERT_TRUE(minidump.Read());
192
193  MinidumpException* exception = minidump.GetException();
194  ASSERT_TRUE(exception);
195  const MDRawExceptionStream* raw_exception = exception->exception();
196  ASSERT_TRUE(raw_exception);
197
198  EXPECT_EQ(MD_EXCEPTION_MAC_BREAKPOINT,
199            raw_exception->exception_record.exception_code);
200}
201
202TEST_F(ExceptionHandlerTest, DumpChildProcess) {
203  const int kTimeoutMs = 2000;
204  // Create a mach port to receive the child task on.
205  char machPortName[128];
206  sprintf(machPortName, "ExceptionHandlerTest.%d", getpid());
207  ReceivePort parent_recv_port(machPortName);
208
209  // Give the child process a pipe to block on.
210  int fds[2];
211  ASSERT_EQ(0, pipe(fds));
212
213  // Fork off a child process to dump.
214  pid_t pid = fork();
215  if (pid == 0) {
216    // In the child process
217    close(fds[1]);
218
219    // Send parent process the task and thread ports.
220    MachSendMessage child_message(0);
221    child_message.AddDescriptor(mach_task_self());
222    child_message.AddDescriptor(mach_thread_self());
223
224    MachPortSender child_sender(machPortName);
225    if (child_sender.SendMessage(child_message, kTimeoutMs) != KERN_SUCCESS)
226      exit(1);
227
228    // Wait for the parent process.
229    uint8_t data;
230    read(fds[0], &data, 1);
231    exit(0);
232  }
233  // In the parent process.
234  ASSERT_NE(-1, pid);
235  close(fds[0]);
236
237  // Read the child's task and thread ports.
238  MachReceiveMessage child_message;
239  ASSERT_EQ(KERN_SUCCESS,
240	    parent_recv_port.WaitForMessage(&child_message, kTimeoutMs));
241  mach_port_t child_task = child_message.GetTranslatedPort(0);
242  mach_port_t child_thread = child_message.GetTranslatedPort(1);
243  ASSERT_NE((mach_port_t)MACH_PORT_NULL, child_task);
244  ASSERT_NE((mach_port_t)MACH_PORT_NULL, child_thread);
245
246  // Write a minidump of the child process.
247  bool result = ExceptionHandler::WriteMinidumpForChild(child_task,
248                                                        child_thread,
249                                                        tempDir.path(),
250                                                        DumpNameMDCallback,
251                                                        this);
252  ASSERT_EQ(true, result);
253
254  // Ensure that minidump file exists and is > 0 bytes.
255  ASSERT_FALSE(lastDumpName.empty());
256  struct stat st;
257  ASSERT_EQ(0, stat(lastDumpName.c_str(), &st));
258  ASSERT_LT(0, st.st_size);
259
260  // Unblock child process
261  uint8_t data = 1;
262  (void)write(fds[1], &data, 1);
263
264  // Child process should have exited with a zero status.
265  int ret;
266  ASSERT_EQ(pid, waitpid(pid, &ret, 0));
267  EXPECT_NE(0, WIFEXITED(ret));
268  EXPECT_EQ(0, WEXITSTATUS(ret));
269}
270
271// Test that memory around the instruction pointer is written
272// to the dump as a MinidumpMemoryRegion.
273TEST_F(ExceptionHandlerTest, InstructionPointerMemory) {
274  // Give the child process a pipe to report back on.
275  int fds[2];
276  ASSERT_EQ(0, pipe(fds));
277
278  // These are defined here so the parent can use them to check the
279  // data from the minidump afterwards.
280  const u_int32_t kMemorySize = 256;  // bytes
281  const int kOffset = kMemorySize / 2;
282  // This crashes with SIGILL on x86/x86-64/arm.
283  const unsigned char instructions[] = { 0xff, 0xff, 0xff, 0xff };
284
285  pid_t pid = fork();
286  if (pid == 0) {
287    close(fds[0]);
288    ExceptionHandler eh(tempDir.path(), NULL, MDCallback, &fds[1], true, NULL);
289    // Get some executable memory.
290    char* memory =
291      reinterpret_cast<char*>(mmap(NULL,
292                                   kMemorySize,
293                                   PROT_READ | PROT_WRITE | PROT_EXEC,
294                                   MAP_PRIVATE | MAP_ANON,
295                                   -1,
296                                   0));
297    if (!memory)
298      exit(0);
299
300    // Write some instructions that will crash. Put them in the middle
301    // of the block of memory, because the minidump should contain 128
302    // bytes on either side of the instruction pointer.
303    memcpy(memory + kOffset, instructions, sizeof(instructions));
304
305    // Now execute the instructions, which should crash.
306    typedef void (*void_function)(void);
307    void_function memory_function =
308      reinterpret_cast<void_function>(memory + kOffset);
309    memory_function();
310    // not reached
311    exit(1);
312  }
313  // In the parent process.
314  ASSERT_NE(-1, pid);
315  close(fds[1]);
316
317  // Wait for the background process to return the minidump file.
318  close(fds[1]);
319  char minidump_file[PATH_MAX];
320  ssize_t nbytes = read(fds[0], minidump_file, sizeof(minidump_file));
321  ASSERT_NE(0, nbytes);
322  // Ensure that minidump file exists and is > 0 bytes.
323  struct stat st;
324  ASSERT_EQ(0, stat(minidump_file, &st));
325  ASSERT_LT(0, st.st_size);
326
327  // Child process should have exited with a zero status.
328  int ret;
329  ASSERT_EQ(pid, waitpid(pid, &ret, 0));
330  EXPECT_NE(0, WIFEXITED(ret));
331  EXPECT_EQ(0, WEXITSTATUS(ret));
332
333  // Read the minidump. Locate the exception record and the
334  // memory list, and then ensure that there is a memory region
335  // in the memory list that covers the instruction pointer from
336  // the exception record.
337  Minidump minidump(minidump_file);
338  ASSERT_TRUE(minidump.Read());
339
340  MinidumpException* exception = minidump.GetException();
341  MinidumpMemoryList* memory_list = minidump.GetMemoryList();
342  ASSERT_TRUE(exception);
343  ASSERT_TRUE(memory_list);
344  ASSERT_NE((unsigned int)0, memory_list->region_count());
345
346  MinidumpContext* context = exception->GetContext();
347  ASSERT_TRUE(context);
348
349  u_int64_t instruction_pointer;
350  switch (context->GetContextCPU()) {
351  case MD_CONTEXT_X86:
352    instruction_pointer = context->GetContextX86()->eip;
353    break;
354  case MD_CONTEXT_AMD64:
355    instruction_pointer = context->GetContextAMD64()->rip;
356    break;
357  case MD_CONTEXT_ARM:
358    instruction_pointer = context->GetContextARM()->iregs[15];
359    break;
360  default:
361    FAIL() << "Unknown context CPU: " << context->GetContextCPU();
362    break;
363  }
364
365  MinidumpMemoryRegion* region =
366    memory_list->GetMemoryRegionForAddress(instruction_pointer);
367  EXPECT_TRUE(region);
368
369  EXPECT_EQ(kMemorySize, region->GetSize());
370  const u_int8_t* bytes = region->GetMemory();
371  ASSERT_TRUE(bytes);
372
373  u_int8_t prefix_bytes[kOffset];
374  u_int8_t suffix_bytes[kMemorySize - kOffset - sizeof(instructions)];
375  memset(prefix_bytes, 0, sizeof(prefix_bytes));
376  memset(suffix_bytes, 0, sizeof(suffix_bytes));
377  EXPECT_TRUE(memcmp(bytes, prefix_bytes, sizeof(prefix_bytes)) == 0);
378  EXPECT_TRUE(memcmp(bytes + kOffset, instructions, sizeof(instructions)) == 0);
379  EXPECT_TRUE(memcmp(bytes + kOffset + sizeof(instructions),
380                     suffix_bytes, sizeof(suffix_bytes)) == 0);
381}
382
383// Test that the memory region around the instruction pointer is
384// bounded correctly on the low end.
385TEST_F(ExceptionHandlerTest, InstructionPointerMemoryMinBound) {
386  // Give the child process a pipe to report back on.
387  int fds[2];
388  ASSERT_EQ(0, pipe(fds));
389
390  // These are defined here so the parent can use them to check the
391  // data from the minidump afterwards.
392  const u_int32_t kMemorySize = 256;  // bytes
393  const int kOffset = 0;
394  // This crashes with SIGILL on x86/x86-64/arm.
395  const unsigned char instructions[] = { 0xff, 0xff, 0xff, 0xff };
396
397  pid_t pid = fork();
398  if (pid == 0) {
399    close(fds[0]);
400    ExceptionHandler eh(tempDir.path(), NULL, MDCallback, &fds[1], true, NULL);
401    // Get some executable memory.
402    char* memory =
403      reinterpret_cast<char*>(mmap(NULL,
404                                   kMemorySize,
405                                   PROT_READ | PROT_WRITE | PROT_EXEC,
406                                   MAP_PRIVATE | MAP_ANON,
407                                   -1,
408                                   0));
409    if (!memory)
410      exit(0);
411
412    // Write some instructions that will crash. Put them at the start
413    // of the block of memory, to ensure that the memory bounding
414    // works properly.
415    memcpy(memory + kOffset, instructions, sizeof(instructions));
416    
417    // Now execute the instructions, which should crash.
418    typedef void (*void_function)(void);
419    void_function memory_function =
420      reinterpret_cast<void_function>(memory + kOffset);
421    memory_function();
422    // not reached
423    exit(1);
424  }
425  // In the parent process.
426  ASSERT_NE(-1, pid);
427  close(fds[1]);
428
429  // Wait for the background process to return the minidump file.
430  close(fds[1]);
431  char minidump_file[PATH_MAX];
432  ssize_t nbytes = read(fds[0], minidump_file, sizeof(minidump_file));
433  ASSERT_NE(0, nbytes);
434  // Ensure that minidump file exists and is > 0 bytes.
435  struct stat st;
436  ASSERT_EQ(0, stat(minidump_file, &st));
437  ASSERT_LT(0, st.st_size);
438
439  // Child process should have exited with a zero status.
440  int ret;
441  ASSERT_EQ(pid, waitpid(pid, &ret, 0));
442  EXPECT_NE(0, WIFEXITED(ret));
443  EXPECT_EQ(0, WEXITSTATUS(ret));
444
445  // Read the minidump. Locate the exception record and the
446  // memory list, and then ensure that there is a memory region
447  // in the memory list that covers the instruction pointer from
448  // the exception record.
449  Minidump minidump(minidump_file);
450  ASSERT_TRUE(minidump.Read());
451
452  MinidumpException* exception = minidump.GetException();
453  MinidumpMemoryList* memory_list = minidump.GetMemoryList();
454  ASSERT_TRUE(exception);
455  ASSERT_TRUE(memory_list);
456  ASSERT_NE((unsigned int)0, memory_list->region_count());
457
458  MinidumpContext* context = exception->GetContext();
459  ASSERT_TRUE(context);
460
461  u_int64_t instruction_pointer;
462  switch (context->GetContextCPU()) {
463  case MD_CONTEXT_X86:
464    instruction_pointer = context->GetContextX86()->eip;
465    break;
466  case MD_CONTEXT_AMD64:
467    instruction_pointer = context->GetContextAMD64()->rip;
468    break;
469  case MD_CONTEXT_ARM:
470    instruction_pointer = context->GetContextARM()->iregs[15];
471    break;
472  default:
473    FAIL() << "Unknown context CPU: " << context->GetContextCPU();
474    break;
475  }
476
477  MinidumpMemoryRegion* region =
478    memory_list->GetMemoryRegionForAddress(instruction_pointer);
479  EXPECT_TRUE(region);
480
481  EXPECT_EQ(kMemorySize / 2, region->GetSize());
482  const u_int8_t* bytes = region->GetMemory();
483  ASSERT_TRUE(bytes);
484
485  u_int8_t suffix_bytes[kMemorySize / 2 - sizeof(instructions)];
486  memset(suffix_bytes, 0, sizeof(suffix_bytes));
487  EXPECT_TRUE(memcmp(bytes + kOffset, instructions, sizeof(instructions)) == 0);
488  EXPECT_TRUE(memcmp(bytes + kOffset + sizeof(instructions),
489                     suffix_bytes, sizeof(suffix_bytes)) == 0);
490}
491
492// Test that the memory region around the instruction pointer is
493// bounded correctly on the high end.
494TEST_F(ExceptionHandlerTest, InstructionPointerMemoryMaxBound) {
495  // Give the child process a pipe to report back on.
496  int fds[2];
497  ASSERT_EQ(0, pipe(fds));
498
499  // These are defined here so the parent can use them to check the
500  // data from the minidump afterwards.
501  // Use 4k here because the OS will hand out a single page even
502  // if a smaller size is requested, and this test wants to
503  // test the upper bound of the memory range.
504  const u_int32_t kMemorySize = 4096;  // bytes
505  // This crashes with SIGILL on x86/x86-64/arm.
506  const unsigned char instructions[] = { 0xff, 0xff, 0xff, 0xff };
507  const int kOffset = kMemorySize - sizeof(instructions);
508
509  pid_t pid = fork();
510  if (pid == 0) {
511    close(fds[0]);
512    ExceptionHandler eh(tempDir.path(), NULL, MDCallback, &fds[1], true, NULL);
513    // Get some executable memory.
514    char* memory =
515      reinterpret_cast<char*>(mmap(NULL,
516                                   kMemorySize,
517                                   PROT_READ | PROT_WRITE | PROT_EXEC,
518                                   MAP_PRIVATE | MAP_ANON,
519                                   -1,
520                                   0));
521    if (!memory)
522      exit(0);
523
524    // Write some instructions that will crash. Put them at the start
525    // of the block of memory, to ensure that the memory bounding
526    // works properly.
527    memcpy(memory + kOffset, instructions, sizeof(instructions));
528    
529    // Now execute the instructions, which should crash.
530    typedef void (*void_function)(void);
531    void_function memory_function =
532      reinterpret_cast<void_function>(memory + kOffset);
533    memory_function();
534    // not reached
535    exit(1);
536  }
537  // In the parent process.
538  ASSERT_NE(-1, pid);
539  close(fds[1]);
540
541  // Wait for the background process to return the minidump file.
542  close(fds[1]);
543  char minidump_file[PATH_MAX];
544  ssize_t nbytes = read(fds[0], minidump_file, sizeof(minidump_file));
545  ASSERT_NE(0, nbytes);
546  // Ensure that minidump file exists and is > 0 bytes.
547  struct stat st;
548  ASSERT_EQ(0, stat(minidump_file, &st));
549  ASSERT_LT(0, st.st_size);
550
551  // Child process should have exited with a zero status.
552  int ret;
553  ASSERT_EQ(pid, waitpid(pid, &ret, 0));
554  EXPECT_NE(0, WIFEXITED(ret));
555  EXPECT_EQ(0, WEXITSTATUS(ret));
556
557  // Read the minidump. Locate the exception record and the
558  // memory list, and then ensure that there is a memory region
559  // in the memory list that covers the instruction pointer from
560  // the exception record.
561  Minidump minidump(minidump_file);
562  ASSERT_TRUE(minidump.Read());
563
564  MinidumpException* exception = minidump.GetException();
565  MinidumpMemoryList* memory_list = minidump.GetMemoryList();
566  ASSERT_TRUE(exception);
567  ASSERT_TRUE(memory_list);
568  ASSERT_NE((unsigned int)0, memory_list->region_count());
569
570  MinidumpContext* context = exception->GetContext();
571  ASSERT_TRUE(context);
572
573  u_int64_t instruction_pointer;
574  switch (context->GetContextCPU()) {
575  case MD_CONTEXT_X86:
576    instruction_pointer = context->GetContextX86()->eip;
577    break;
578  case MD_CONTEXT_AMD64:
579    instruction_pointer = context->GetContextAMD64()->rip;
580    break;
581  case MD_CONTEXT_ARM:
582    instruction_pointer = context->GetContextARM()->iregs[15];
583    break;
584  default:
585    FAIL() << "Unknown context CPU: " << context->GetContextCPU();
586    break;
587  }
588
589  MinidumpMemoryRegion* region =
590    memory_list->GetMemoryRegionForAddress(instruction_pointer);
591  EXPECT_TRUE(region);
592
593  const size_t kPrefixSize = 128;  // bytes
594  EXPECT_EQ(kPrefixSize + sizeof(instructions), region->GetSize());
595  const u_int8_t* bytes = region->GetMemory();
596  ASSERT_TRUE(bytes);
597
598  u_int8_t prefix_bytes[kPrefixSize];
599  memset(prefix_bytes, 0, sizeof(prefix_bytes));
600  EXPECT_TRUE(memcmp(bytes, prefix_bytes, sizeof(prefix_bytes)) == 0);
601  EXPECT_TRUE(memcmp(bytes + kPrefixSize,
602                     instructions, sizeof(instructions)) == 0);
603}
604
605// Ensure that an extra memory block doesn't get added when the
606// instruction pointer is not in mapped memory.
607TEST_F(ExceptionHandlerTest, InstructionPointerMemoryNullPointer) {
608  // Give the child process a pipe to report back on.
609  int fds[2];
610  ASSERT_EQ(0, pipe(fds));
611
612  pid_t pid = fork();
613  if (pid == 0) {
614    close(fds[0]);
615    ExceptionHandler eh(tempDir.path(), NULL, MDCallback, &fds[1], true, NULL);
616    // Try calling a NULL pointer.
617    typedef void (*void_function)(void);
618    void_function memory_function =
619      reinterpret_cast<void_function>(NULL);
620    memory_function();
621    // not reached
622    exit(1);
623  }
624  // In the parent process.
625  ASSERT_NE(-1, pid);
626  close(fds[1]);
627
628  // Wait for the background process to return the minidump file.
629  close(fds[1]);
630  char minidump_file[PATH_MAX];
631  ssize_t nbytes = read(fds[0], minidump_file, sizeof(minidump_file));
632  ASSERT_NE(0, nbytes);
633  // Ensure that minidump file exists and is > 0 bytes.
634  struct stat st;
635  ASSERT_EQ(0, stat(minidump_file, &st));
636  ASSERT_LT(0, st.st_size);
637
638  // Child process should have exited with a zero status.
639  int ret;
640  ASSERT_EQ(pid, waitpid(pid, &ret, 0));
641  EXPECT_NE(0, WIFEXITED(ret));
642  EXPECT_EQ(0, WEXITSTATUS(ret));
643
644  // Read the minidump. Locate the exception record and the
645  // memory list, and then ensure that there is only one memory region
646  // in the memory list (the thread memory from the single thread).
647  Minidump minidump(minidump_file);
648  ASSERT_TRUE(minidump.Read());
649
650  MinidumpException* exception = minidump.GetException();
651  MinidumpMemoryList* memory_list = minidump.GetMemoryList();
652  ASSERT_TRUE(exception);
653  ASSERT_TRUE(memory_list);
654  ASSERT_EQ((unsigned int)1, memory_list->region_count());
655}
656
657static void *Junk(void *) {
658  sleep(1000000);
659  return NULL;
660}
661
662// Test that the memory list gets written correctly when multiple
663// threads are running.
664TEST_F(ExceptionHandlerTest, MemoryListMultipleThreads) {
665  // Give the child process a pipe to report back on.
666  int fds[2];
667  ASSERT_EQ(0, pipe(fds));
668
669  pid_t pid = fork();
670  if (pid == 0) {
671    close(fds[0]);
672    ExceptionHandler eh(tempDir.path(), NULL, MDCallback, &fds[1], true, NULL);
673
674    // Run an extra thread so >2 memory regions will be written.
675    pthread_t junk_thread;
676    if (pthread_create(&junk_thread, NULL, Junk, NULL) == 0)
677      pthread_detach(junk_thread);
678
679    // Just crash.
680    Crasher();
681
682    // not reached
683    exit(1);
684  }
685  // In the parent process.
686  ASSERT_NE(-1, pid);
687  close(fds[1]);
688
689  // Wait for the background process to return the minidump file.
690  close(fds[1]);
691  char minidump_file[PATH_MAX];
692  ssize_t nbytes = read(fds[0], minidump_file, sizeof(minidump_file));
693  ASSERT_NE(0, nbytes);
694  // Ensure that minidump file exists and is > 0 bytes.
695  struct stat st;
696  ASSERT_EQ(0, stat(minidump_file, &st));
697  ASSERT_LT(0, st.st_size);
698
699  // Child process should have exited with a zero status.
700  int ret;
701  ASSERT_EQ(pid, waitpid(pid, &ret, 0));
702  EXPECT_NE(0, WIFEXITED(ret));
703  EXPECT_EQ(0, WEXITSTATUS(ret));
704
705  // Read the minidump, and verify that the memory list can be read.
706  Minidump minidump(minidump_file);
707  ASSERT_TRUE(minidump.Read());
708
709  MinidumpMemoryList* memory_list = minidump.GetMemoryList();
710  ASSERT_TRUE(memory_list);
711  // Verify that there are three memory regions:
712  // one per thread, and one for the instruction pointer memory.
713  ASSERT_EQ((unsigned int)3, memory_list->region_count());
714}
715
716}