summaryrefslogtreecommitdiffstats
path: root/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_ptrace_dumper_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_ptrace_dumper_unittest.cc')
-rw-r--r--toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_ptrace_dumper_unittest.cc470
1 files changed, 0 insertions, 470 deletions
diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_ptrace_dumper_unittest.cc b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_ptrace_dumper_unittest.cc
deleted file mode 100644
index be533e157..000000000
--- a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_ptrace_dumper_unittest.cc
+++ /dev/null
@@ -1,470 +0,0 @@
-// Copyright (c) 2009, 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.
-
-// linux_ptrace_dumper_unittest.cc:
-// Unit tests for google_breakpad::LinuxPtraceDumper.
-//
-// This file was renamed from linux_dumper_unittest.cc and modified due
-// to LinuxDumper being splitted into two classes.
-
-#include <errno.h>
-#include <fcntl.h>
-#include <limits.h>
-#include <unistd.h>
-#include <signal.h>
-#include <stdint.h>
-#include <string.h>
-#include <sys/mman.h>
-#include <sys/prctl.h>
-#include <sys/poll.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-
-#include <string>
-
-#include "breakpad_googletest_includes.h"
-#include "client/linux/minidump_writer/linux_ptrace_dumper.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/memory.h"
-#include "common/using_std_string.h"
-
-#ifndef PR_SET_PTRACER
-#define PR_SET_PTRACER 0x59616d61
-#endif
-
-using namespace google_breakpad;
-
-namespace {
-
-typedef wasteful_vector<uint8_t> id_vector;
-typedef testing::Test LinuxPtraceDumperTest;
-
-/* Fixture for running tests in a child process. */
-class LinuxPtraceDumperChildTest : public testing::Test {
- protected:
- virtual void SetUp() {
- child_pid_ = fork();
-#ifndef __ANDROID__
- prctl(PR_SET_PTRACER, child_pid_);
-#endif
- }
-
- /* Gtest is calling TestBody from this class, which sets up a child
- * process in which the RealTestBody virtual member is called.
- * As such, TestBody is not supposed to be overridden in derived classes.
- */
- virtual void TestBody() /* final */ {
- if (child_pid_ == 0) {
- // child process
- RealTestBody();
- exit(HasFatalFailure() ? kFatalFailure :
- (HasNonfatalFailure() ? kNonFatalFailure : 0));
- }
-
- ASSERT_TRUE(child_pid_ > 0);
- int status;
- waitpid(child_pid_, &status, 0);
- if (WEXITSTATUS(status) == kFatalFailure) {
- GTEST_FATAL_FAILURE_("Test failed in child process");
- } else if (WEXITSTATUS(status) == kNonFatalFailure) {
- GTEST_NONFATAL_FAILURE_("Test failed in child process");
- }
- }
-
- /* Gtest defines TestBody functions through its macros, but classes
- * derived from this one need to define RealTestBody instead.
- * This is achieved by defining a TestBody macro further below.
- */
- virtual void RealTestBody() = 0;
-
- id_vector make_vector() {
- return id_vector(&allocator, kDefaultBuildIdSize);
- }
-
- private:
- static const int kFatalFailure = 1;
- static const int kNonFatalFailure = 2;
-
- pid_t child_pid_;
- PageAllocator allocator;
-};
-
-} // namespace
-
-/* Replace TestBody declarations within TEST*() with RealTestBody
- * declarations */
-#define TestBody RealTestBody
-
-TEST_F(LinuxPtraceDumperChildTest, Setup) {
- LinuxPtraceDumper dumper(getppid());
-}
-
-TEST_F(LinuxPtraceDumperChildTest, FindMappings) {
- LinuxPtraceDumper dumper(getppid());
- ASSERT_TRUE(dumper.Init());
-
- ASSERT_TRUE(dumper.FindMapping(reinterpret_cast<void*>(getpid)));
- ASSERT_TRUE(dumper.FindMapping(reinterpret_cast<void*>(printf)));
- ASSERT_FALSE(dumper.FindMapping(NULL));
-}
-
-TEST_F(LinuxPtraceDumperChildTest, ThreadList) {
- LinuxPtraceDumper dumper(getppid());
- ASSERT_TRUE(dumper.Init());
-
- ASSERT_GE(dumper.threads().size(), (size_t)1);
- bool found = false;
- for (size_t i = 0; i < dumper.threads().size(); ++i) {
- if (dumper.threads()[i] == getppid()) {
- ASSERT_FALSE(found);
- found = true;
- }
- }
- ASSERT_TRUE(found);
-}
-
-// Helper stack class to close a file descriptor and unmap
-// a mmap'ed mapping.
-class StackHelper {
- public:
- StackHelper()
- : fd_(-1), mapping_(NULL), size_(0) {}
- ~StackHelper() {
- if (size_)
- munmap(mapping_, size_);
- if (fd_ >= 0)
- close(fd_);
- }
- void Init(int fd, char* mapping, size_t size) {
- fd_ = fd;
- mapping_ = mapping;
- size_ = size;
- }
-
- char* mapping() const { return mapping_; }
- size_t size() const { return size_; }
-
- private:
- int fd_;
- char* mapping_;
- size_t size_;
-};
-
-class LinuxPtraceDumperMappingsTest : public LinuxPtraceDumperChildTest {
- protected:
- virtual void SetUp();
-
- string helper_path_;
- size_t page_size_;
- StackHelper helper_;
-};
-
-void LinuxPtraceDumperMappingsTest::SetUp() {
- helper_path_ = GetHelperBinary();
- if (helper_path_.empty()) {
- FAIL() << "Couldn't find helper binary";
- exit(1);
- }
-
- // mmap two segments out of the helper binary, one
- // enclosed in the other, but with different protections.
- page_size_ = sysconf(_SC_PAGESIZE);
- const size_t kMappingSize = 3 * page_size_;
- int fd = open(helper_path_.c_str(), O_RDONLY);
- ASSERT_NE(-1, fd) << "Failed to open file: " << helper_path_
- << ", Error: " << strerror(errno);
- char* mapping =
- reinterpret_cast<char*>(mmap(NULL,
- kMappingSize,
- PROT_READ,
- MAP_SHARED,
- fd,
- 0));
- ASSERT_TRUE(mapping);
-
- // Ensure that things get cleaned up.
- helper_.Init(fd, mapping, kMappingSize);
-
- // Carve a page out of the first mapping with different permissions.
- char* inside_mapping = reinterpret_cast<char*>(
- mmap(mapping + 2 * page_size_,
- page_size_,
- PROT_NONE,
- MAP_SHARED | MAP_FIXED,
- fd,
- // Map a different offset just to
- // better test real-world conditions.
- page_size_));
- ASSERT_TRUE(inside_mapping);
-
- LinuxPtraceDumperChildTest::SetUp();
-}
-
-TEST_F(LinuxPtraceDumperMappingsTest, MergedMappings) {
- // Now check that LinuxPtraceDumper interpreted the mappings properly.
- LinuxPtraceDumper dumper(getppid());
- ASSERT_TRUE(dumper.Init());
- int mapping_count = 0;
- for (unsigned i = 0; i < dumper.mappings().size(); ++i) {
- const MappingInfo& mapping = *dumper.mappings()[i];
- if (strcmp(mapping.name, this->helper_path_.c_str()) == 0) {
- // This mapping should encompass the entire original mapped
- // range.
- EXPECT_EQ(reinterpret_cast<uintptr_t>(this->helper_.mapping()),
- mapping.start_addr);
- EXPECT_EQ(this->helper_.size(), mapping.size);
- EXPECT_EQ(0U, mapping.offset);
- mapping_count++;
- }
- }
- EXPECT_EQ(1, mapping_count);
-}
-
-TEST_F(LinuxPtraceDumperChildTest, BuildProcPath) {
- const pid_t pid = getppid();
- LinuxPtraceDumper dumper(pid);
-
- char maps_path[NAME_MAX] = "";
- char maps_path_expected[NAME_MAX];
- snprintf(maps_path_expected, sizeof(maps_path_expected),
- "/proc/%d/maps", pid);
- EXPECT_TRUE(dumper.BuildProcPath(maps_path, pid, "maps"));
- EXPECT_STREQ(maps_path_expected, maps_path);
-
- EXPECT_FALSE(dumper.BuildProcPath(NULL, pid, "maps"));
- EXPECT_FALSE(dumper.BuildProcPath(maps_path, 0, "maps"));
- EXPECT_FALSE(dumper.BuildProcPath(maps_path, pid, ""));
- EXPECT_FALSE(dumper.BuildProcPath(maps_path, pid, NULL));
-
- char long_node[NAME_MAX];
- size_t long_node_len = NAME_MAX - strlen("/proc/123") - 1;
- memset(long_node, 'a', long_node_len);
- long_node[long_node_len] = '\0';
- EXPECT_FALSE(dumper.BuildProcPath(maps_path, 123, long_node));
-}
-
-#if !defined(__ARM_EABI__) && !defined(__mips__)
-// Ensure that the linux-gate VDSO is included in the mapping list.
-TEST_F(LinuxPtraceDumperChildTest, MappingsIncludeLinuxGate) {
- LinuxPtraceDumper dumper(getppid());
- ASSERT_TRUE(dumper.Init());
-
- void* linux_gate_loc =
- reinterpret_cast<void *>(dumper.auxv()[AT_SYSINFO_EHDR]);
- ASSERT_TRUE(linux_gate_loc);
- bool found_linux_gate = false;
-
- const wasteful_vector<MappingInfo*> mappings = dumper.mappings();
- const MappingInfo* mapping;
- for (unsigned i = 0; i < mappings.size(); ++i) {
- mapping = mappings[i];
- if (!strcmp(mapping->name, kLinuxGateLibraryName)) {
- found_linux_gate = true;
- break;
- }
- }
- EXPECT_TRUE(found_linux_gate);
- EXPECT_EQ(linux_gate_loc, reinterpret_cast<void*>(mapping->start_addr));
- EXPECT_EQ(0, memcmp(linux_gate_loc, ELFMAG, SELFMAG));
-}
-
-// Ensure that the linux-gate VDSO can generate a non-zeroed File ID.
-TEST_F(LinuxPtraceDumperChildTest, LinuxGateMappingID) {
- LinuxPtraceDumper dumper(getppid());
- ASSERT_TRUE(dumper.Init());
-
- bool found_linux_gate = false;
- const wasteful_vector<MappingInfo*> mappings = dumper.mappings();
- unsigned index = 0;
- for (unsigned i = 0; i < mappings.size(); ++i) {
- if (!strcmp(mappings[i]->name, kLinuxGateLibraryName)) {
- found_linux_gate = true;
- index = i;
- break;
- }
- }
- ASSERT_TRUE(found_linux_gate);
-
- // Need to suspend the child so ptrace actually works.
- ASSERT_TRUE(dumper.ThreadsSuspend());
- id_vector identifier(make_vector());
- ASSERT_TRUE(dumper.ElfFileIdentifierForMapping(*mappings[index],
- true,
- index,
- identifier));
-
- id_vector empty_identifier(make_vector());
- empty_identifier.resize(kDefaultBuildIdSize, 0);
- EXPECT_NE(empty_identifier, identifier);
- EXPECT_TRUE(dumper.ThreadsResume());
-}
-#endif
-
-TEST_F(LinuxPtraceDumperChildTest, FileIDsMatch) {
- // Calculate the File ID of our binary using both
- // FileID::ElfFileIdentifier and LinuxDumper::ElfFileIdentifierForMapping
- // and ensure that we get the same result from both.
- char exe_name[PATH_MAX];
- ASSERT_TRUE(SafeReadLink("/proc/self/exe", exe_name));
-
- LinuxPtraceDumper dumper(getppid());
- ASSERT_TRUE(dumper.Init());
- const wasteful_vector<MappingInfo*> mappings = dumper.mappings();
- bool found_exe = false;
- unsigned i;
- for (i = 0; i < mappings.size(); ++i) {
- const MappingInfo* mapping = mappings[i];
- if (!strcmp(mapping->name, exe_name)) {
- found_exe = true;
- break;
- }
- }
- ASSERT_TRUE(found_exe);
-
- id_vector identifier1(make_vector());
- id_vector identifier2(make_vector());
- EXPECT_TRUE(dumper.ElfFileIdentifierForMapping(*mappings[i], true, i,
- identifier1));
- FileID fileid(exe_name);
- EXPECT_TRUE(fileid.ElfFileIdentifier(identifier2));
-
- string identifier_string1 =
- FileID::ConvertIdentifierToUUIDString(identifier1);
- string identifier_string2 =
- FileID::ConvertIdentifierToUUIDString(identifier2);
- EXPECT_EQ(identifier_string1, identifier_string2);
-}
-
-/* Get back to normal behavior of TEST*() macros wrt TestBody. */
-#undef TestBody
-
-TEST(LinuxPtraceDumperTest, VerifyStackReadWithMultipleThreads) {
- static const int kNumberOfThreadsInHelperProgram = 5;
- char kNumberOfThreadsArgument[2];
- sprintf(kNumberOfThreadsArgument, "%d", kNumberOfThreadsInHelperProgram);
-
- int fds[2];
- ASSERT_NE(-1, pipe(fds));
-
- pid_t child_pid = fork();
- if (child_pid == 0) {
- // In child process.
- close(fds[0]);
-
- string helper_path(GetHelperBinary());
- if (helper_path.empty()) {
- FAIL() << "Couldn't find helper binary";
- exit(1);
- }
-
- // 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(),
- "linux_dumper_unittest_helper",
- pipe_fd_string,
- kNumberOfThreadsArgument,
- NULL);
- // Kill if we get here.
- printf("Errno from exec: %d", errno);
- FAIL() << "Exec of " << helper_path << " failed: " << strerror(errno);
- exit(0);
- }
- 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);
-
- // Children are ready now.
- LinuxPtraceDumper dumper(child_pid);
- ASSERT_TRUE(dumper.Init());
- EXPECT_EQ((size_t)kNumberOfThreadsInHelperProgram, dumper.threads().size());
- EXPECT_TRUE(dumper.ThreadsSuspend());
-
- ThreadInfo one_thread;
- for (size_t i = 0; i < dumper.threads().size(); ++i) {
- EXPECT_TRUE(dumper.GetThreadInfoByIndex(i, &one_thread));
- const void* stack;
- size_t stack_len;
- EXPECT_TRUE(dumper.GetStackInfo(&stack, &stack_len,
- one_thread.stack_pointer));
- // In the helper program, we stored a pointer to the thread id in a
- // specific register. Check that we can recover its value.
-#if defined(__ARM_EABI__)
- pid_t* process_tid_location = (pid_t*)(one_thread.regs.uregs[3]);
-#elif defined(__aarch64__)
- pid_t* process_tid_location = (pid_t*)(one_thread.regs.regs[3]);
-#elif defined(__i386)
- pid_t* process_tid_location = (pid_t*)(one_thread.regs.ecx);
-#elif defined(__x86_64)
- pid_t* process_tid_location = (pid_t*)(one_thread.regs.rcx);
-#elif defined(__mips__)
- pid_t* process_tid_location =
- reinterpret_cast<pid_t*>(one_thread.mcontext.gregs[1]);
-#else
-#error This test has not been ported to this platform.
-#endif
- pid_t one_thread_id;
- dumper.CopyFromProcess(&one_thread_id,
- dumper.threads()[i],
- process_tid_location,
- 4);
- EXPECT_EQ(dumper.threads()[i], one_thread_id);
- }
- EXPECT_TRUE(dumper.ThreadsResume());
- kill(child_pid, SIGKILL);
-
- // Reap child
- int status;
- ASSERT_NE(-1, HANDLE_EINTR(waitpid(child_pid, &status, 0)));
- ASSERT_TRUE(WIFSIGNALED(status));
- ASSERT_EQ(SIGKILL, WTERMSIG(status));
-}