diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/client/linux/minidump_writer/linux_dumper.cc | 33 | ||||
-rw-r--r-- | src/client/linux/minidump_writer/linux_dumper_unittest.cc | 107 |
2 files changed, 117 insertions, 23 deletions
diff --git a/src/client/linux/minidump_writer/linux_dumper.cc b/src/client/linux/minidump_writer/linux_dumper.cc index 4a4b5e72..88900f9b 100644 --- a/src/client/linux/minidump_writer/linux_dumper.cc +++ b/src/client/linux/minidump_writer/linux_dumper.cc @@ -306,25 +306,36 @@ LinuxDumper::EnumerateMappings(wasteful_vector<MappingInfo*>* result) const { if (*i2 == ' ') { const char* i3 = my_read_hex_ptr(&offset, i2 + 6 /* skip ' rwxp ' */); if (*i3 == ' ') { + const char* name = NULL; + // Only copy name if the name is a valid path name, or if + // it's the VDSO image. + if (((name = my_strchr(line, '/')) == NULL) && + linux_gate_loc && + reinterpret_cast<void*>(start_addr) == linux_gate_loc) { + name = kLinuxGateLibraryName; + offset = 0; + } + // Merge adjacent mappings with the same name into one module, + // assuming they're a single library mapped by the dynamic linker + if (name && result->size()) { + MappingInfo* module = (*result)[result->size() - 1]; + if ((start_addr == module->start_addr + module->size) && + (my_strlen(name) == my_strlen(module->name)) && + (my_strncmp(name, module->name, my_strlen(name)) == 0)) { + module->size = end_addr - module->start_addr; + line_reader->PopLine(line_len); + continue; + } + } MappingInfo* const module = new(allocator_) MappingInfo; memset(module, 0, sizeof(MappingInfo)); module->start_addr = start_addr; module->size = end_addr - start_addr; module->offset = offset; - const char* name = NULL; - // Only copy name if the name is a valid path name, or if - // it's the VDSO image. - if ((name = my_strchr(line, '/')) != NULL) { + if (name != NULL) { const unsigned l = my_strlen(name); if (l < sizeof(module->name)) memcpy(module->name, name, l); - } else if (linux_gate_loc && - reinterpret_cast<void*>(module->start_addr) == - linux_gate_loc) { - memcpy(module->name, - kLinuxGateLibraryName, - my_strlen(kLinuxGateLibraryName)); - module->offset = 0; } result->push_back(module); } diff --git a/src/client/linux/minidump_writer/linux_dumper_unittest.cc b/src/client/linux/minidump_writer/linux_dumper_unittest.cc index a3e84dbc..33d9da7c 100644 --- a/src/client/linux/minidump_writer/linux_dumper_unittest.cc +++ b/src/client/linux/minidump_writer/linux_dumper_unittest.cc @@ -33,6 +33,7 @@ #include <unistd.h> #include <signal.h> #include <stdint.h> +#include <sys/mman.h> #include <sys/poll.h> #include <sys/types.h> @@ -47,6 +48,24 @@ using namespace google_breakpad; 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) { @@ -76,6 +95,79 @@ TEST(LinuxDumperTest, ThreadList) { } } +// 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); +} + TEST(LinuxDumperTest, VerifyStackReadWithMultipleThreads) { static const int kNumberOfThreadsInHelperProgram = 5; char kNumberOfThreadsArgument[2]; @@ -89,20 +181,11 @@ TEST(LinuxDumperTest, VerifyStackReadWithMultipleThreads) { // In child process. close(fds[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; + string helper_path(GetHelperBinary()); + if (helper_path.empty()) { + FAIL() << "Couldn't find helper binary"; exit(1); } - helper_path.erase(pos + 1); - helper_path += "linux_dumper_unittest_helper"; // Pass the pipe fd and the number of threads as arguments. char pipe_fd_string[8]; |