Home | History | Annotate | Download | only in minidump_writer
      1 // Copyright (c) 2011 Google Inc.
      2 // All rights reserved.
      3 //
      4 // Redistribution and use in source and binary forms, with or without
      5 // modification, are permitted provided that the following conditions are
      6 // met:
      7 //
      8 //     * Redistributions of source code must retain the above copyright
      9 // notice, this list of conditions and the following disclaimer.
     10 //     * Redistributions in binary form must reproduce the above
     11 // copyright notice, this list of conditions and the following disclaimer
     12 // in the documentation and/or other materials provided with the
     13 // distribution.
     14 //     * Neither the name of Google Inc. nor the names of its
     15 // contributors may be used to endorse or promote products derived from
     16 // this software without specific prior written permission.
     17 //
     18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29 
     30 #include <fcntl.h>
     31 #include <sys/poll.h>
     32 #include <sys/stat.h>
     33 #include <sys/syscall.h>
     34 #include <sys/types.h>
     35 #include <ucontext.h>
     36 #include <unistd.h>
     37 
     38 #include <string>
     39 
     40 #include "breakpad_googletest_includes.h"
     41 #include "client/linux/handler/exception_handler.h"
     42 #include "client/linux/minidump_writer/linux_dumper.h"
     43 #include "client/linux/minidump_writer/minidump_writer.h"
     44 #include "client/linux/minidump_writer/minidump_writer_unittest_utils.h"
     45 #include "common/linux/eintr_wrapper.h"
     46 #include "common/linux/file_id.h"
     47 #include "common/linux/ignore_ret.h"
     48 #include "common/linux/safe_readlink.h"
     49 #include "common/scoped_ptr.h"
     50 #include "common/tests/auto_tempdir.h"
     51 #include "common/tests/file_utils.h"
     52 #include "common/using_std_string.h"
     53 #include "google_breakpad/processor/minidump.h"
     54 
     55 using namespace google_breakpad;
     56 
     57 // Length of a formatted GUID string =
     58 // sizeof(MDGUID) * 2 + 4 (for dashes) + 1 (null terminator)
     59 const int kGUIDStringSize = 37;
     60 
     61 namespace {
     62 
     63 typedef testing::Test MinidumpWriterTest;
     64 
     65 const char kMDWriterUnitTestFileName[] = "/minidump-writer-unittest";
     66 
     67 TEST(MinidumpWriterTest, SetupWithPath) {
     68   int fds[2];
     69   ASSERT_NE(-1, pipe(fds));
     70 
     71   const pid_t child = fork();
     72   if (child == 0) {
     73     close(fds[1]);
     74     char b;
     75     IGNORE_RET(HANDLE_EINTR(read(fds[0], &b, sizeof(b))));
     76     close(fds[0]);
     77     syscall(__NR_exit);
     78   }
     79   close(fds[0]);
     80 
     81   ExceptionHandler::CrashContext context;
     82   memset(&context, 0, sizeof(context));
     83 
     84   AutoTempDir temp_dir;
     85   string templ = temp_dir.path() + kMDWriterUnitTestFileName;
     86   // Set a non-zero tid to avoid tripping asserts.
     87   context.tid = child;
     88   ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context)));
     89   struct stat st;
     90   ASSERT_EQ(0, stat(templ.c_str(), &st));
     91   ASSERT_GT(st.st_size, 0);
     92 
     93   close(fds[1]);
     94 }
     95 
     96 TEST(MinidumpWriterTest, SetupWithFD) {
     97   int fds[2];
     98   ASSERT_NE(-1, pipe(fds));
     99 
    100   const pid_t child = fork();
    101   if (child == 0) {
    102     close(fds[1]);
    103     char b;
    104     HANDLE_EINTR(read(fds[0], &b, sizeof(b)));
    105     close(fds[0]);
    106     syscall(__NR_exit);
    107   }
    108   close(fds[0]);
    109 
    110   ExceptionHandler::CrashContext context;
    111   memset(&context, 0, sizeof(context));
    112 
    113   AutoTempDir temp_dir;
    114   string templ = temp_dir.path() + kMDWriterUnitTestFileName;
    115   int fd = open(templ.c_str(), O_CREAT | O_WRONLY, S_IRWXU);
    116   // Set a non-zero tid to avoid tripping asserts.
    117   context.tid = child;
    118   ASSERT_TRUE(WriteMinidump(fd, child, &context, sizeof(context)));
    119   struct stat st;
    120   ASSERT_EQ(0, stat(templ.c_str(), &st));
    121   ASSERT_GT(st.st_size, 0);
    122 
    123   close(fds[1]);
    124 }
    125 
    126 // Test that mapping info can be specified when writing a minidump,
    127 // and that it ends up in the module list of the minidump.
    128 TEST(MinidumpWriterTest, MappingInfo) {
    129   int fds[2];
    130   ASSERT_NE(-1, pipe(fds));
    131 
    132   // These are defined here so the parent can use them to check the
    133   // data from the minidump afterwards.
    134   const uint32_t memory_size = sysconf(_SC_PAGESIZE);
    135   const char* kMemoryName = "a fake module";
    136   const uint8_t kModuleGUID[sizeof(MDGUID)] = {
    137     0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
    138     0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF
    139   };
    140   char module_identifier_buffer[kGUIDStringSize];
    141   FileID::ConvertIdentifierToString(kModuleGUID,
    142                                     module_identifier_buffer,
    143                                     sizeof(module_identifier_buffer));
    144   string module_identifier(module_identifier_buffer);
    145   // Strip out dashes
    146   size_t pos;
    147   while ((pos = module_identifier.find('-')) != string::npos) {
    148     module_identifier.erase(pos, 1);
    149   }
    150   // And append a zero, because module IDs include an "age" field
    151   // which is always zero on Linux.
    152   module_identifier += "0";
    153 
    154   // Get some memory.
    155   char* memory =
    156     reinterpret_cast<char*>(mmap(NULL,
    157                                  memory_size,
    158                                  PROT_READ | PROT_WRITE,
    159                                  MAP_PRIVATE | MAP_ANON,
    160                                  -1,
    161                                  0));
    162   const uintptr_t kMemoryAddress = reinterpret_cast<uintptr_t>(memory);
    163   ASSERT_TRUE(memory);
    164 
    165   const pid_t child = fork();
    166   if (child == 0) {
    167     close(fds[1]);
    168     char b;
    169     IGNORE_RET(HANDLE_EINTR(read(fds[0], &b, sizeof(b))));
    170     close(fds[0]);
    171     syscall(__NR_exit);
    172   }
    173   close(fds[0]);
    174 
    175   ExceptionHandler::CrashContext context;
    176   memset(&context, 0, sizeof(context));
    177   ASSERT_EQ(0, getcontext(&context.context));
    178   context.tid = child;
    179 
    180   AutoTempDir temp_dir;
    181   string templ = temp_dir.path() + kMDWriterUnitTestFileName;
    182 
    183   // Add information about the mapped memory.
    184   MappingInfo info;
    185   info.start_addr = kMemoryAddress;
    186   info.size = memory_size;
    187   info.offset = 0;
    188   strcpy(info.name, kMemoryName);
    189 
    190   MappingList mappings;
    191   AppMemoryList memory_list;
    192   MappingEntry mapping;
    193   mapping.first = info;
    194   memcpy(mapping.second, kModuleGUID, sizeof(MDGUID));
    195   mappings.push_back(mapping);
    196   ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context),
    197                             mappings, memory_list));
    198 
    199   // Read the minidump. Load the module list, and ensure that
    200   // the mmap'ed |memory| is listed with the given module name
    201   // and debug ID.
    202   Minidump minidump(templ);
    203   ASSERT_TRUE(minidump.Read());
    204 
    205   MinidumpModuleList* module_list = minidump.GetModuleList();
    206   ASSERT_TRUE(module_list);
    207   const MinidumpModule* module =
    208     module_list->GetModuleForAddress(kMemoryAddress);
    209   ASSERT_TRUE(module);
    210 
    211   EXPECT_EQ(kMemoryAddress, module->base_address());
    212   EXPECT_EQ(memory_size, module->size());
    213   EXPECT_EQ(kMemoryName, module->code_file());
    214   EXPECT_EQ(module_identifier, module->debug_identifier());
    215 
    216   uint32_t len;
    217   // These streams are expected to be there
    218   EXPECT_TRUE(minidump.SeekToStreamType(MD_THREAD_LIST_STREAM, &len));
    219   EXPECT_TRUE(minidump.SeekToStreamType(MD_MEMORY_LIST_STREAM, &len));
    220   EXPECT_TRUE(minidump.SeekToStreamType(MD_EXCEPTION_STREAM, &len));
    221   EXPECT_TRUE(minidump.SeekToStreamType(MD_SYSTEM_INFO_STREAM, &len));
    222   EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_CPU_INFO, &len));
    223   EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_PROC_STATUS, &len));
    224   EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_CMD_LINE, &len));
    225   EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_ENVIRON, &len));
    226   EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_AUXV, &len));
    227   EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_MAPS, &len));
    228   EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_DSO_DEBUG, &len));
    229 
    230   close(fds[1]);
    231 }
    232 
    233 // Test that mapping info can be specified, and that it overrides
    234 // existing mappings that are wholly contained within the specified
    235 // range.
    236 #ifdef ENABLE_FLAKY_TESTS
    237 TEST(MinidumpWriterTest, MappingInfoContained) {
    238   int fds[2];
    239   ASSERT_NE(-1, pipe(fds));
    240 
    241   // These are defined here so the parent can use them to check the
    242   // data from the minidump afterwards.
    243   const int32_t memory_size = sysconf(_SC_PAGESIZE);
    244   const char* kMemoryName = "a fake module";
    245   const uint8_t kModuleGUID[sizeof(MDGUID)] = {
    246     0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
    247     0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF
    248   };
    249   char module_identifier_buffer[kGUIDStringSize];
    250   FileID::ConvertIdentifierToString(kModuleGUID,
    251                                     module_identifier_buffer,
    252                                     sizeof(module_identifier_buffer));
    253   string module_identifier(module_identifier_buffer);
    254   // Strip out dashes
    255   size_t pos;
    256   while ((pos = module_identifier.find('-')) != string::npos) {
    257     module_identifier.erase(pos, 1);
    258   }
    259   // And append a zero, because module IDs include an "age" field
    260   // which is always zero on Linux.
    261   module_identifier += "0";
    262 
    263   // mmap a file
    264   AutoTempDir temp_dir;
    265   string tempfile = temp_dir.path() + "/minidump-writer-unittest-temp";
    266   int fd = open(tempfile.c_str(), O_RDWR | O_CREAT, 0);
    267   ASSERT_NE(-1, fd);
    268   unlink(tempfile.c_str());
    269   // fill with zeros
    270   google_breakpad::scoped_array<char> buffer(new char[memory_size]);
    271   memset(buffer.get(), 0, memory_size);
    272   ASSERT_EQ(memory_size, write(fd, buffer.get(), memory_size));
    273   lseek(fd, 0, SEEK_SET);
    274 
    275   char* memory =
    276     reinterpret_cast<char*>(mmap(NULL,
    277                                  memory_size,
    278                                  PROT_READ | PROT_WRITE,
    279                                  MAP_PRIVATE,
    280                                  fd,
    281                                  0));
    282   const uintptr_t kMemoryAddress = reinterpret_cast<uintptr_t>(memory);
    283   ASSERT_TRUE(memory);
    284   close(fd);
    285 
    286   const pid_t child = fork();
    287   if (child == 0) {
    288     close(fds[1]);
    289     char b;
    290     IGNORE_RET(HANDLE_EINTR(read(fds[0], &b, sizeof(b))));
    291     close(fds[0]);
    292     syscall(__NR_exit);
    293   }
    294   close(fds[0]);
    295 
    296   ExceptionHandler::CrashContext context;
    297   memset(&context, 0, sizeof(context));
    298   context.tid = 1;
    299 
    300   string dumpfile = temp_dir.path() + kMDWriterUnitTestFileName;
    301 
    302   // Add information about the mapped memory. Report it as being larger than
    303   // it actually is.
    304   MappingInfo info;
    305   info.start_addr = kMemoryAddress - memory_size;
    306   info.size = memory_size * 3;
    307   info.offset = 0;
    308   strcpy(info.name, kMemoryName);
    309 
    310   MappingList mappings;
    311   AppMemoryList memory_list;
    312   MappingEntry mapping;
    313   mapping.first = info;
    314   memcpy(mapping.second, kModuleGUID, sizeof(MDGUID));
    315   mappings.push_back(mapping);
    316   ASSERT_TRUE(WriteMinidump(dumpfile.c_str(), child, &context, sizeof(context),
    317                             mappings, memory_list));
    318 
    319   // Read the minidump. Load the module list, and ensure that
    320   // the mmap'ed |memory| is listed with the given module name
    321   // and debug ID.
    322   Minidump minidump(dumpfile);
    323   ASSERT_TRUE(minidump.Read());
    324 
    325   MinidumpModuleList* module_list = minidump.GetModuleList();
    326   ASSERT_TRUE(module_list);
    327   const MinidumpModule* module =
    328     module_list->GetModuleForAddress(kMemoryAddress);
    329   ASSERT_TRUE(module);
    330 
    331   EXPECT_EQ(info.start_addr, module->base_address());
    332   EXPECT_EQ(info.size, module->size());
    333   EXPECT_EQ(kMemoryName, module->code_file());
    334   EXPECT_EQ(module_identifier, module->debug_identifier());
    335 
    336   close(fds[1]);
    337 }
    338 #endif
    339 
    340 TEST(MinidumpWriterTest, DeletedBinary) {
    341   const string kNumberOfThreadsArgument = "1";
    342   const string helper_path(GetHelperBinary());
    343   if (helper_path.empty()) {
    344     FAIL() << "Couldn't find helper binary";
    345     exit(1);
    346   }
    347 
    348   // Copy binary to a temp file.
    349   AutoTempDir temp_dir;
    350   string binpath = temp_dir.path() + "/linux-dumper-unittest-helper";
    351   ASSERT_TRUE(CopyFile(helper_path.c_str(), binpath.c_str()))
    352       << "Failed to copy " << helper_path << " to " << binpath;
    353   ASSERT_EQ(0, chmod(binpath.c_str(), 0755));
    354 
    355   int fds[2];
    356   ASSERT_NE(-1, pipe(fds));
    357 
    358   pid_t child_pid = fork();
    359   if (child_pid == 0) {
    360     // In child process.
    361     close(fds[0]);
    362 
    363     // Pass the pipe fd and the number of threads as arguments.
    364     char pipe_fd_string[8];
    365     sprintf(pipe_fd_string, "%d", fds[1]);
    366     execl(binpath.c_str(),
    367           binpath.c_str(),
    368           pipe_fd_string,
    369           kNumberOfThreadsArgument.c_str(),
    370           NULL);
    371   }
    372   close(fds[1]);
    373   // Wait for the child process to signal that it's ready.
    374   struct pollfd pfd;
    375   memset(&pfd, 0, sizeof(pfd));
    376   pfd.fd = fds[0];
    377   pfd.events = POLLIN | POLLERR;
    378 
    379   const int r = HANDLE_EINTR(poll(&pfd, 1, 1000));
    380   ASSERT_EQ(1, r);
    381   ASSERT_TRUE(pfd.revents & POLLIN);
    382   uint8_t junk;
    383   const int nr = HANDLE_EINTR(read(fds[0], &junk, sizeof(junk)));
    384   ASSERT_EQ(static_cast<ssize_t>(sizeof(junk)), nr);
    385   close(fds[0]);
    386 
    387   // Child is ready now.
    388   // Unlink the test binary.
    389   unlink(binpath.c_str());
    390 
    391   ExceptionHandler::CrashContext context;
    392   memset(&context, 0, sizeof(context));
    393 
    394   string templ = temp_dir.path() + kMDWriterUnitTestFileName;
    395   // Set a non-zero tid to avoid tripping asserts.
    396   context.tid = child_pid;
    397   ASSERT_TRUE(WriteMinidump(templ.c_str(), child_pid, &context,
    398                             sizeof(context)));
    399   kill(child_pid, SIGKILL);
    400 
    401   struct stat st;
    402   ASSERT_EQ(0, stat(templ.c_str(), &st));
    403   ASSERT_GT(st.st_size, 0);
    404 
    405   Minidump minidump(templ);
    406   ASSERT_TRUE(minidump.Read());
    407 
    408   // Check that the main module filename is correct.
    409   MinidumpModuleList* module_list = minidump.GetModuleList();
    410   ASSERT_TRUE(module_list);
    411   const MinidumpModule* module = module_list->GetMainModule();
    412   EXPECT_STREQ(binpath.c_str(), module->code_file().c_str());
    413   // Check that the file ID is correct.
    414   FileID fileid(helper_path.c_str());
    415   uint8_t identifier[sizeof(MDGUID)];
    416   EXPECT_TRUE(fileid.ElfFileIdentifier(identifier));
    417   char identifier_string[kGUIDStringSize];
    418   FileID::ConvertIdentifierToString(identifier,
    419                                     identifier_string,
    420                                     kGUIDStringSize);
    421   string module_identifier(identifier_string);
    422   // Strip out dashes
    423   size_t pos;
    424   while ((pos = module_identifier.find('-')) != string::npos) {
    425     module_identifier.erase(pos, 1);
    426   }
    427   // And append a zero, because module IDs include an "age" field
    428   // which is always zero on Linux.
    429   module_identifier += "0";
    430   EXPECT_EQ(module_identifier, module->debug_identifier());
    431 }
    432 
    433 // Test that an additional memory region can be added to the minidump.
    434 TEST(MinidumpWriterTest, AdditionalMemory) {
    435   int fds[2];
    436   ASSERT_NE(-1, pipe(fds));
    437 
    438   // These are defined here so the parent can use them to check the
    439   // data from the minidump afterwards.
    440   const uint32_t kMemorySize = sysconf(_SC_PAGESIZE);
    441 
    442   // Get some heap memory.
    443   uint8_t* memory = new uint8_t[kMemorySize];
    444   const uintptr_t kMemoryAddress = reinterpret_cast<uintptr_t>(memory);
    445   ASSERT_TRUE(memory);
    446 
    447   // Stick some data into the memory so the contents can be verified.
    448   for (uint32_t i = 0; i < kMemorySize; ++i) {
    449     memory[i] = i % 255;
    450   }
    451 
    452   const pid_t child = fork();
    453   if (child == 0) {
    454     close(fds[1]);
    455     char b;
    456     HANDLE_EINTR(read(fds[0], &b, sizeof(b)));
    457     close(fds[0]);
    458     syscall(__NR_exit);
    459   }
    460   close(fds[0]);
    461 
    462   ExceptionHandler::CrashContext context;
    463 
    464   // This needs a valid context for minidump writing to work, but getting
    465   // a useful one from the child is too much work, so just use one from
    466   // the parent since the child is just a forked copy anyway.
    467   ASSERT_EQ(0, getcontext(&context.context));
    468   context.tid = child;
    469 
    470   AutoTempDir temp_dir;
    471   string templ = temp_dir.path() + kMDWriterUnitTestFileName;
    472   unlink(templ.c_str());
    473 
    474   MappingList mappings;
    475   AppMemoryList memory_list;
    476 
    477   // Add the memory region to the list of memory to be included.
    478   AppMemory app_memory;
    479   app_memory.ptr = memory;
    480   app_memory.length = kMemorySize;
    481   memory_list.push_back(app_memory);
    482   ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context),
    483                             mappings, memory_list));
    484 
    485   // Read the minidump. Ensure that the memory region is present
    486   Minidump minidump(templ);
    487   ASSERT_TRUE(minidump.Read());
    488 
    489   MinidumpMemoryList* dump_memory_list = minidump.GetMemoryList();
    490   ASSERT_TRUE(dump_memory_list);
    491   const MinidumpMemoryRegion* region =
    492     dump_memory_list->GetMemoryRegionForAddress(kMemoryAddress);
    493   ASSERT_TRUE(region);
    494 
    495   EXPECT_EQ(kMemoryAddress, region->GetBase());
    496   EXPECT_EQ(kMemorySize, region->GetSize());
    497 
    498   // Verify memory contents.
    499   EXPECT_EQ(0, memcmp(region->GetMemory(), memory, kMemorySize));
    500 
    501   delete[] memory;
    502   close(fds[1]);
    503 }
    504 
    505 // Test that an invalid thread stack pointer still results in a minidump.
    506 TEST(MinidumpWriterTest, InvalidStackPointer) {
    507   int fds[2];
    508   ASSERT_NE(-1, pipe(fds));
    509 
    510   const pid_t child = fork();
    511   if (child == 0) {
    512     close(fds[1]);
    513     char b;
    514     HANDLE_EINTR(read(fds[0], &b, sizeof(b)));
    515     close(fds[0]);
    516     syscall(__NR_exit);
    517   }
    518   close(fds[0]);
    519 
    520   ExceptionHandler::CrashContext context;
    521 
    522   // This needs a valid context for minidump writing to work, but getting
    523   // a useful one from the child is too much work, so just use one from
    524   // the parent since the child is just a forked copy anyway.
    525   ASSERT_EQ(0, getcontext(&context.context));
    526   context.tid = child;
    527 
    528   // Fake the child's stack pointer for its crashing thread.  NOTE: This must
    529   // be an invalid memory address for the child process (stack or otherwise).
    530   // Try 1MB below the current stack.
    531   uintptr_t invalid_stack_pointer =
    532       reinterpret_cast<uintptr_t>(&context) - 1024*1024;
    533 #if defined(__i386)
    534   context.context.uc_mcontext.gregs[REG_ESP] = invalid_stack_pointer;
    535 #elif defined(__x86_64)
    536   context.context.uc_mcontext.gregs[REG_RSP] = invalid_stack_pointer;
    537 #elif defined(__ARM_EABI__)
    538   context.context.uc_mcontext.arm_sp = invalid_stack_pointer;
    539 #elif defined(__aarch64__)
    540   context.context.uc_mcontext.sp = invalid_stack_pointer;
    541 #elif defined(__mips__)
    542   context.context.uc_mcontext.gregs[MD_CONTEXT_MIPS_REG_SP] =
    543       invalid_stack_pointer;
    544 #else
    545 # error "This code has not been ported to your platform yet."
    546 #endif
    547 
    548   AutoTempDir temp_dir;
    549   string templ = temp_dir.path() + kMDWriterUnitTestFileName;
    550   // NOTE: In previous versions of Breakpad, WriteMinidump() would fail if
    551   // presented with an invalid stack pointer.
    552   ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context)));
    553 
    554   // Read the minidump. Ensure that the memory region is present
    555   Minidump minidump(templ);
    556   ASSERT_TRUE(minidump.Read());
    557 
    558   // TODO(ted.mielczarek,mkrebs): Enable this part of the test once
    559   // https://breakpad.appspot.com/413002/ is committed.
    560 #if 0
    561   // Make sure there's a thread without a stack.  NOTE: It's okay if
    562   // GetThreadList() shows the error: "ERROR: MinidumpThread has a memory
    563   // region problem".
    564   MinidumpThreadList* dump_thread_list = minidump.GetThreadList();
    565   ASSERT_TRUE(dump_thread_list);
    566   bool found_empty_stack = false;
    567   for (int i = 0; i < dump_thread_list->thread_count(); i++) {
    568     MinidumpThread* thread = dump_thread_list->GetThreadAtIndex(i);
    569     ASSERT_TRUE(thread->thread() != NULL);
    570     // When the stack size is zero bytes, GetMemory() returns NULL.
    571     if (thread->GetMemory() == NULL) {
    572       found_empty_stack = true;
    573       break;
    574     }
    575   }
    576   // NOTE: If you fail this, first make sure that "invalid_stack_pointer"
    577   // above is indeed set to an invalid address.
    578   ASSERT_TRUE(found_empty_stack);
    579 #endif
    580 
    581   close(fds[1]);
    582 }
    583 
    584 // Test that limiting the size of the minidump works.
    585 TEST(MinidumpWriterTest, MinidumpSizeLimit) {
    586   static const int kNumberOfThreadsInHelperProgram = 40;
    587 
    588   char number_of_threads_arg[3];
    589   sprintf(number_of_threads_arg, "%d", kNumberOfThreadsInHelperProgram);
    590 
    591   string helper_path(GetHelperBinary());
    592   if (helper_path.empty()) {
    593     FAIL() << "Couldn't find helper binary";
    594     exit(1);
    595   }
    596 
    597   int fds[2];
    598   ASSERT_NE(-1, pipe(fds));
    599 
    600   pid_t child_pid = fork();
    601   if (child_pid == 0) {
    602     // In child process.
    603     close(fds[0]);
    604 
    605     // Pass the pipe fd and the number of threads as arguments.
    606     char pipe_fd_string[8];
    607     sprintf(pipe_fd_string, "%d", fds[1]);
    608     execl(helper_path.c_str(),
    609           helper_path.c_str(),
    610           pipe_fd_string,
    611           number_of_threads_arg,
    612           NULL);
    613   }
    614   close(fds[1]);
    615 
    616   // Wait for all child threads to indicate that they have started
    617   for (int threads = 0; threads < kNumberOfThreadsInHelperProgram; threads++) {
    618     struct pollfd pfd;
    619     memset(&pfd, 0, sizeof(pfd));
    620     pfd.fd = fds[0];
    621     pfd.events = POLLIN | POLLERR;
    622 
    623     const int r = HANDLE_EINTR(poll(&pfd, 1, 1000));
    624     ASSERT_EQ(1, r);
    625     ASSERT_TRUE(pfd.revents & POLLIN);
    626     uint8_t junk;
    627     ASSERT_EQ(read(fds[0], &junk, sizeof(junk)),
    628               static_cast<ssize_t>(sizeof(junk)));
    629   }
    630   close(fds[0]);
    631 
    632   // There is a race here because we may stop a child thread before
    633   // it is actually running the busy loop. Empirically this sleep
    634   // is sufficient to avoid the race.
    635   usleep(100000);
    636 
    637   // Child and its threads are ready now.
    638 
    639 
    640   off_t normal_file_size;
    641   int total_normal_stack_size = 0;
    642   AutoTempDir temp_dir;
    643 
    644   // First, write a minidump with no size limit.
    645   {
    646     string normal_dump = temp_dir.path() +
    647         "/minidump-writer-unittest.dmp";
    648     ASSERT_TRUE(WriteMinidump(normal_dump.c_str(), -1,
    649                               child_pid, NULL, 0,
    650                               MappingList(), AppMemoryList()));
    651     struct stat st;
    652     ASSERT_EQ(0, stat(normal_dump.c_str(), &st));
    653     ASSERT_GT(st.st_size, 0);
    654     normal_file_size = st.st_size;
    655 
    656     Minidump minidump(normal_dump);
    657     ASSERT_TRUE(minidump.Read());
    658     MinidumpThreadList* dump_thread_list = minidump.GetThreadList();
    659     ASSERT_TRUE(dump_thread_list);
    660     for (unsigned int i = 0; i < dump_thread_list->thread_count(); i++) {
    661       MinidumpThread* thread = dump_thread_list->GetThreadAtIndex(i);
    662       ASSERT_TRUE(thread->thread() != NULL);
    663       // When the stack size is zero bytes, GetMemory() returns NULL.
    664       MinidumpMemoryRegion* memory = thread->GetMemory();
    665       ASSERT_TRUE(memory != NULL);
    666       total_normal_stack_size += memory->GetSize();
    667     }
    668   }
    669 
    670   // Second, write a minidump with a size limit big enough to not trigger
    671   // anything.
    672   {
    673     // Set size limit arbitrarily 1MB larger than the normal file size -- such
    674     // that the limiting code will not kick in.
    675     const off_t minidump_size_limit = normal_file_size + 1024*1024;
    676 
    677     string same_dump = temp_dir.path() +
    678         "/minidump-writer-unittest-same.dmp";
    679     ASSERT_TRUE(WriteMinidump(same_dump.c_str(), minidump_size_limit,
    680                               child_pid, NULL, 0,
    681                               MappingList(), AppMemoryList()));
    682     struct stat st;
    683     ASSERT_EQ(0, stat(same_dump.c_str(), &st));
    684     // Make sure limiting wasn't actually triggered.  NOTE: If you fail this,
    685     // first make sure that "minidump_size_limit" above is indeed set to a
    686     // large enough value -- the limit-checking code in minidump_writer.cc
    687     // does just a rough estimate.
    688     ASSERT_EQ(normal_file_size, st.st_size);
    689   }
    690 
    691   // Third, write a minidump with a size limit small enough to be triggered.
    692   {
    693     // Set size limit to some arbitrary amount, such that the limiting code
    694     // will kick in.  The equation used to set this value was determined by
    695     // simply reversing the size-limit logic a little bit in order to pick a
    696     // size we know will trigger it.  The definition of
    697     // kLimitAverageThreadStackLength here was copied from class
    698     // MinidumpWriter in minidump_writer.cc.
    699     static const unsigned kLimitAverageThreadStackLength = 8 * 1024;
    700     off_t minidump_size_limit = kNumberOfThreadsInHelperProgram *
    701         kLimitAverageThreadStackLength;
    702     // If, in reality, each of the threads' stack is *smaller* than
    703     // kLimitAverageThreadStackLength, the normal file size could very well be
    704     // smaller than the arbitrary limit that was just set.  In that case,
    705     // either of these numbers should trigger the size-limiting code, but we
    706     // might as well pick the smallest.
    707     if (normal_file_size < minidump_size_limit)
    708       minidump_size_limit = normal_file_size;
    709 
    710     string limit_dump = temp_dir.path() +
    711         "/minidump-writer-unittest-limit.dmp";
    712     ASSERT_TRUE(WriteMinidump(limit_dump.c_str(), minidump_size_limit,
    713                               child_pid, NULL, 0,
    714                               MappingList(), AppMemoryList()));
    715     struct stat st;
    716     ASSERT_EQ(0, stat(limit_dump.c_str(), &st));
    717     ASSERT_GT(st.st_size, 0);
    718     // Make sure the file size is at least smaller than the original.  If this
    719     // fails because it's the same size, then the size-limit logic didn't kick
    720     // in like it was supposed to.
    721     EXPECT_LT(st.st_size, normal_file_size);
    722 
    723     Minidump minidump(limit_dump);
    724     ASSERT_TRUE(minidump.Read());
    725     MinidumpThreadList* dump_thread_list = minidump.GetThreadList();
    726     ASSERT_TRUE(dump_thread_list);
    727     int total_limit_stack_size = 0;
    728     for (unsigned int i = 0; i < dump_thread_list->thread_count(); i++) {
    729       MinidumpThread* thread = dump_thread_list->GetThreadAtIndex(i);
    730       ASSERT_TRUE(thread->thread() != NULL);
    731       // When the stack size is zero bytes, GetMemory() returns NULL.
    732       MinidumpMemoryRegion* memory = thread->GetMemory();
    733       ASSERT_TRUE(memory != NULL);
    734       total_limit_stack_size += memory->GetSize();
    735     }
    736 
    737     // Make sure stack size shrunk by at least 1KB per extra thread.  The
    738     // definition of kLimitBaseThreadCount here was copied from class
    739     // MinidumpWriter in minidump_writer.cc.
    740     // Note: The 1KB is arbitrary, and assumes that the thread stacks are big
    741     // enough to shrink by that much.  For example, if each thread stack was
    742     // originally only 2KB, the current size-limit logic wouldn't actually
    743     // shrink them because that's the size to which it tries to shrink.  If
    744     // you fail this part of the test due to something like that, the test
    745     // logic should probably be improved to account for your situation.
    746     const unsigned kLimitBaseThreadCount = 20;
    747     const unsigned kMinPerExtraThreadStackReduction = 1024;
    748     const int min_expected_reduction = (kNumberOfThreadsInHelperProgram -
    749         kLimitBaseThreadCount) * kMinPerExtraThreadStackReduction;
    750     EXPECT_LT(total_limit_stack_size,
    751               total_normal_stack_size - min_expected_reduction);
    752   }
    753 
    754   // Kill the helper program.
    755   kill(child_pid, SIGKILL);
    756 }
    757 
    758 }  // namespace
    759