144af96cd2d1218224ac4258fee102891bd3f25d0ted.mielczarek@gmail.com// Copyright (c) 2010, Google Inc.
25ac2b9a569890f165478f91670dcdd553ce2d10ewaylonis// All rights reserved.
35ac2b9a569890f165478f91670dcdd553ce2d10ewaylonis//
45ac2b9a569890f165478f91670dcdd553ce2d10ewaylonis// Redistribution and use in source and binary forms, with or without
55ac2b9a569890f165478f91670dcdd553ce2d10ewaylonis// modification, are permitted provided that the following conditions are
65ac2b9a569890f165478f91670dcdd553ce2d10ewaylonis// met:
75ac2b9a569890f165478f91670dcdd553ce2d10ewaylonis//
85ac2b9a569890f165478f91670dcdd553ce2d10ewaylonis//     * Redistributions of source code must retain the above copyright
95ac2b9a569890f165478f91670dcdd553ce2d10ewaylonis// notice, this list of conditions and the following disclaimer.
105ac2b9a569890f165478f91670dcdd553ce2d10ewaylonis//     * Redistributions in binary form must reproduce the above
115ac2b9a569890f165478f91670dcdd553ce2d10ewaylonis// copyright notice, this list of conditions and the following disclaimer
125ac2b9a569890f165478f91670dcdd553ce2d10ewaylonis// in the documentation and/or other materials provided with the
135ac2b9a569890f165478f91670dcdd553ce2d10ewaylonis// distribution.
145ac2b9a569890f165478f91670dcdd553ce2d10ewaylonis//     * Neither the name of Google Inc. nor the names of its
155ac2b9a569890f165478f91670dcdd553ce2d10ewaylonis// contributors may be used to endorse or promote products derived from
165ac2b9a569890f165478f91670dcdd553ce2d10ewaylonis// this software without specific prior written permission.
175ac2b9a569890f165478f91670dcdd553ce2d10ewaylonis//
185ac2b9a569890f165478f91670dcdd553ce2d10ewaylonis// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
195ac2b9a569890f165478f91670dcdd553ce2d10ewaylonis// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
205ac2b9a569890f165478f91670dcdd553ce2d10ewaylonis// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
215ac2b9a569890f165478f91670dcdd553ce2d10ewaylonis// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
225ac2b9a569890f165478f91670dcdd553ce2d10ewaylonis// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
235ac2b9a569890f165478f91670dcdd553ce2d10ewaylonis// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
245ac2b9a569890f165478f91670dcdd553ce2d10ewaylonis// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
255ac2b9a569890f165478f91670dcdd553ce2d10ewaylonis// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
265ac2b9a569890f165478f91670dcdd553ce2d10ewaylonis// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
275ac2b9a569890f165478f91670dcdd553ce2d10ewaylonis// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
285ac2b9a569890f165478f91670dcdd553ce2d10ewaylonis// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
295ac2b9a569890f165478f91670dcdd553ce2d10ewaylonis
3044af96cd2d1218224ac4258fee102891bd3f25d0ted.mielczarek@gmail.com// exception_handler_test.cc: Unit tests for google_breakpad::ExceptionHandler
31e5dc60822e5938fea2ae892ccddb906641ba174emmentovai
3230b075c4a5f9fabd3b66f3c607c12337f78a62b5ted.mielczarek#include <pthread.h>
334621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek#include <sys/mman.h>
3444af96cd2d1218224ac4258fee102891bd3f25d0ted.mielczarek@gmail.com#include <sys/stat.h>
355ac2b9a569890f165478f91670dcdd553ce2d10ewaylonis#include <unistd.h>
365ac2b9a569890f165478f91670dcdd553ce2d10ewaylonis
3744af96cd2d1218224ac4258fee102891bd3f25d0ted.mielczarek@gmail.com#include "breakpad_googletest_includes.h"
3844af96cd2d1218224ac4258fee102891bd3f25d0ted.mielczarek@gmail.com#include "client/mac/handler/exception_handler.h"
39144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com#include "common/mac/MachIPC.h"
40b12089f06d061d1ae393f208d37dc17e003d21c4qsr@chromium.org#include "common/tests/auto_tempdir.h"
414621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek#include "google_breakpad/processor/minidump.h"
424621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek
434621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczareknamespace google_breakpad {
444621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek// This acts as the log sink for INFO logging from the processor
454621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek// logging code. The logging output confuses XCode and makes it think
464621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek// there are unit test failures. testlogging.h handles the overriding.
474621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarekstd::ostringstream info_log;
484621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek}
495ac2b9a569890f165478f91670dcdd553ce2d10ewaylonis
50315fd78199bc606ee02cb085dacadd58e0fc40c8ted.mielczarek@gmail.comnamespace {
515ac2b9a569890f165478f91670dcdd553ce2d10ewaylonisusing std::string;
5244af96cd2d1218224ac4258fee102891bd3f25d0ted.mielczarek@gmail.comusing google_breakpad::AutoTempDir;
53e5dc60822e5938fea2ae892ccddb906641ba174emmentovaiusing google_breakpad::ExceptionHandler;
54a599ae80aa44f2f6dbe8c5b86d2e503a8c7b6639ted.mielczarek@gmail.comusing google_breakpad::MachPortSender;
55a599ae80aa44f2f6dbe8c5b86d2e503a8c7b6639ted.mielczarek@gmail.comusing google_breakpad::MachReceiveMessage;
56a599ae80aa44f2f6dbe8c5b86d2e503a8c7b6639ted.mielczarek@gmail.comusing google_breakpad::MachSendMessage;
574621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarekusing google_breakpad::Minidump;
584621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarekusing google_breakpad::MinidumpContext;
594621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarekusing google_breakpad::MinidumpException;
604621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarekusing google_breakpad::MinidumpMemoryList;
614621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarekusing google_breakpad::MinidumpMemoryRegion;
62a599ae80aa44f2f6dbe8c5b86d2e503a8c7b6639ted.mielczarek@gmail.comusing google_breakpad::ReceivePort;
6344af96cd2d1218224ac4258fee102891bd3f25d0ted.mielczarek@gmail.comusing testing::Test;
645ac2b9a569890f165478f91670dcdd553ce2d10ewaylonis
65144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.comclass ExceptionHandlerTest : public Test {
66144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com public:
67094ca0f8a92d3f154fc78e31c2f6722b0785d5c5qsr@chromium.org  void InProcessCrash(bool aborting);
68144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com  AutoTempDir tempDir;
69144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com  string lastDumpName;
70144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com};
71144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com
725ac2b9a569890f165478f91670dcdd553ce2d10ewaylonisstatic void Crasher() {
7361e88c7ad7eb072977b4d4d26bcf8929b75af2d4ted.mielczarek  int *a = (int*)0x42;
745ac2b9a569890f165478f91670dcdd553ce2d10ewaylonis
7544af96cd2d1218224ac4258fee102891bd3f25d0ted.mielczarek@gmail.com  fprintf(stdout, "Going to crash...\n");
765ac2b9a569890f165478f91670dcdd553ce2d10ewaylonis  fprintf(stdout, "A = %d", *a);
775ac2b9a569890f165478f91670dcdd553ce2d10ewaylonis}
785ac2b9a569890f165478f91670dcdd553ce2d10ewaylonis
79094ca0f8a92d3f154fc78e31c2f6722b0785d5c5qsr@chromium.orgstatic void AbortCrasher() {
80094ca0f8a92d3f154fc78e31c2f6722b0785d5c5qsr@chromium.org  fprintf(stdout, "Going to crash...\n");
81094ca0f8a92d3f154fc78e31c2f6722b0785d5c5qsr@chromium.org  abort();
82094ca0f8a92d3f154fc78e31c2f6722b0785d5c5qsr@chromium.org}
83094ca0f8a92d3f154fc78e31c2f6722b0785d5c5qsr@chromium.org
84094ca0f8a92d3f154fc78e31c2f6722b0785d5c5qsr@chromium.orgstatic void SoonToCrash(void(*crasher)()) {
85094ca0f8a92d3f154fc78e31c2f6722b0785d5c5qsr@chromium.org  crasher();
865ac2b9a569890f165478f91670dcdd553ce2d10ewaylonis}
875ac2b9a569890f165478f91670dcdd553ce2d10ewaylonis
884ac61acb3a7dad6ce722fe07564be8ec92713228dmaclachstatic bool MDCallback(const char *dump_dir, const char *file_name,
894ac61acb3a7dad6ce722fe07564be8ec92713228dmaclach                       void *context, bool success) {
905ac2b9a569890f165478f91670dcdd553ce2d10ewaylonis  string path(dump_dir);
9144af96cd2d1218224ac4258fee102891bd3f25d0ted.mielczarek@gmail.com  path.append("/");
925ac2b9a569890f165478f91670dcdd553ce2d10ewaylonis  path.append(file_name);
935ac2b9a569890f165478f91670dcdd553ce2d10ewaylonis  path.append(".dmp");
945ac2b9a569890f165478f91670dcdd553ce2d10ewaylonis
9544af96cd2d1218224ac4258fee102891bd3f25d0ted.mielczarek@gmail.com  int fd = *reinterpret_cast<int*>(context);
9644af96cd2d1218224ac4258fee102891bd3f25d0ted.mielczarek@gmail.com  (void)write(fd, path.c_str(), path.length() + 1);
9744af96cd2d1218224ac4258fee102891bd3f25d0ted.mielczarek@gmail.com  close(fd);
983ebdb1bd7ae38bf0fb205dfaa2f5fde3d67ea141nealsid  exit(0);
9944af96cd2d1218224ac4258fee102891bd3f25d0ted.mielczarek@gmail.com  // not reached
10044af96cd2d1218224ac4258fee102891bd3f25d0ted.mielczarek@gmail.com  return true;
1015ac2b9a569890f165478f91670dcdd553ce2d10ewaylonis}
1025ac2b9a569890f165478f91670dcdd553ce2d10ewaylonis
103094ca0f8a92d3f154fc78e31c2f6722b0785d5c5qsr@chromium.orgvoid ExceptionHandlerTest::InProcessCrash(bool aborting) {
10444af96cd2d1218224ac4258fee102891bd3f25d0ted.mielczarek@gmail.com  // Give the child process a pipe to report back on.
10544af96cd2d1218224ac4258fee102891bd3f25d0ted.mielczarek@gmail.com  int fds[2];
10644af96cd2d1218224ac4258fee102891bd3f25d0ted.mielczarek@gmail.com  ASSERT_EQ(0, pipe(fds));
10744af96cd2d1218224ac4258fee102891bd3f25d0ted.mielczarek@gmail.com  // Fork off a child process so it can crash.
10844af96cd2d1218224ac4258fee102891bd3f25d0ted.mielczarek@gmail.com  pid_t pid = fork();
10944af96cd2d1218224ac4258fee102891bd3f25d0ted.mielczarek@gmail.com  if (pid == 0) {
110144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com    // In the child process.
11144af96cd2d1218224ac4258fee102891bd3f25d0ted.mielczarek@gmail.com    close(fds[0]);
112b12089f06d061d1ae393f208d37dc17e003d21c4qsr@chromium.org    ExceptionHandler eh(tempDir.path(), NULL, MDCallback, &fds[1], true, NULL);
11344af96cd2d1218224ac4258fee102891bd3f25d0ted.mielczarek@gmail.com    // crash
114094ca0f8a92d3f154fc78e31c2f6722b0785d5c5qsr@chromium.org    SoonToCrash(aborting ? &AbortCrasher : &Crasher);
11544af96cd2d1218224ac4258fee102891bd3f25d0ted.mielczarek@gmail.com    // not reached
11644af96cd2d1218224ac4258fee102891bd3f25d0ted.mielczarek@gmail.com    exit(1);
1175ac2b9a569890f165478f91670dcdd553ce2d10ewaylonis  }
118144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com  // In the parent process.
11944af96cd2d1218224ac4258fee102891bd3f25d0ted.mielczarek@gmail.com  ASSERT_NE(-1, pid);
12044af96cd2d1218224ac4258fee102891bd3f25d0ted.mielczarek@gmail.com  // Wait for the background process to return the minidump file.
12144af96cd2d1218224ac4258fee102891bd3f25d0ted.mielczarek@gmail.com  close(fds[1]);
12244af96cd2d1218224ac4258fee102891bd3f25d0ted.mielczarek@gmail.com  char minidump_file[PATH_MAX];
12344af96cd2d1218224ac4258fee102891bd3f25d0ted.mielczarek@gmail.com  ssize_t nbytes = read(fds[0], minidump_file, sizeof(minidump_file));
12444af96cd2d1218224ac4258fee102891bd3f25d0ted.mielczarek@gmail.com  ASSERT_NE(0, nbytes);
1256da6dcc0be00e04fbeed83c773d07eb6598d4746ted.mielczarek@gmail.com
1266da6dcc0be00e04fbeed83c773d07eb6598d4746ted.mielczarek@gmail.com  Minidump minidump(minidump_file);
1276da6dcc0be00e04fbeed83c773d07eb6598d4746ted.mielczarek@gmail.com  ASSERT_TRUE(minidump.Read());
1286da6dcc0be00e04fbeed83c773d07eb6598d4746ted.mielczarek@gmail.com
1296da6dcc0be00e04fbeed83c773d07eb6598d4746ted.mielczarek@gmail.com  MinidumpException* exception = minidump.GetException();
1306da6dcc0be00e04fbeed83c773d07eb6598d4746ted.mielczarek@gmail.com  ASSERT_TRUE(exception);
1316da6dcc0be00e04fbeed83c773d07eb6598d4746ted.mielczarek@gmail.com
1326da6dcc0be00e04fbeed83c773d07eb6598d4746ted.mielczarek@gmail.com  const MDRawExceptionStream* raw_exception = exception->exception();
1336da6dcc0be00e04fbeed83c773d07eb6598d4746ted.mielczarek@gmail.com  ASSERT_TRUE(raw_exception);
1346da6dcc0be00e04fbeed83c773d07eb6598d4746ted.mielczarek@gmail.com
1356da6dcc0be00e04fbeed83c773d07eb6598d4746ted.mielczarek@gmail.com  if (aborting) {
1366da6dcc0be00e04fbeed83c773d07eb6598d4746ted.mielczarek@gmail.com    EXPECT_EQ(MD_EXCEPTION_MAC_SOFTWARE,
1376da6dcc0be00e04fbeed83c773d07eb6598d4746ted.mielczarek@gmail.com              raw_exception->exception_record.exception_code);
1386da6dcc0be00e04fbeed83c773d07eb6598d4746ted.mielczarek@gmail.com    EXPECT_EQ(MD_EXCEPTION_CODE_MAC_ABORT,
1396da6dcc0be00e04fbeed83c773d07eb6598d4746ted.mielczarek@gmail.com              raw_exception->exception_record.exception_flags);
1406da6dcc0be00e04fbeed83c773d07eb6598d4746ted.mielczarek@gmail.com  } else {
1416da6dcc0be00e04fbeed83c773d07eb6598d4746ted.mielczarek@gmail.com    EXPECT_EQ(MD_EXCEPTION_MAC_BAD_ACCESS,
1426da6dcc0be00e04fbeed83c773d07eb6598d4746ted.mielczarek@gmail.com              raw_exception->exception_record.exception_code);
1436da6dcc0be00e04fbeed83c773d07eb6598d4746ted.mielczarek@gmail.com#if defined(__x86_64__)
1446da6dcc0be00e04fbeed83c773d07eb6598d4746ted.mielczarek@gmail.com    EXPECT_EQ(MD_EXCEPTION_CODE_MAC_INVALID_ADDRESS,
1456da6dcc0be00e04fbeed83c773d07eb6598d4746ted.mielczarek@gmail.com              raw_exception->exception_record.exception_flags);
1466da6dcc0be00e04fbeed83c773d07eb6598d4746ted.mielczarek@gmail.com#elif defined(__i386__)
1476da6dcc0be00e04fbeed83c773d07eb6598d4746ted.mielczarek@gmail.com    EXPECT_EQ(MD_EXCEPTION_CODE_MAC_PROTECTION_FAILURE,
1486da6dcc0be00e04fbeed83c773d07eb6598d4746ted.mielczarek@gmail.com              raw_exception->exception_record.exception_flags);
1496da6dcc0be00e04fbeed83c773d07eb6598d4746ted.mielczarek@gmail.com#endif
1506da6dcc0be00e04fbeed83c773d07eb6598d4746ted.mielczarek@gmail.com  }
1516da6dcc0be00e04fbeed83c773d07eb6598d4746ted.mielczarek@gmail.com
1526da6dcc0be00e04fbeed83c773d07eb6598d4746ted.mielczarek@gmail.com  const MinidumpContext* context = exception->GetContext();
1536da6dcc0be00e04fbeed83c773d07eb6598d4746ted.mielczarek@gmail.com  ASSERT_TRUE(context);
1546da6dcc0be00e04fbeed83c773d07eb6598d4746ted.mielczarek@gmail.com
1556da6dcc0be00e04fbeed83c773d07eb6598d4746ted.mielczarek@gmail.com  uint64_t instruction_pointer;
1566da6dcc0be00e04fbeed83c773d07eb6598d4746ted.mielczarek@gmail.com  ASSERT_TRUE(context->GetInstructionPointer(&instruction_pointer));
1576da6dcc0be00e04fbeed83c773d07eb6598d4746ted.mielczarek@gmail.com
1586da6dcc0be00e04fbeed83c773d07eb6598d4746ted.mielczarek@gmail.com  // Ideally would like to sanity check that abort() is on the stack
1596da6dcc0be00e04fbeed83c773d07eb6598d4746ted.mielczarek@gmail.com  // but that's hard.
1606da6dcc0be00e04fbeed83c773d07eb6598d4746ted.mielczarek@gmail.com  MinidumpMemoryList* memory_list = minidump.GetMemoryList();
1616da6dcc0be00e04fbeed83c773d07eb6598d4746ted.mielczarek@gmail.com  ASSERT_TRUE(memory_list);
1626da6dcc0be00e04fbeed83c773d07eb6598d4746ted.mielczarek@gmail.com  MinidumpMemoryRegion* region =
1636da6dcc0be00e04fbeed83c773d07eb6598d4746ted.mielczarek@gmail.com      memory_list->GetMemoryRegionForAddress(instruction_pointer);
1646da6dcc0be00e04fbeed83c773d07eb6598d4746ted.mielczarek@gmail.com  EXPECT_TRUE(region);
16544af96cd2d1218224ac4258fee102891bd3f25d0ted.mielczarek@gmail.com
16644af96cd2d1218224ac4258fee102891bd3f25d0ted.mielczarek@gmail.com  // Child process should have exited with a zero status.
16744af96cd2d1218224ac4258fee102891bd3f25d0ted.mielczarek@gmail.com  int ret;
16844af96cd2d1218224ac4258fee102891bd3f25d0ted.mielczarek@gmail.com  ASSERT_EQ(pid, waitpid(pid, &ret, 0));
16944af96cd2d1218224ac4258fee102891bd3f25d0ted.mielczarek@gmail.com  EXPECT_NE(0, WIFEXITED(ret));
17044af96cd2d1218224ac4258fee102891bd3f25d0ted.mielczarek@gmail.com  EXPECT_EQ(0, WEXITSTATUS(ret));
1715ac2b9a569890f165478f91670dcdd553ce2d10ewaylonis}
172315fd78199bc606ee02cb085dacadd58e0fc40c8ted.mielczarek@gmail.com
173094ca0f8a92d3f154fc78e31c2f6722b0785d5c5qsr@chromium.orgTEST_F(ExceptionHandlerTest, InProcess) {
174094ca0f8a92d3f154fc78e31c2f6722b0785d5c5qsr@chromium.org  InProcessCrash(false);
175094ca0f8a92d3f154fc78e31c2f6722b0785d5c5qsr@chromium.org}
176094ca0f8a92d3f154fc78e31c2f6722b0785d5c5qsr@chromium.org
177094ca0f8a92d3f154fc78e31c2f6722b0785d5c5qsr@chromium.orgTEST_F(ExceptionHandlerTest, InProcessAbort) {
178094ca0f8a92d3f154fc78e31c2f6722b0785d5c5qsr@chromium.org  InProcessCrash(true);
179094ca0f8a92d3f154fc78e31c2f6722b0785d5c5qsr@chromium.org}
180094ca0f8a92d3f154fc78e31c2f6722b0785d5c5qsr@chromium.org
1810d9bd40775211c223c75543890d862135f677d67ted.mielczarek@gmail.comstatic bool DumpNameMDCallback(const char *dump_dir, const char *file_name,
1820d9bd40775211c223c75543890d862135f677d67ted.mielczarek@gmail.com                               void *context, bool success) {
183144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com  ExceptionHandlerTest *self = reinterpret_cast<ExceptionHandlerTest*>(context);
184144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com  if (dump_dir && file_name) {
185144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com    self->lastDumpName = dump_dir;
186144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com    self->lastDumpName += "/";
187144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com    self->lastDumpName += file_name;
188144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com    self->lastDumpName += ".dmp";
189144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com  }
190144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com  return true;
191144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com}
192144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com
1930d9bd40775211c223c75543890d862135f677d67ted.mielczarek@gmail.comTEST_F(ExceptionHandlerTest, WriteMinidump) {
194b12089f06d061d1ae393f208d37dc17e003d21c4qsr@chromium.org  ExceptionHandler eh(tempDir.path(), NULL, DumpNameMDCallback, this, true,
195b12089f06d061d1ae393f208d37dc17e003d21c4qsr@chromium.org                      NULL);
1960d9bd40775211c223c75543890d862135f677d67ted.mielczarek@gmail.com  ASSERT_TRUE(eh.WriteMinidump());
1970d9bd40775211c223c75543890d862135f677d67ted.mielczarek@gmail.com
1980d9bd40775211c223c75543890d862135f677d67ted.mielczarek@gmail.com  // Ensure that minidump file exists and is > 0 bytes.
1990d9bd40775211c223c75543890d862135f677d67ted.mielczarek@gmail.com  ASSERT_FALSE(lastDumpName.empty());
2000d9bd40775211c223c75543890d862135f677d67ted.mielczarek@gmail.com  struct stat st;
2010d9bd40775211c223c75543890d862135f677d67ted.mielczarek@gmail.com  ASSERT_EQ(0, stat(lastDumpName.c_str(), &st));
2020d9bd40775211c223c75543890d862135f677d67ted.mielczarek@gmail.com  ASSERT_LT(0, st.st_size);
2030d9bd40775211c223c75543890d862135f677d67ted.mielczarek@gmail.com
2040d9bd40775211c223c75543890d862135f677d67ted.mielczarek@gmail.com  // The minidump should not contain an exception stream.
2050d9bd40775211c223c75543890d862135f677d67ted.mielczarek@gmail.com  Minidump minidump(lastDumpName);
2060d9bd40775211c223c75543890d862135f677d67ted.mielczarek@gmail.com  ASSERT_TRUE(minidump.Read());
2070d9bd40775211c223c75543890d862135f677d67ted.mielczarek@gmail.com
2080d9bd40775211c223c75543890d862135f677d67ted.mielczarek@gmail.com  MinidumpException* exception = minidump.GetException();
2090d9bd40775211c223c75543890d862135f677d67ted.mielczarek@gmail.com  EXPECT_FALSE(exception);
2100d9bd40775211c223c75543890d862135f677d67ted.mielczarek@gmail.com}
2110d9bd40775211c223c75543890d862135f677d67ted.mielczarek@gmail.com
2120d9bd40775211c223c75543890d862135f677d67ted.mielczarek@gmail.comTEST_F(ExceptionHandlerTest, WriteMinidumpWithException) {
213b12089f06d061d1ae393f208d37dc17e003d21c4qsr@chromium.org  ExceptionHandler eh(tempDir.path(), NULL, DumpNameMDCallback, this, true,
214b12089f06d061d1ae393f208d37dc17e003d21c4qsr@chromium.org                      NULL);
2150d9bd40775211c223c75543890d862135f677d67ted.mielczarek@gmail.com  ASSERT_TRUE(eh.WriteMinidump(true));
2160d9bd40775211c223c75543890d862135f677d67ted.mielczarek@gmail.com
2170d9bd40775211c223c75543890d862135f677d67ted.mielczarek@gmail.com  // Ensure that minidump file exists and is > 0 bytes.
2180d9bd40775211c223c75543890d862135f677d67ted.mielczarek@gmail.com  ASSERT_FALSE(lastDumpName.empty());
2190d9bd40775211c223c75543890d862135f677d67ted.mielczarek@gmail.com  struct stat st;
2200d9bd40775211c223c75543890d862135f677d67ted.mielczarek@gmail.com  ASSERT_EQ(0, stat(lastDumpName.c_str(), &st));
2210d9bd40775211c223c75543890d862135f677d67ted.mielczarek@gmail.com  ASSERT_LT(0, st.st_size);
2220d9bd40775211c223c75543890d862135f677d67ted.mielczarek@gmail.com
2230d9bd40775211c223c75543890d862135f677d67ted.mielczarek@gmail.com  // The minidump should contain an exception stream.
2240d9bd40775211c223c75543890d862135f677d67ted.mielczarek@gmail.com  Minidump minidump(lastDumpName);
2250d9bd40775211c223c75543890d862135f677d67ted.mielczarek@gmail.com  ASSERT_TRUE(minidump.Read());
2260d9bd40775211c223c75543890d862135f677d67ted.mielczarek@gmail.com
2270d9bd40775211c223c75543890d862135f677d67ted.mielczarek@gmail.com  MinidumpException* exception = minidump.GetException();
2280d9bd40775211c223c75543890d862135f677d67ted.mielczarek@gmail.com  ASSERT_TRUE(exception);
2290d9bd40775211c223c75543890d862135f677d67ted.mielczarek@gmail.com  const MDRawExceptionStream* raw_exception = exception->exception();
2300d9bd40775211c223c75543890d862135f677d67ted.mielczarek@gmail.com  ASSERT_TRUE(raw_exception);
2310d9bd40775211c223c75543890d862135f677d67ted.mielczarek@gmail.com
2320d9bd40775211c223c75543890d862135f677d67ted.mielczarek@gmail.com  EXPECT_EQ(MD_EXCEPTION_MAC_BREAKPOINT,
2330d9bd40775211c223c75543890d862135f677d67ted.mielczarek@gmail.com            raw_exception->exception_record.exception_code);
2340d9bd40775211c223c75543890d862135f677d67ted.mielczarek@gmail.com}
2350d9bd40775211c223c75543890d862135f677d67ted.mielczarek@gmail.com
236144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.comTEST_F(ExceptionHandlerTest, DumpChildProcess) {
237144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com  const int kTimeoutMs = 2000;
238144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com  // Create a mach port to receive the child task on.
239144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com  char machPortName[128];
240144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com  sprintf(machPortName, "ExceptionHandlerTest.%d", getpid());
241144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com  ReceivePort parent_recv_port(machPortName);
242144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com
243144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com  // Give the child process a pipe to block on.
244144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com  int fds[2];
245144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com  ASSERT_EQ(0, pipe(fds));
246144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com
247144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com  // Fork off a child process to dump.
248144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com  pid_t pid = fork();
249144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com  if (pid == 0) {
250144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com    // In the child process
251efbe428d832769f6ddb09e2b9cdba796e196e6afted.mielczarek    close(fds[1]);
252144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com
253144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com    // Send parent process the task and thread ports.
254144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com    MachSendMessage child_message(0);
255144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com    child_message.AddDescriptor(mach_task_self());
256144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com    child_message.AddDescriptor(mach_thread_self());
257144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com
258144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com    MachPortSender child_sender(machPortName);
259144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com    if (child_sender.SendMessage(child_message, kTimeoutMs) != KERN_SUCCESS)
260144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com      exit(1);
261144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com
262144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com    // Wait for the parent process.
263144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com    uint8_t data;
264efbe428d832769f6ddb09e2b9cdba796e196e6afted.mielczarek    read(fds[0], &data, 1);
265144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com    exit(0);
266144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com  }
267144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com  // In the parent process.
268144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com  ASSERT_NE(-1, pid);
269efbe428d832769f6ddb09e2b9cdba796e196e6afted.mielczarek  close(fds[0]);
270144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com
271144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com  // Read the child's task and thread ports.
272144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com  MachReceiveMessage child_message;
273144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com  ASSERT_EQ(KERN_SUCCESS,
274144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com	    parent_recv_port.WaitForMessage(&child_message, kTimeoutMs));
275144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com  mach_port_t child_task = child_message.GetTranslatedPort(0);
276144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com  mach_port_t child_thread = child_message.GetTranslatedPort(1);
2774621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  ASSERT_NE((mach_port_t)MACH_PORT_NULL, child_task);
2784621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  ASSERT_NE((mach_port_t)MACH_PORT_NULL, child_thread);
279144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com
280144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com  // Write a minidump of the child process.
281144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com  bool result = ExceptionHandler::WriteMinidumpForChild(child_task,
282b12089f06d061d1ae393f208d37dc17e003d21c4qsr@chromium.org                                                        child_thread,
283b12089f06d061d1ae393f208d37dc17e003d21c4qsr@chromium.org                                                        tempDir.path(),
284b12089f06d061d1ae393f208d37dc17e003d21c4qsr@chromium.org                                                        DumpNameMDCallback,
285b12089f06d061d1ae393f208d37dc17e003d21c4qsr@chromium.org                                                        this);
286144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com  ASSERT_EQ(true, result);
287144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com
288144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com  // Ensure that minidump file exists and is > 0 bytes.
289144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com  ASSERT_FALSE(lastDumpName.empty());
290144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com  struct stat st;
291144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com  ASSERT_EQ(0, stat(lastDumpName.c_str(), &st));
292144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com  ASSERT_LT(0, st.st_size);
293144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com
294144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com  // Unblock child process
295144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com  uint8_t data = 1;
296efbe428d832769f6ddb09e2b9cdba796e196e6afted.mielczarek  (void)write(fds[1], &data, 1);
297144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com
298144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com  // Child process should have exited with a zero status.
299144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com  int ret;
300144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com  ASSERT_EQ(pid, waitpid(pid, &ret, 0));
301144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com  EXPECT_NE(0, WIFEXITED(ret));
302144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com  EXPECT_EQ(0, WEXITSTATUS(ret));
303144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com}
304144938cf22243407a56601bd5b75147f796a8424ted.mielczarek@gmail.com
3054621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek// Test that memory around the instruction pointer is written
3064621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek// to the dump as a MinidumpMemoryRegion.
3074621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarekTEST_F(ExceptionHandlerTest, InstructionPointerMemory) {
3084621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  // Give the child process a pipe to report back on.
3094621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  int fds[2];
3104621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  ASSERT_EQ(0, pipe(fds));
3114621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek
3124621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  // These are defined here so the parent can use them to check the
3134621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  // data from the minidump afterwards.
3146162aed3c3fcfc53373c963ac375d39a5dfa5a25ted.mielczarek@gmail.com  const uint32_t kMemorySize = 256;  // bytes
3154621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  const int kOffset = kMemorySize / 2;
3164621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  // This crashes with SIGILL on x86/x86-64/arm.
3174621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  const unsigned char instructions[] = { 0xff, 0xff, 0xff, 0xff };
3184621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek
3194621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  pid_t pid = fork();
3204621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  if (pid == 0) {
3214621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek    close(fds[0]);
322b12089f06d061d1ae393f208d37dc17e003d21c4qsr@chromium.org    ExceptionHandler eh(tempDir.path(), NULL, MDCallback, &fds[1], true, NULL);
3234621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek    // Get some executable memory.
3244621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek    char* memory =
3254621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek      reinterpret_cast<char*>(mmap(NULL,
3264621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek                                   kMemorySize,
3274621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek                                   PROT_READ | PROT_WRITE | PROT_EXEC,
3284621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek                                   MAP_PRIVATE | MAP_ANON,
3294621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek                                   -1,
3304621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek                                   0));
3314621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek    if (!memory)
3324621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek      exit(0);
3334621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek
3344621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek    // Write some instructions that will crash. Put them in the middle
3354621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek    // of the block of memory, because the minidump should contain 128
3364621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek    // bytes on either side of the instruction pointer.
3374621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek    memcpy(memory + kOffset, instructions, sizeof(instructions));
33830b075c4a5f9fabd3b66f3c607c12337f78a62b5ted.mielczarek
3394621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek    // Now execute the instructions, which should crash.
3404621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek    typedef void (*void_function)(void);
3414621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek    void_function memory_function =
3424621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek      reinterpret_cast<void_function>(memory + kOffset);
3434621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek    memory_function();
3444621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek    // not reached
3454621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek    exit(1);
3464621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  }
3474621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  // In the parent process.
3484621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  ASSERT_NE(-1, pid);
3494621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  close(fds[1]);
3504621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek
3514621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  // Wait for the background process to return the minidump file.
3524621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  close(fds[1]);
3534621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  char minidump_file[PATH_MAX];
3544621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  ssize_t nbytes = read(fds[0], minidump_file, sizeof(minidump_file));
3554621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  ASSERT_NE(0, nbytes);
3564621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  // Ensure that minidump file exists and is > 0 bytes.
3574621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  struct stat st;
3584621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  ASSERT_EQ(0, stat(minidump_file, &st));
3594621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  ASSERT_LT(0, st.st_size);
3604621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek
3614621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  // Child process should have exited with a zero status.
3624621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  int ret;
3634621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  ASSERT_EQ(pid, waitpid(pid, &ret, 0));
3644621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  EXPECT_NE(0, WIFEXITED(ret));
3654621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  EXPECT_EQ(0, WEXITSTATUS(ret));
3664621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek
3674621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  // Read the minidump. Locate the exception record and the
3684621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  // memory list, and then ensure that there is a memory region
3694621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  // in the memory list that covers the instruction pointer from
3704621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  // the exception record.
3714621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  Minidump minidump(minidump_file);
3724621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  ASSERT_TRUE(minidump.Read());
3734621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek
3744621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  MinidumpException* exception = minidump.GetException();
3754621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  MinidumpMemoryList* memory_list = minidump.GetMemoryList();
3764621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  ASSERT_TRUE(exception);
3774621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  ASSERT_TRUE(memory_list);
3784621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  ASSERT_NE((unsigned int)0, memory_list->region_count());
3794621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek
3804621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  MinidumpContext* context = exception->GetContext();
3814621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  ASSERT_TRUE(context);
3824621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek
3836162aed3c3fcfc53373c963ac375d39a5dfa5a25ted.mielczarek@gmail.com  uint64_t instruction_pointer;
384c551efe9b6129aa5a7911e3469630b52478057fcted.mielczarek@gmail.com  ASSERT_TRUE(context->GetInstructionPointer(&instruction_pointer));
3854621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek
3864621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  MinidumpMemoryRegion* region =
3874621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek    memory_list->GetMemoryRegionForAddress(instruction_pointer);
3884621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  EXPECT_TRUE(region);
3894621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek
3904621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  EXPECT_EQ(kMemorySize, region->GetSize());
3916162aed3c3fcfc53373c963ac375d39a5dfa5a25ted.mielczarek@gmail.com  const uint8_t* bytes = region->GetMemory();
3924621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  ASSERT_TRUE(bytes);
3934621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek
3946162aed3c3fcfc53373c963ac375d39a5dfa5a25ted.mielczarek@gmail.com  uint8_t prefix_bytes[kOffset];
3956162aed3c3fcfc53373c963ac375d39a5dfa5a25ted.mielczarek@gmail.com  uint8_t suffix_bytes[kMemorySize - kOffset - sizeof(instructions)];
3964621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  memset(prefix_bytes, 0, sizeof(prefix_bytes));
3974621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  memset(suffix_bytes, 0, sizeof(suffix_bytes));
3984621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  EXPECT_TRUE(memcmp(bytes, prefix_bytes, sizeof(prefix_bytes)) == 0);
3994621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  EXPECT_TRUE(memcmp(bytes + kOffset, instructions, sizeof(instructions)) == 0);
4004621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  EXPECT_TRUE(memcmp(bytes + kOffset + sizeof(instructions),
4014621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek                     suffix_bytes, sizeof(suffix_bytes)) == 0);
4024621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek}
4034621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek
4044621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek// Test that the memory region around the instruction pointer is
4054621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek// bounded correctly on the low end.
4064621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarekTEST_F(ExceptionHandlerTest, InstructionPointerMemoryMinBound) {
4074621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  // Give the child process a pipe to report back on.
4084621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  int fds[2];
4094621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  ASSERT_EQ(0, pipe(fds));
4104621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek
4114621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  // These are defined here so the parent can use them to check the
4124621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  // data from the minidump afterwards.
4136162aed3c3fcfc53373c963ac375d39a5dfa5a25ted.mielczarek@gmail.com  const uint32_t kMemorySize = 256;  // bytes
4144621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  const int kOffset = 0;
4154621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  // This crashes with SIGILL on x86/x86-64/arm.
4164621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  const unsigned char instructions[] = { 0xff, 0xff, 0xff, 0xff };
4174621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek
4184621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  pid_t pid = fork();
4194621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  if (pid == 0) {
4204621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek    close(fds[0]);
421b12089f06d061d1ae393f208d37dc17e003d21c4qsr@chromium.org    ExceptionHandler eh(tempDir.path(), NULL, MDCallback, &fds[1], true, NULL);
4224621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek    // Get some executable memory.
4234621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek    char* memory =
4244621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek      reinterpret_cast<char*>(mmap(NULL,
4254621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek                                   kMemorySize,
4264621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek                                   PROT_READ | PROT_WRITE | PROT_EXEC,
4274621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek                                   MAP_PRIVATE | MAP_ANON,
4284621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek                                   -1,
4294621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek                                   0));
4304621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek    if (!memory)
4314621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek      exit(0);
4324621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek
4334621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek    // Write some instructions that will crash. Put them at the start
4344621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek    // of the block of memory, to ensure that the memory bounding
4354621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek    // works properly.
4364621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek    memcpy(memory + kOffset, instructions, sizeof(instructions));
4374621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek
4384621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek    // Now execute the instructions, which should crash.
4394621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek    typedef void (*void_function)(void);
4404621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek    void_function memory_function =
4414621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek      reinterpret_cast<void_function>(memory + kOffset);
4424621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek    memory_function();
4434621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek    // not reached
4444621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek    exit(1);
4454621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  }
4464621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  // In the parent process.
4474621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  ASSERT_NE(-1, pid);
4484621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  close(fds[1]);
4494621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek
4504621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  // Wait for the background process to return the minidump file.
4514621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  close(fds[1]);
4524621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  char minidump_file[PATH_MAX];
4534621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  ssize_t nbytes = read(fds[0], minidump_file, sizeof(minidump_file));
4544621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  ASSERT_NE(0, nbytes);
4554621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  // Ensure that minidump file exists and is > 0 bytes.
4564621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  struct stat st;
4574621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  ASSERT_EQ(0, stat(minidump_file, &st));
4584621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  ASSERT_LT(0, st.st_size);
4594621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek
4604621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  // Child process should have exited with a zero status.
4614621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  int ret;
4624621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  ASSERT_EQ(pid, waitpid(pid, &ret, 0));
4634621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  EXPECT_NE(0, WIFEXITED(ret));
4644621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  EXPECT_EQ(0, WEXITSTATUS(ret));
4654621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek
4664621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  // Read the minidump. Locate the exception record and the
4674621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  // memory list, and then ensure that there is a memory region
4684621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  // in the memory list that covers the instruction pointer from
4694621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  // the exception record.
4704621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  Minidump minidump(minidump_file);
4714621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  ASSERT_TRUE(minidump.Read());
4724621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek
4734621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  MinidumpException* exception = minidump.GetException();
4744621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  MinidumpMemoryList* memory_list = minidump.GetMemoryList();
4754621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  ASSERT_TRUE(exception);
4764621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  ASSERT_TRUE(memory_list);
4774621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  ASSERT_NE((unsigned int)0, memory_list->region_count());
4784621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek
4794621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  MinidumpContext* context = exception->GetContext();
4804621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  ASSERT_TRUE(context);
4814621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek
4826162aed3c3fcfc53373c963ac375d39a5dfa5a25ted.mielczarek@gmail.com  uint64_t instruction_pointer;
483c551efe9b6129aa5a7911e3469630b52478057fcted.mielczarek@gmail.com  ASSERT_TRUE(context->GetInstructionPointer(&instruction_pointer));
4844621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek
4854621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  MinidumpMemoryRegion* region =
4864621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek    memory_list->GetMemoryRegionForAddress(instruction_pointer);
4874621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  EXPECT_TRUE(region);
4884621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek
4894621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  EXPECT_EQ(kMemorySize / 2, region->GetSize());
4906162aed3c3fcfc53373c963ac375d39a5dfa5a25ted.mielczarek@gmail.com  const uint8_t* bytes = region->GetMemory();
4914621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  ASSERT_TRUE(bytes);
4924621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek
4936162aed3c3fcfc53373c963ac375d39a5dfa5a25ted.mielczarek@gmail.com  uint8_t suffix_bytes[kMemorySize / 2 - sizeof(instructions)];
4944621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  memset(suffix_bytes, 0, sizeof(suffix_bytes));
4954621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  EXPECT_TRUE(memcmp(bytes + kOffset, instructions, sizeof(instructions)) == 0);
4964621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  EXPECT_TRUE(memcmp(bytes + kOffset + sizeof(instructions),
4974621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek                     suffix_bytes, sizeof(suffix_bytes)) == 0);
4984621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek}
4994621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek
5004621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek// Test that the memory region around the instruction pointer is
5014621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek// bounded correctly on the high end.
5024621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarekTEST_F(ExceptionHandlerTest, InstructionPointerMemoryMaxBound) {
5034621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  // Give the child process a pipe to report back on.
5044621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  int fds[2];
5054621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  ASSERT_EQ(0, pipe(fds));
5064621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek
5074621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  // These are defined here so the parent can use them to check the
5084621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  // data from the minidump afterwards.
5094621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  // Use 4k here because the OS will hand out a single page even
5104621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  // if a smaller size is requested, and this test wants to
5114621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  // test the upper bound of the memory range.
5126162aed3c3fcfc53373c963ac375d39a5dfa5a25ted.mielczarek@gmail.com  const uint32_t kMemorySize = 4096;  // bytes
5134621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  // This crashes with SIGILL on x86/x86-64/arm.
5144621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  const unsigned char instructions[] = { 0xff, 0xff, 0xff, 0xff };
5154621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  const int kOffset = kMemorySize - sizeof(instructions);
5164621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek
5174621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  pid_t pid = fork();
5184621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  if (pid == 0) {
5194621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek    close(fds[0]);
520b12089f06d061d1ae393f208d37dc17e003d21c4qsr@chromium.org    ExceptionHandler eh(tempDir.path(), NULL, MDCallback, &fds[1], true, NULL);
5214621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek    // Get some executable memory.
5224621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek    char* memory =
5234621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek      reinterpret_cast<char*>(mmap(NULL,
5244621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek                                   kMemorySize,
5254621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek                                   PROT_READ | PROT_WRITE | PROT_EXEC,
5264621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek                                   MAP_PRIVATE | MAP_ANON,
5274621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek                                   -1,
5284621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek                                   0));
5294621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek    if (!memory)
5304621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek      exit(0);
5314621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek
5324621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek    // Write some instructions that will crash. Put them at the start
5334621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek    // of the block of memory, to ensure that the memory bounding
5344621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek    // works properly.
5354621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek    memcpy(memory + kOffset, instructions, sizeof(instructions));
5364621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek
5374621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek    // Now execute the instructions, which should crash.
5384621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek    typedef void (*void_function)(void);
5394621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek    void_function memory_function =
5404621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek      reinterpret_cast<void_function>(memory + kOffset);
5414621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek    memory_function();
5424621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek    // not reached
5434621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek    exit(1);
5444621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  }
5454621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  // In the parent process.
5464621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  ASSERT_NE(-1, pid);
5474621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  close(fds[1]);
5484621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek
5494621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  // Wait for the background process to return the minidump file.
5504621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  close(fds[1]);
5514621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  char minidump_file[PATH_MAX];
5524621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  ssize_t nbytes = read(fds[0], minidump_file, sizeof(minidump_file));
5534621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  ASSERT_NE(0, nbytes);
5544621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  // Ensure that minidump file exists and is > 0 bytes.
5554621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  struct stat st;
5564621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  ASSERT_EQ(0, stat(minidump_file, &st));
5574621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  ASSERT_LT(0, st.st_size);
5584621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek
5594621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  // Child process should have exited with a zero status.
5604621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  int ret;
5614621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  ASSERT_EQ(pid, waitpid(pid, &ret, 0));
5624621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  EXPECT_NE(0, WIFEXITED(ret));
5634621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  EXPECT_EQ(0, WEXITSTATUS(ret));
5644621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek
5654621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  // Read the minidump. Locate the exception record and the
5664621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  // memory list, and then ensure that there is a memory region
5674621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  // in the memory list that covers the instruction pointer from
5684621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  // the exception record.
5694621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  Minidump minidump(minidump_file);
5704621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  ASSERT_TRUE(minidump.Read());
5714621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek
5724621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  MinidumpException* exception = minidump.GetException();
5734621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  MinidumpMemoryList* memory_list = minidump.GetMemoryList();
5744621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  ASSERT_TRUE(exception);
5754621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  ASSERT_TRUE(memory_list);
5764621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  ASSERT_NE((unsigned int)0, memory_list->region_count());
5774621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek
5784621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  MinidumpContext* context = exception->GetContext();
5794621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  ASSERT_TRUE(context);
5804621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek
5816162aed3c3fcfc53373c963ac375d39a5dfa5a25ted.mielczarek@gmail.com  uint64_t instruction_pointer;
582c551efe9b6129aa5a7911e3469630b52478057fcted.mielczarek@gmail.com  ASSERT_TRUE(context->GetInstructionPointer(&instruction_pointer));
5834621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek
5844621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  MinidumpMemoryRegion* region =
5854621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek    memory_list->GetMemoryRegionForAddress(instruction_pointer);
5864621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  EXPECT_TRUE(region);
5874621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek
5884621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  const size_t kPrefixSize = 128;  // bytes
5894621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  EXPECT_EQ(kPrefixSize + sizeof(instructions), region->GetSize());
5906162aed3c3fcfc53373c963ac375d39a5dfa5a25ted.mielczarek@gmail.com  const uint8_t* bytes = region->GetMemory();
5914621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  ASSERT_TRUE(bytes);
5924621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek
5936162aed3c3fcfc53373c963ac375d39a5dfa5a25ted.mielczarek@gmail.com  uint8_t prefix_bytes[kPrefixSize];
5944621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  memset(prefix_bytes, 0, sizeof(prefix_bytes));
5954621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  EXPECT_TRUE(memcmp(bytes, prefix_bytes, sizeof(prefix_bytes)) == 0);
5964621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  EXPECT_TRUE(memcmp(bytes + kPrefixSize,
5974621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek                     instructions, sizeof(instructions)) == 0);
5984621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek}
5994621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek
6004621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek// Ensure that an extra memory block doesn't get added when the
6014621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek// instruction pointer is not in mapped memory.
6024621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarekTEST_F(ExceptionHandlerTest, InstructionPointerMemoryNullPointer) {
6034621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  // Give the child process a pipe to report back on.
6044621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  int fds[2];
6054621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  ASSERT_EQ(0, pipe(fds));
6064621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek
6074621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  pid_t pid = fork();
6084621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  if (pid == 0) {
6094621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek    close(fds[0]);
610b12089f06d061d1ae393f208d37dc17e003d21c4qsr@chromium.org    ExceptionHandler eh(tempDir.path(), NULL, MDCallback, &fds[1], true, NULL);
6114621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek    // Try calling a NULL pointer.
6124621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek    typedef void (*void_function)(void);
6134621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek    void_function memory_function =
6144621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek      reinterpret_cast<void_function>(NULL);
6154621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek    memory_function();
6164621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek    // not reached
6174621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek    exit(1);
6184621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  }
6194621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  // In the parent process.
6204621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  ASSERT_NE(-1, pid);
6214621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  close(fds[1]);
6224621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek
6234621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  // Wait for the background process to return the minidump file.
6244621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  close(fds[1]);
6254621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  char minidump_file[PATH_MAX];
6264621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  ssize_t nbytes = read(fds[0], minidump_file, sizeof(minidump_file));
6274621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  ASSERT_NE(0, nbytes);
6284621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  // Ensure that minidump file exists and is > 0 bytes.
6294621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  struct stat st;
6304621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  ASSERT_EQ(0, stat(minidump_file, &st));
6314621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  ASSERT_LT(0, st.st_size);
6324621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek
6334621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  // Child process should have exited with a zero status.
6344621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  int ret;
6354621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  ASSERT_EQ(pid, waitpid(pid, &ret, 0));
6364621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  EXPECT_NE(0, WIFEXITED(ret));
6374621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  EXPECT_EQ(0, WEXITSTATUS(ret));
6384621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek
6394621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  // Read the minidump. Locate the exception record and the
6404621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  // memory list, and then ensure that there is only one memory region
6414621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  // in the memory list (the thread memory from the single thread).
6424621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  Minidump minidump(minidump_file);
6434621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  ASSERT_TRUE(minidump.Read());
6444621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek
6454621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  MinidumpException* exception = minidump.GetException();
6464621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  MinidumpMemoryList* memory_list = minidump.GetMemoryList();
6474621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  ASSERT_TRUE(exception);
6484621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  ASSERT_TRUE(memory_list);
6494621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek  ASSERT_EQ((unsigned int)1, memory_list->region_count());
6504621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek}
6514621ee06914b2ebe963c93ea78fabf982cf670dfted.mielczarek
65230b075c4a5f9fabd3b66f3c607c12337f78a62b5ted.mielczarekstatic void *Junk(void *) {
65330b075c4a5f9fabd3b66f3c607c12337f78a62b5ted.mielczarek  sleep(1000000);
65430b075c4a5f9fabd3b66f3c607c12337f78a62b5ted.mielczarek  return NULL;
65530b075c4a5f9fabd3b66f3c607c12337f78a62b5ted.mielczarek}
65630b075c4a5f9fabd3b66f3c607c12337f78a62b5ted.mielczarek
65730b075c4a5f9fabd3b66f3c607c12337f78a62b5ted.mielczarek// Test that the memory list gets written correctly when multiple
65830b075c4a5f9fabd3b66f3c607c12337f78a62b5ted.mielczarek// threads are running.
65930b075c4a5f9fabd3b66f3c607c12337f78a62b5ted.mielczarekTEST_F(ExceptionHandlerTest, MemoryListMultipleThreads) {
66030b075c4a5f9fabd3b66f3c607c12337f78a62b5ted.mielczarek  // Give the child process a pipe to report back on.
66130b075c4a5f9fabd3b66f3c607c12337f78a62b5ted.mielczarek  int fds[2];
66230b075c4a5f9fabd3b66f3c607c12337f78a62b5ted.mielczarek  ASSERT_EQ(0, pipe(fds));
66330b075c4a5f9fabd3b66f3c607c12337f78a62b5ted.mielczarek
66430b075c4a5f9fabd3b66f3c607c12337f78a62b5ted.mielczarek  pid_t pid = fork();
66530b075c4a5f9fabd3b66f3c607c12337f78a62b5ted.mielczarek  if (pid == 0) {
66630b075c4a5f9fabd3b66f3c607c12337f78a62b5ted.mielczarek    close(fds[0]);
667b12089f06d061d1ae393f208d37dc17e003d21c4qsr@chromium.org    ExceptionHandler eh(tempDir.path(), NULL, MDCallback, &fds[1], true, NULL);
66830b075c4a5f9fabd3b66f3c607c12337f78a62b5ted.mielczarek
66930b075c4a5f9fabd3b66f3c607c12337f78a62b5ted.mielczarek    // Run an extra thread so >2 memory regions will be written.
67030b075c4a5f9fabd3b66f3c607c12337f78a62b5ted.mielczarek    pthread_t junk_thread;
67130b075c4a5f9fabd3b66f3c607c12337f78a62b5ted.mielczarek    if (pthread_create(&junk_thread, NULL, Junk, NULL) == 0)
67230b075c4a5f9fabd3b66f3c607c12337f78a62b5ted.mielczarek      pthread_detach(junk_thread);
67330b075c4a5f9fabd3b66f3c607c12337f78a62b5ted.mielczarek
67430b075c4a5f9fabd3b66f3c607c12337f78a62b5ted.mielczarek    // Just crash.
67530b075c4a5f9fabd3b66f3c607c12337f78a62b5ted.mielczarek    Crasher();
67630b075c4a5f9fabd3b66f3c607c12337f78a62b5ted.mielczarek
67730b075c4a5f9fabd3b66f3c607c12337f78a62b5ted.mielczarek    // not reached
67830b075c4a5f9fabd3b66f3c607c12337f78a62b5ted.mielczarek    exit(1);
67930b075c4a5f9fabd3b66f3c607c12337f78a62b5ted.mielczarek  }
68030b075c4a5f9fabd3b66f3c607c12337f78a62b5ted.mielczarek  // In the parent process.
68130b075c4a5f9fabd3b66f3c607c12337f78a62b5ted.mielczarek  ASSERT_NE(-1, pid);
68230b075c4a5f9fabd3b66f3c607c12337f78a62b5ted.mielczarek  close(fds[1]);
68330b075c4a5f9fabd3b66f3c607c12337f78a62b5ted.mielczarek
68430b075c4a5f9fabd3b66f3c607c12337f78a62b5ted.mielczarek  // Wait for the background process to return the minidump file.
68530b075c4a5f9fabd3b66f3c607c12337f78a62b5ted.mielczarek  close(fds[1]);
68630b075c4a5f9fabd3b66f3c607c12337f78a62b5ted.mielczarek  char minidump_file[PATH_MAX];
68730b075c4a5f9fabd3b66f3c607c12337f78a62b5ted.mielczarek  ssize_t nbytes = read(fds[0], minidump_file, sizeof(minidump_file));
68830b075c4a5f9fabd3b66f3c607c12337f78a62b5ted.mielczarek  ASSERT_NE(0, nbytes);
68930b075c4a5f9fabd3b66f3c607c12337f78a62b5ted.mielczarek  // Ensure that minidump file exists and is > 0 bytes.
69030b075c4a5f9fabd3b66f3c607c12337f78a62b5ted.mielczarek  struct stat st;
69130b075c4a5f9fabd3b66f3c607c12337f78a62b5ted.mielczarek  ASSERT_EQ(0, stat(minidump_file, &st));
69230b075c4a5f9fabd3b66f3c607c12337f78a62b5ted.mielczarek  ASSERT_LT(0, st.st_size);
69330b075c4a5f9fabd3b66f3c607c12337f78a62b5ted.mielczarek
69430b075c4a5f9fabd3b66f3c607c12337f78a62b5ted.mielczarek  // Child process should have exited with a zero status.
69530b075c4a5f9fabd3b66f3c607c12337f78a62b5ted.mielczarek  int ret;
69630b075c4a5f9fabd3b66f3c607c12337f78a62b5ted.mielczarek  ASSERT_EQ(pid, waitpid(pid, &ret, 0));
69730b075c4a5f9fabd3b66f3c607c12337f78a62b5ted.mielczarek  EXPECT_NE(0, WIFEXITED(ret));
69830b075c4a5f9fabd3b66f3c607c12337f78a62b5ted.mielczarek  EXPECT_EQ(0, WEXITSTATUS(ret));
69930b075c4a5f9fabd3b66f3c607c12337f78a62b5ted.mielczarek
70030b075c4a5f9fabd3b66f3c607c12337f78a62b5ted.mielczarek  // Read the minidump, and verify that the memory list can be read.
70130b075c4a5f9fabd3b66f3c607c12337f78a62b5ted.mielczarek  Minidump minidump(minidump_file);
70230b075c4a5f9fabd3b66f3c607c12337f78a62b5ted.mielczarek  ASSERT_TRUE(minidump.Read());
70330b075c4a5f9fabd3b66f3c607c12337f78a62b5ted.mielczarek
70430b075c4a5f9fabd3b66f3c607c12337f78a62b5ted.mielczarek  MinidumpMemoryList* memory_list = minidump.GetMemoryList();
70530b075c4a5f9fabd3b66f3c607c12337f78a62b5ted.mielczarek  ASSERT_TRUE(memory_list);
70630b075c4a5f9fabd3b66f3c607c12337f78a62b5ted.mielczarek  // Verify that there are three memory regions:
70730b075c4a5f9fabd3b66f3c607c12337f78a62b5ted.mielczarek  // one per thread, and one for the instruction pointer memory.
70830b075c4a5f9fabd3b66f3c607c12337f78a62b5ted.mielczarek  ASSERT_EQ((unsigned int)3, memory_list->region_count());
70930b075c4a5f9fabd3b66f3c607c12337f78a62b5ted.mielczarek}
71030b075c4a5f9fabd3b66f3c607c12337f78a62b5ted.mielczarek
711315fd78199bc606ee02cb085dacadd58e0fc40c8ted.mielczarek@gmail.com}
712