summaryrefslogtreecommitdiffstats
path: root/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/minidump_writer_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/minidump_writer_unittest.cc')
-rw-r--r--toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/minidump_writer_unittest.cc775
1 files changed, 0 insertions, 775 deletions
diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/minidump_writer_unittest.cc b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/minidump_writer_unittest.cc
deleted file mode 100644
index 2e4749e7e..000000000
--- a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/minidump_writer_unittest.cc
+++ /dev/null
@@ -1,775 +0,0 @@
-// Copyright (c) 2011 Google Inc.
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#include <fcntl.h>
-#include <sys/poll.h>
-#include <sys/stat.h>
-#include <sys/syscall.h>
-#include <sys/types.h>
-#include <ucontext.h>
-#include <unistd.h>
-
-#include <string>
-
-#include "breakpad_googletest_includes.h"
-#include "client/linux/handler/exception_handler.h"
-#include "client/linux/minidump_writer/linux_dumper.h"
-#include "client/linux/minidump_writer/minidump_writer.h"
-#include "client/linux/minidump_writer/minidump_writer_unittest_utils.h"
-#include "common/linux/eintr_wrapper.h"
-#include "common/linux/file_id.h"
-#include "common/linux/ignore_ret.h"
-#include "common/linux/safe_readlink.h"
-#include "common/scoped_ptr.h"
-#include "common/tests/auto_tempdir.h"
-#include "common/tests/file_utils.h"
-#include "common/using_std_string.h"
-#include "google_breakpad/processor/minidump.h"
-
-using namespace google_breakpad;
-
-namespace {
-
-typedef testing::Test MinidumpWriterTest;
-
-const char kMDWriterUnitTestFileName[] = "/minidump-writer-unittest";
-
-TEST(MinidumpWriterTest, SetupWithPath) {
- int fds[2];
- ASSERT_NE(-1, pipe(fds));
-
- const pid_t child = fork();
- if (child == 0) {
- close(fds[1]);
- char b;
- IGNORE_RET(HANDLE_EINTR(read(fds[0], &b, sizeof(b))));
- close(fds[0]);
- syscall(__NR_exit);
- }
- close(fds[0]);
-
- ExceptionHandler::CrashContext context;
- memset(&context, 0, sizeof(context));
-
- AutoTempDir temp_dir;
- string templ = temp_dir.path() + kMDWriterUnitTestFileName;
- // Set a non-zero tid to avoid tripping asserts.
- context.tid = child;
- ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context)));
- struct stat st;
- ASSERT_EQ(0, stat(templ.c_str(), &st));
- ASSERT_GT(st.st_size, 0);
-
- close(fds[1]);
-}
-
-TEST(MinidumpWriterTest, SetupWithFD) {
- int fds[2];
- ASSERT_NE(-1, pipe(fds));
-
- const pid_t child = fork();
- if (child == 0) {
- close(fds[1]);
- char b;
- HANDLE_EINTR(read(fds[0], &b, sizeof(b)));
- close(fds[0]);
- syscall(__NR_exit);
- }
- close(fds[0]);
-
- ExceptionHandler::CrashContext context;
- memset(&context, 0, sizeof(context));
-
- AutoTempDir temp_dir;
- string templ = temp_dir.path() + kMDWriterUnitTestFileName;
- int fd = open(templ.c_str(), O_CREAT | O_WRONLY, S_IRWXU);
- // Set a non-zero tid to avoid tripping asserts.
- context.tid = child;
- ASSERT_TRUE(WriteMinidump(fd, child, &context, sizeof(context)));
- struct stat st;
- ASSERT_EQ(0, stat(templ.c_str(), &st));
- ASSERT_GT(st.st_size, 0);
-
- close(fds[1]);
-}
-
-// Test that mapping info can be specified when writing a minidump,
-// and that it ends up in the module list of the minidump.
-TEST(MinidumpWriterTest, MappingInfo) {
- int fds[2];
- ASSERT_NE(-1, pipe(fds));
-
- // These are defined here so the parent can use them to check the
- // data from the minidump afterwards.
- const uint32_t memory_size = sysconf(_SC_PAGESIZE);
- const char* kMemoryName = "a fake module";
- const uint8_t kModuleGUID[sizeof(MDGUID)] = {
- 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
- 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF
- };
- const string module_identifier = "33221100554477668899AABBCCDDEEFF0";
-
- // Get some memory.
- char* memory =
- reinterpret_cast<char*>(mmap(NULL,
- memory_size,
- PROT_READ | PROT_WRITE,
- MAP_PRIVATE | MAP_ANON,
- -1,
- 0));
- const uintptr_t kMemoryAddress = reinterpret_cast<uintptr_t>(memory);
- ASSERT_TRUE(memory);
-
- const pid_t child = fork();
- if (child == 0) {
- close(fds[1]);
- char b;
- IGNORE_RET(HANDLE_EINTR(read(fds[0], &b, sizeof(b))));
- close(fds[0]);
- syscall(__NR_exit);
- }
- close(fds[0]);
-
- ExceptionHandler::CrashContext context;
- memset(&context, 0, sizeof(context));
- ASSERT_EQ(0, getcontext(&context.context));
- context.tid = child;
-
- AutoTempDir temp_dir;
- string templ = temp_dir.path() + kMDWriterUnitTestFileName;
-
- // Add information about the mapped memory.
- MappingInfo info;
- info.start_addr = kMemoryAddress;
- info.size = memory_size;
- info.offset = 0;
- info.exec = false;
- strcpy(info.name, kMemoryName);
-
- MappingList mappings;
- AppMemoryList memory_list;
- MappingEntry mapping;
- mapping.first = info;
- memcpy(mapping.second, kModuleGUID, sizeof(MDGUID));
- mappings.push_back(mapping);
- ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context),
- mappings, memory_list));
-
- // Read the minidump. Load the module list, and ensure that
- // the mmap'ed |memory| is listed with the given module name
- // and debug ID.
- Minidump minidump(templ);
- ASSERT_TRUE(minidump.Read());
-
- MinidumpModuleList* module_list = minidump.GetModuleList();
- ASSERT_TRUE(module_list);
- const MinidumpModule* module =
- module_list->GetModuleForAddress(kMemoryAddress);
- ASSERT_TRUE(module);
-
- EXPECT_EQ(kMemoryAddress, module->base_address());
- EXPECT_EQ(memory_size, module->size());
- EXPECT_EQ(kMemoryName, module->code_file());
- EXPECT_EQ(module_identifier, module->debug_identifier());
-
- uint32_t len;
- // These streams are expected to be there
- EXPECT_TRUE(minidump.SeekToStreamType(MD_THREAD_LIST_STREAM, &len));
- EXPECT_TRUE(minidump.SeekToStreamType(MD_MEMORY_LIST_STREAM, &len));
- EXPECT_TRUE(minidump.SeekToStreamType(MD_EXCEPTION_STREAM, &len));
- EXPECT_TRUE(minidump.SeekToStreamType(MD_SYSTEM_INFO_STREAM, &len));
- EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_CPU_INFO, &len));
- EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_PROC_STATUS, &len));
- EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_CMD_LINE, &len));
- EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_ENVIRON, &len));
- EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_AUXV, &len));
- EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_MAPS, &len));
- EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_DSO_DEBUG, &len));
-
- close(fds[1]);
-}
-
-// Test that a binary with a longer-than-usual build id note
-// makes its way all the way through to the minidump unscathed.
-// The linux_client_unittest is linked with an explicit --build-id
-// in Makefile.am.
-TEST(MinidumpWriterTest, BuildIDLong) {
- int fds[2];
- ASSERT_NE(-1, pipe(fds));
-
- const pid_t child = fork();
- if (child == 0) {
- close(fds[1]);
- char b;
- IGNORE_RET(HANDLE_EINTR(read(fds[0], &b, sizeof(b))));
- close(fds[0]);
- syscall(__NR_exit);
- }
- close(fds[0]);
-
- ExceptionHandler::CrashContext context;
- memset(&context, 0, sizeof(context));
- ASSERT_EQ(0, getcontext(&context.context));
- context.tid = child;
-
- AutoTempDir temp_dir;
- const string dump_path = temp_dir.path() + kMDWriterUnitTestFileName;
-
- EXPECT_TRUE(WriteMinidump(dump_path.c_str(),
- child, &context, sizeof(context)));
- close(fds[1]);
-
- // Read the minidump. Load the module list, and ensure that
- // the main module has the correct debug id and code id.
- Minidump minidump(dump_path);
- ASSERT_TRUE(minidump.Read());
-
- MinidumpModuleList* module_list = minidump.GetModuleList();
- ASSERT_TRUE(module_list);
- const MinidumpModule* module = module_list->GetMainModule();
- ASSERT_TRUE(module);
- const string module_identifier = "030201000504070608090A0B0C0D0E0F0";
- // This is passed explicitly to the linker in Makefile.am
- const string build_id =
- "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f";
- EXPECT_EQ(module_identifier, module->debug_identifier());
- EXPECT_EQ(build_id, module->code_identifier());
-}
-
-// Test that mapping info can be specified, and that it overrides
-// existing mappings that are wholly contained within the specified
-// range.
-TEST(MinidumpWriterTest, MappingInfoContained) {
- int fds[2];
- ASSERT_NE(-1, pipe(fds));
-
- // These are defined here so the parent can use them to check the
- // data from the minidump afterwards.
- const int32_t memory_size = sysconf(_SC_PAGESIZE);
- const char* kMemoryName = "a fake module";
- const uint8_t kModuleGUID[sizeof(MDGUID)] = {
- 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
- 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF
- };
- const string module_identifier = "33221100554477668899AABBCCDDEEFF0";
-
- // mmap a file
- AutoTempDir temp_dir;
- string tempfile = temp_dir.path() + "/minidump-writer-unittest-temp";
- int fd = open(tempfile.c_str(), O_RDWR | O_CREAT, 0);
- ASSERT_NE(-1, fd);
- unlink(tempfile.c_str());
- // fill with zeros
- google_breakpad::scoped_array<char> buffer(new char[memory_size]);
- memset(buffer.get(), 0, memory_size);
- ASSERT_EQ(memory_size, write(fd, buffer.get(), memory_size));
- lseek(fd, 0, SEEK_SET);
-
- char* memory =
- reinterpret_cast<char*>(mmap(NULL,
- memory_size,
- PROT_READ | PROT_WRITE,
- MAP_PRIVATE,
- fd,
- 0));
- const uintptr_t kMemoryAddress = reinterpret_cast<uintptr_t>(memory);
- ASSERT_TRUE(memory);
- close(fd);
-
- const pid_t child = fork();
- if (child == 0) {
- close(fds[1]);
- char b;
- IGNORE_RET(HANDLE_EINTR(read(fds[0], &b, sizeof(b))));
- close(fds[0]);
- syscall(__NR_exit);
- }
- close(fds[0]);
-
- ExceptionHandler::CrashContext context;
- memset(&context, 0, sizeof(context));
- context.tid = 1;
-
- string dumpfile = temp_dir.path() + kMDWriterUnitTestFileName;
-
- // Add information about the mapped memory. Report it as being larger than
- // it actually is.
- MappingInfo info;
- info.start_addr = kMemoryAddress - memory_size;
- info.size = memory_size * 3;
- info.offset = 0;
- info.exec = false;
- strcpy(info.name, kMemoryName);
-
- MappingList mappings;
- AppMemoryList memory_list;
- MappingEntry mapping;
- mapping.first = info;
- memcpy(mapping.second, kModuleGUID, sizeof(MDGUID));
- mappings.push_back(mapping);
- ASSERT_TRUE(WriteMinidump(dumpfile.c_str(), child, &context, sizeof(context),
- mappings, memory_list));
-
- // Read the minidump. Load the module list, and ensure that
- // the mmap'ed |memory| is listed with the given module name
- // and debug ID.
- Minidump minidump(dumpfile);
- ASSERT_TRUE(minidump.Read());
-
- MinidumpModuleList* module_list = minidump.GetModuleList();
- ASSERT_TRUE(module_list);
- const MinidumpModule* module =
- module_list->GetModuleForAddress(kMemoryAddress);
- ASSERT_TRUE(module);
-
- EXPECT_EQ(info.start_addr, module->base_address());
- EXPECT_EQ(info.size, module->size());
- EXPECT_EQ(kMemoryName, module->code_file());
- EXPECT_EQ(module_identifier, module->debug_identifier());
-
- close(fds[1]);
-}
-
-TEST(MinidumpWriterTest, DeletedBinary) {
- const string kNumberOfThreadsArgument = "1";
- const string helper_path(GetHelperBinary());
- if (helper_path.empty()) {
- FAIL() << "Couldn't find helper binary";
- exit(1);
- }
-
- // Copy binary to a temp file.
- AutoTempDir temp_dir;
- string binpath = temp_dir.path() + "/linux-dumper-unittest-helper";
- ASSERT_TRUE(CopyFile(helper_path.c_str(), binpath.c_str()))
- << "Failed to copy " << helper_path << " to " << binpath;
- ASSERT_EQ(0, chmod(binpath.c_str(), 0755));
-
- int fds[2];
- ASSERT_NE(-1, pipe(fds));
-
- pid_t child_pid = fork();
- if (child_pid == 0) {
- // In child process.
- close(fds[0]);
-
- // Pass the pipe fd and the number of threads as arguments.
- char pipe_fd_string[8];
- sprintf(pipe_fd_string, "%d", fds[1]);
- execl(binpath.c_str(),
- binpath.c_str(),
- pipe_fd_string,
- kNumberOfThreadsArgument.c_str(),
- NULL);
- }
- close(fds[1]);
- // Wait for the child process to signal that it's ready.
- struct pollfd pfd;
- memset(&pfd, 0, sizeof(pfd));
- pfd.fd = fds[0];
- pfd.events = POLLIN | POLLERR;
-
- const int r = HANDLE_EINTR(poll(&pfd, 1, 1000));
- ASSERT_EQ(1, r);
- ASSERT_TRUE(pfd.revents & POLLIN);
- uint8_t junk;
- const int nr = HANDLE_EINTR(read(fds[0], &junk, sizeof(junk)));
- ASSERT_EQ(static_cast<ssize_t>(sizeof(junk)), nr);
- close(fds[0]);
-
- // Child is ready now.
- // Unlink the test binary.
- unlink(binpath.c_str());
-
- ExceptionHandler::CrashContext context;
- memset(&context, 0, sizeof(context));
-
- string templ = temp_dir.path() + kMDWriterUnitTestFileName;
- // Set a non-zero tid to avoid tripping asserts.
- context.tid = child_pid;
- ASSERT_TRUE(WriteMinidump(templ.c_str(), child_pid, &context,
- sizeof(context)));
- kill(child_pid, SIGKILL);
-
- struct stat st;
- ASSERT_EQ(0, stat(templ.c_str(), &st));
- ASSERT_GT(st.st_size, 0);
-
- Minidump minidump(templ);
- ASSERT_TRUE(minidump.Read());
-
- // Check that the main module filename is correct.
- MinidumpModuleList* module_list = minidump.GetModuleList();
- ASSERT_TRUE(module_list);
- const MinidumpModule* module = module_list->GetMainModule();
- EXPECT_STREQ(binpath.c_str(), module->code_file().c_str());
- // Check that the file ID is correct.
- FileID fileid(helper_path.c_str());
- PageAllocator allocator;
- wasteful_vector<uint8_t> identifier(&allocator, kDefaultBuildIdSize);
- EXPECT_TRUE(fileid.ElfFileIdentifier(identifier));
- string identifier_string = FileID::ConvertIdentifierToUUIDString(identifier);
- string module_identifier(identifier_string);
- // Strip out dashes
- size_t pos;
- while ((pos = module_identifier.find('-')) != string::npos) {
- module_identifier.erase(pos, 1);
- }
- // And append a zero, because module IDs include an "age" field
- // which is always zero on Linux.
- module_identifier += "0";
- EXPECT_EQ(module_identifier, module->debug_identifier());
-}
-
-// Test that an additional memory region can be added to the minidump.
-TEST(MinidumpWriterTest, AdditionalMemory) {
- int fds[2];
- ASSERT_NE(-1, pipe(fds));
-
- // These are defined here so the parent can use them to check the
- // data from the minidump afterwards.
- const uint32_t kMemorySize = sysconf(_SC_PAGESIZE);
-
- // Get some heap memory.
- uint8_t* memory = new uint8_t[kMemorySize];
- const uintptr_t kMemoryAddress = reinterpret_cast<uintptr_t>(memory);
- ASSERT_TRUE(memory);
-
- // Stick some data into the memory so the contents can be verified.
- for (uint32_t i = 0; i < kMemorySize; ++i) {
- memory[i] = i % 255;
- }
-
- const pid_t child = fork();
- if (child == 0) {
- close(fds[1]);
- char b;
- HANDLE_EINTR(read(fds[0], &b, sizeof(b)));
- close(fds[0]);
- syscall(__NR_exit);
- }
- close(fds[0]);
-
- ExceptionHandler::CrashContext context;
-
- // This needs a valid context for minidump writing to work, but getting
- // a useful one from the child is too much work, so just use one from
- // the parent since the child is just a forked copy anyway.
- ASSERT_EQ(0, getcontext(&context.context));
- context.tid = child;
-
- AutoTempDir temp_dir;
- string templ = temp_dir.path() + kMDWriterUnitTestFileName;
- unlink(templ.c_str());
-
- MappingList mappings;
- AppMemoryList memory_list;
-
- // Add the memory region to the list of memory to be included.
- AppMemory app_memory;
- app_memory.ptr = memory;
- app_memory.length = kMemorySize;
- memory_list.push_back(app_memory);
- ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context),
- mappings, memory_list));
-
- // Read the minidump. Ensure that the memory region is present
- Minidump minidump(templ);
- ASSERT_TRUE(minidump.Read());
-
- MinidumpMemoryList* dump_memory_list = minidump.GetMemoryList();
- ASSERT_TRUE(dump_memory_list);
- const MinidumpMemoryRegion* region =
- dump_memory_list->GetMemoryRegionForAddress(kMemoryAddress);
- ASSERT_TRUE(region);
-
- EXPECT_EQ(kMemoryAddress, region->GetBase());
- EXPECT_EQ(kMemorySize, region->GetSize());
-
- // Verify memory contents.
- EXPECT_EQ(0, memcmp(region->GetMemory(), memory, kMemorySize));
-
- delete[] memory;
- close(fds[1]);
-}
-
-// Test that an invalid thread stack pointer still results in a minidump.
-TEST(MinidumpWriterTest, InvalidStackPointer) {
- int fds[2];
- ASSERT_NE(-1, pipe(fds));
-
- const pid_t child = fork();
- if (child == 0) {
- close(fds[1]);
- char b;
- HANDLE_EINTR(read(fds[0], &b, sizeof(b)));
- close(fds[0]);
- syscall(__NR_exit);
- }
- close(fds[0]);
-
- ExceptionHandler::CrashContext context;
-
- // This needs a valid context for minidump writing to work, but getting
- // a useful one from the child is too much work, so just use one from
- // the parent since the child is just a forked copy anyway.
- ASSERT_EQ(0, getcontext(&context.context));
- context.tid = child;
-
- // Fake the child's stack pointer for its crashing thread. NOTE: This must
- // be an invalid memory address for the child process (stack or otherwise).
- // Try 1MB below the current stack.
- uintptr_t invalid_stack_pointer =
- reinterpret_cast<uintptr_t>(&context) - 1024*1024;
-#if defined(__i386)
- context.context.uc_mcontext.gregs[REG_ESP] = invalid_stack_pointer;
-#elif defined(__x86_64)
- context.context.uc_mcontext.gregs[REG_RSP] = invalid_stack_pointer;
-#elif defined(__ARM_EABI__)
- context.context.uc_mcontext.arm_sp = invalid_stack_pointer;
-#elif defined(__aarch64__)
- context.context.uc_mcontext.sp = invalid_stack_pointer;
-#elif defined(__mips__)
- context.context.uc_mcontext.gregs[MD_CONTEXT_MIPS_REG_SP] =
- invalid_stack_pointer;
-#else
-# error "This code has not been ported to your platform yet."
-#endif
-
- AutoTempDir temp_dir;
- string templ = temp_dir.path() + kMDWriterUnitTestFileName;
- // NOTE: In previous versions of Breakpad, WriteMinidump() would fail if
- // presented with an invalid stack pointer.
- ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context)));
-
- // Read the minidump. Ensure that the memory region is present
- Minidump minidump(templ);
- ASSERT_TRUE(minidump.Read());
-
- // TODO(ted.mielczarek,mkrebs): Enable this part of the test once
- // https://breakpad.appspot.com/413002/ is committed.
-#if 0
- // Make sure there's a thread without a stack. NOTE: It's okay if
- // GetThreadList() shows the error: "ERROR: MinidumpThread has a memory
- // region problem".
- MinidumpThreadList* dump_thread_list = minidump.GetThreadList();
- ASSERT_TRUE(dump_thread_list);
- bool found_empty_stack = false;
- for (int i = 0; i < dump_thread_list->thread_count(); i++) {
- MinidumpThread* thread = dump_thread_list->GetThreadAtIndex(i);
- ASSERT_TRUE(thread->thread() != NULL);
- // When the stack size is zero bytes, GetMemory() returns NULL.
- if (thread->GetMemory() == NULL) {
- found_empty_stack = true;
- break;
- }
- }
- // NOTE: If you fail this, first make sure that "invalid_stack_pointer"
- // above is indeed set to an invalid address.
- ASSERT_TRUE(found_empty_stack);
-#endif
-
- close(fds[1]);
-}
-
-// Test that limiting the size of the minidump works.
-TEST(MinidumpWriterTest, MinidumpSizeLimit) {
- static const int kNumberOfThreadsInHelperProgram = 40;
-
- char number_of_threads_arg[3];
- sprintf(number_of_threads_arg, "%d", kNumberOfThreadsInHelperProgram);
-
- string helper_path(GetHelperBinary());
- if (helper_path.empty()) {
- FAIL() << "Couldn't find helper binary";
- exit(1);
- }
-
- int fds[2];
- ASSERT_NE(-1, pipe(fds));
-
- pid_t child_pid = fork();
- if (child_pid == 0) {
- // In child process.
- close(fds[0]);
-
- // Pass the pipe fd and the number of threads as arguments.
- char pipe_fd_string[8];
- sprintf(pipe_fd_string, "%d", fds[1]);
- execl(helper_path.c_str(),
- helper_path.c_str(),
- pipe_fd_string,
- number_of_threads_arg,
- NULL);
- }
- close(fds[1]);
-
- // Wait for all child threads to indicate that they have started
- for (int threads = 0; threads < kNumberOfThreadsInHelperProgram; threads++) {
- struct pollfd pfd;
- memset(&pfd, 0, sizeof(pfd));
- pfd.fd = fds[0];
- pfd.events = POLLIN | POLLERR;
-
- const int r = HANDLE_EINTR(poll(&pfd, 1, 1000));
- ASSERT_EQ(1, r);
- ASSERT_TRUE(pfd.revents & POLLIN);
- uint8_t junk;
- ASSERT_EQ(read(fds[0], &junk, sizeof(junk)),
- static_cast<ssize_t>(sizeof(junk)));
- }
- close(fds[0]);
-
- // There is a race here because we may stop a child thread before
- // it is actually running the busy loop. Empirically this sleep
- // is sufficient to avoid the race.
- usleep(100000);
-
- // Child and its threads are ready now.
-
-
- off_t normal_file_size;
- int total_normal_stack_size = 0;
- AutoTempDir temp_dir;
-
- // First, write a minidump with no size limit.
- {
- string normal_dump = temp_dir.path() +
- "/minidump-writer-unittest.dmp";
- ASSERT_TRUE(WriteMinidump(normal_dump.c_str(), -1,
- child_pid, NULL, 0,
- MappingList(), AppMemoryList()));
- struct stat st;
- ASSERT_EQ(0, stat(normal_dump.c_str(), &st));
- ASSERT_GT(st.st_size, 0);
- normal_file_size = st.st_size;
-
- Minidump minidump(normal_dump);
- ASSERT_TRUE(minidump.Read());
- MinidumpThreadList* dump_thread_list = minidump.GetThreadList();
- ASSERT_TRUE(dump_thread_list);
- for (unsigned int i = 0; i < dump_thread_list->thread_count(); i++) {
- MinidumpThread* thread = dump_thread_list->GetThreadAtIndex(i);
- ASSERT_TRUE(thread->thread() != NULL);
- // When the stack size is zero bytes, GetMemory() returns NULL.
- MinidumpMemoryRegion* memory = thread->GetMemory();
- ASSERT_TRUE(memory != NULL);
- total_normal_stack_size += memory->GetSize();
- }
- }
-
- // Second, write a minidump with a size limit big enough to not trigger
- // anything.
- {
- // Set size limit arbitrarily 1MB larger than the normal file size -- such
- // that the limiting code will not kick in.
- const off_t minidump_size_limit = normal_file_size + 1024*1024;
-
- string same_dump = temp_dir.path() +
- "/minidump-writer-unittest-same.dmp";
- ASSERT_TRUE(WriteMinidump(same_dump.c_str(), minidump_size_limit,
- child_pid, NULL, 0,
- MappingList(), AppMemoryList()));
- struct stat st;
- ASSERT_EQ(0, stat(same_dump.c_str(), &st));
- // Make sure limiting wasn't actually triggered. NOTE: If you fail this,
- // first make sure that "minidump_size_limit" above is indeed set to a
- // large enough value -- the limit-checking code in minidump_writer.cc
- // does just a rough estimate.
- ASSERT_EQ(normal_file_size, st.st_size);
- }
-
- // Third, write a minidump with a size limit small enough to be triggered.
- {
- // Set size limit to some arbitrary amount, such that the limiting code
- // will kick in. The equation used to set this value was determined by
- // simply reversing the size-limit logic a little bit in order to pick a
- // size we know will trigger it. The definition of
- // kLimitAverageThreadStackLength here was copied from class
- // MinidumpWriter in minidump_writer.cc.
- static const unsigned kLimitAverageThreadStackLength = 8 * 1024;
- off_t minidump_size_limit = kNumberOfThreadsInHelperProgram *
- kLimitAverageThreadStackLength;
- // If, in reality, each of the threads' stack is *smaller* than
- // kLimitAverageThreadStackLength, the normal file size could very well be
- // smaller than the arbitrary limit that was just set. In that case,
- // either of these numbers should trigger the size-limiting code, but we
- // might as well pick the smallest.
- if (normal_file_size < minidump_size_limit)
- minidump_size_limit = normal_file_size;
-
- string limit_dump = temp_dir.path() +
- "/minidump-writer-unittest-limit.dmp";
- ASSERT_TRUE(WriteMinidump(limit_dump.c_str(), minidump_size_limit,
- child_pid, NULL, 0,
- MappingList(), AppMemoryList()));
- struct stat st;
- ASSERT_EQ(0, stat(limit_dump.c_str(), &st));
- ASSERT_GT(st.st_size, 0);
- // Make sure the file size is at least smaller than the original. If this
- // fails because it's the same size, then the size-limit logic didn't kick
- // in like it was supposed to.
- EXPECT_LT(st.st_size, normal_file_size);
-
- Minidump minidump(limit_dump);
- ASSERT_TRUE(minidump.Read());
- MinidumpThreadList* dump_thread_list = minidump.GetThreadList();
- ASSERT_TRUE(dump_thread_list);
- int total_limit_stack_size = 0;
- for (unsigned int i = 0; i < dump_thread_list->thread_count(); i++) {
- MinidumpThread* thread = dump_thread_list->GetThreadAtIndex(i);
- ASSERT_TRUE(thread->thread() != NULL);
- // When the stack size is zero bytes, GetMemory() returns NULL.
- MinidumpMemoryRegion* memory = thread->GetMemory();
- ASSERT_TRUE(memory != NULL);
- total_limit_stack_size += memory->GetSize();
- }
-
- // Make sure stack size shrunk by at least 1KB per extra thread. The
- // definition of kLimitBaseThreadCount here was copied from class
- // MinidumpWriter in minidump_writer.cc.
- // Note: The 1KB is arbitrary, and assumes that the thread stacks are big
- // enough to shrink by that much. For example, if each thread stack was
- // originally only 2KB, the current size-limit logic wouldn't actually
- // shrink them because that's the size to which it tries to shrink. If
- // you fail this part of the test due to something like that, the test
- // logic should probably be improved to account for your situation.
- const unsigned kLimitBaseThreadCount = 20;
- const unsigned kMinPerExtraThreadStackReduction = 1024;
- const int min_expected_reduction = (kNumberOfThreadsInHelperProgram -
- kLimitBaseThreadCount) * kMinPerExtraThreadStackReduction;
- EXPECT_LT(total_limit_stack_size,
- total_normal_stack_size - min_expected_reduction);
- }
-
- // Kill the helper program.
- kill(child_pid, SIGKILL);
-}
-
-} // namespace