google-breakpad: uprev breakpad to 875
Change-Id: Ia2403f9eb2db173035b5f511b169723a3fb7c439
diff --git a/src/client/linux/minidump_writer/linux_dumper_unittest.cc b/src/client/linux/minidump_writer/linux_dumper_unittest.cc
index 323a23a..fb287ca 100644
--- a/src/client/linux/minidump_writer/linux_dumper_unittest.cc
+++ b/src/client/linux/minidump_writer/linux_dumper_unittest.cc
@@ -29,31 +29,45 @@
#include <string>
+#include <fcntl.h>
#include <limits.h>
#include <unistd.h>
#include <signal.h>
+#include <stdint.h>
+#include <sys/mman.h>
+#include <sys/poll.h>
+#include <sys/stat.h>
#include <sys/types.h>
#include "breakpad_googletest_includes.h"
#include "client/linux/minidump_writer/linux_dumper.h"
+#include "common/linux/eintr_wrapper.h"
#include "common/linux/file_id.h"
#include "common/memory.h"
using std::string;
using namespace google_breakpad;
-// This provides a wrapper around system calls which may be
-// interrupted by a signal and return EINTR. See man 7 signal.
-#define HANDLE_EINTR(x) ({ \
- typeof(x) __eintr_result__; \
- do { \
- __eintr_result__ = x; \
- } while (__eintr_result__ == -1 && errno == EINTR); \
- __eintr_result__;\
-})
-
namespace {
typedef testing::Test LinuxDumperTest;
+
+string GetHelperBinary() {
+ // Locate helper binary next to the current binary.
+ char self_path[PATH_MAX];
+ if (readlink("/proc/self/exe", self_path, sizeof(self_path) - 1) == -1) {
+ return "";
+ }
+ string helper_path(self_path);
+ size_t pos = helper_path.rfind('/');
+ if (pos == string::npos) {
+ return "";
+ }
+ helper_path.erase(pos + 1);
+ helper_path += "linux_dumper_unittest_helper";
+
+ return helper_path;
+}
+
}
TEST(LinuxDumperTest, Setup) {
@@ -83,7 +97,80 @@
}
}
-// Comment out this test due to crosbug.com/6712. Only seems to
+// Helper stack class to close a file descriptor and unmap
+// a mmap'ed mapping.
+class StackHelper {
+public:
+ StackHelper(int fd, char* mapping, size_t size)
+ : fd_(fd), mapping_(mapping), size_(size) {}
+ ~StackHelper() {
+ munmap(mapping_, size_);
+ close(fd_);
+ }
+
+private:
+ int fd_;
+ char* mapping_;
+ size_t size_;
+};
+
+TEST(LinuxDumperTest, MergedMappings) {
+ string 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.
+ const size_t kPageSize = sysconf(_SC_PAGESIZE);
+ const size_t kMappingSize = 3 * kPageSize;
+ int fd = open(helper_path.c_str(), O_RDONLY);
+ ASSERT_NE(-1, fd);
+ char* mapping =
+ reinterpret_cast<char*>(mmap(NULL,
+ kMappingSize,
+ PROT_READ,
+ MAP_SHARED,
+ fd,
+ 0));
+ ASSERT_TRUE(mapping);
+
+ const u_int64_t kMappingAddress = reinterpret_cast<u_int64_t>(mapping);
+
+ // Ensure that things get cleaned up.
+ StackHelper helper(fd, mapping, kMappingSize);
+
+ // Carve a page out of the first mapping with different permissions.
+ char* inside_mapping = reinterpret_cast<char*>(mmap(mapping + 2 *kPageSize,
+ kPageSize,
+ PROT_NONE,
+ MAP_SHARED | MAP_FIXED,
+ fd,
+ // Map a different offset just to
+ // better test real-world conditions.
+ kPageSize));
+ ASSERT_TRUE(inside_mapping);
+
+ // Now check that LinuxDumper interpreted the mappings properly.
+ LinuxDumper dumper(getpid());
+ 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, helper_path.c_str()) == 0) {
+ // This mapping should encompass the entire original mapped
+ // range.
+ EXPECT_EQ(kMappingAddress, mapping.start_addr);
+ EXPECT_EQ(kMappingSize, mapping.size);
+ EXPECT_EQ(0, mapping.offset);
+ mapping_count++;
+ }
+ }
+ EXPECT_EQ(1, mapping_count);
+}
+
+// Comment out this test due to crosbug.com/6757. Only seems to
// fail on heavily loaded buildbots and is written with timing
// assumptions.
#if 0
@@ -92,26 +179,26 @@
char kNumberOfThreadsArgument[2];
sprintf(kNumberOfThreadsArgument, "%d", kNumberOfThreadsInHelperProgram);
+ int fds[2];
+ ASSERT_NE(-1, pipe(fds));
+
pid_t child_pid = fork();
if (child_pid == 0) {
- // Locate helper binary next to the current binary.
- char self_path[PATH_MAX];
- if (readlink("/proc/self/exe", self_path, sizeof(self_path) - 1) == -1) {
- FAIL() << "readlink failed: " << strerror(errno);
- exit(1);
- }
- string helper_path(self_path);
- size_t pos = helper_path.rfind('/');
- if (pos == string::npos) {
- FAIL() << "no trailing slash in path: " << helper_path;
- exit(1);
- }
- helper_path.erase(pos + 1);
- helper_path += "linux_dumper_unittest_helper";
+ // In child process.
+ close(fds[0]);
- // Set the number of threads
+ 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.
@@ -119,9 +206,21 @@
FAIL() << "Exec of " << helper_path << " failed: " << strerror(errno);
exit(0);
}
- // The sleep is flaky, but prevents us from reading
- // the child process before all threads have been created.
- sleep(1);
+ 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;
+ read(fds[0], &junk, sizeof(junk));
+ close(fds[0]);
+
+ // Child is ready now.
LinuxDumper dumper(child_pid);
ASSERT_TRUE(dumper.Init());
EXPECT_EQ((size_t)kNumberOfThreadsInHelperProgram, dumper.threads().size());
@@ -182,6 +281,7 @@
}
#if !defined(__ARM_EABI__)
+// Ensure that the linux-gate VDSO is included in the mapping list.
TEST(LinuxDumperTest, MappingsIncludeLinuxGate) {
LinuxDumper dumper(getpid());
ASSERT_TRUE(dumper.Init());
@@ -203,6 +303,80 @@
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(LinuxDumperTest, LinuxGateMappingID) {
+ LinuxDumper dumper(getpid());
+ 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);
+
+ uint8_t identifier[sizeof(MDGUID)];
+ ASSERT_TRUE(dumper.ElfFileIdentifierForMapping(*mappings[index],
+ true,
+ index,
+ identifier));
+ uint8_t empty_identifier[sizeof(MDGUID)];
+ memset(empty_identifier, 0, sizeof(empty_identifier));
+ EXPECT_NE(0, memcmp(empty_identifier, identifier, sizeof(identifier)));
+}
+
+// Ensure that the linux-gate VDSO can generate a non-zeroed File ID
+// from a child process.
+TEST(LinuxDumperTest, LinuxGateMappingIDChild) {
+ int fds[2];
+ ASSERT_NE(-1, pipe(fds));
+
+ // Fork a child so ptrace works.
+ const pid_t child = fork();
+ if (child == 0) {
+ close(fds[1]);
+ // Now wait forever for the parent.
+ char b;
+ HANDLE_EINTR(read(fds[0], &b, sizeof(b)));
+ close(fds[0]);
+ syscall(__NR_exit);
+ }
+ close(fds[0]);
+
+ LinuxDumper dumper(child);
+ 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());
+ uint8_t identifier[sizeof(MDGUID)];
+ ASSERT_TRUE(dumper.ElfFileIdentifierForMapping(*mappings[index],
+ true,
+ index,
+ identifier));
+ uint8_t empty_identifier[sizeof(MDGUID)];
+ memset(empty_identifier, 0, sizeof(empty_identifier));
+ EXPECT_NE(0, memcmp(empty_identifier, identifier, sizeof(identifier)));
+ EXPECT_TRUE(dumper.ThreadsResume());
+ close(fds[1]);
+}
#endif
TEST(LinuxDumperTest, FileIDsMatch) {
@@ -217,11 +391,11 @@
int fds[2];
ASSERT_NE(-1, pipe(fds));
- // fork a child so we can ptrace it
+ // Fork a child so ptrace works.
const pid_t child = fork();
if (child == 0) {
close(fds[1]);
- // now wait forever for the parent
+ // Now wait forever for the parent.
char b;
HANDLE_EINTR(read(fds[0], &b, sizeof(b)));
close(fds[0]);
@@ -245,7 +419,8 @@
uint8_t identifier1[sizeof(MDGUID)];
uint8_t identifier2[sizeof(MDGUID)];
- EXPECT_TRUE(dumper.ElfFileIdentifierForMapping(i, identifier1));
+ EXPECT_TRUE(dumper.ElfFileIdentifierForMapping(*mappings[i], true, i,
+ identifier1));
FileID fileid(exe_name);
EXPECT_TRUE(fileid.ElfFileIdentifier(identifier2));
char identifier_string1[37];