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