diff options
author | agl@chromium.org <agl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-06-25 01:25:54 +0000 |
---|---|---|
committer | agl@chromium.org <agl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-06-25 01:25:54 +0000 |
commit | baedff6a7b5271b4b5319371b1c2d4ad47ee6a63 (patch) | |
tree | 1d99a00bd98f51ed16cc2bbd274b395ce1a1234c /breakpad | |
parent | f808dd458cc05ad47de5448669daaeae6bb236c9 (diff) | |
download | chromium_src-baedff6a7b5271b4b5319371b1c2d4ad47ee6a63.zip chromium_src-baedff6a7b5271b4b5319371b1c2d4ad47ee6a63.tar.gz chromium_src-baedff6a7b5271b4b5319371b1c2d4ad47ee6a63.tar.bz2 |
Linux: add linux-gate.so to the minidump mappings.
Add linux-gate.so to the minidump mappings list by checking for the
load address in the aux vector and adding a filename of linux-gate.so
when we encounter that load address. This is necessary for crash
reporting to walk the stack correctly when a crash happens while in a
system call.
http://codereview.chromium.org/147032
Committed for Neal Sidhwaney.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@19214 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'breakpad')
-rw-r--r-- | breakpad/linux/linux_dumper.cc | 154 | ||||
-rw-r--r-- | breakpad/linux/linux_dumper.h | 37 | ||||
-rw-r--r-- | breakpad/linux/linux_dumper_unittest.cc | 52 |
3 files changed, 201 insertions, 42 deletions
diff --git a/breakpad/linux/linux_dumper.cc b/breakpad/linux/linux_dumper.cc index a40f7cd..3dc64db 100644 --- a/breakpad/linux/linux_dumper.cc +++ b/breakpad/linux/linux_dumper.cc @@ -110,73 +110,158 @@ bool LinuxDumper::ThreadsResume() { return good; } +void +LinuxDumper::BuildProcPath(char* path, pid_t pid, const char* node) const { + assert(path); + if (!path) { + return; + } + + path[0] = '\0'; + + const unsigned pid_len = my_int_len(pid); + + assert(node); + if (!node) { + return; + } + + size_t node_len = my_strlen(node); + assert(node_len < NAME_MAX); + if (node_len >= NAME_MAX) { + return; + } + + assert(node_len > 0); + if (node_len == 0) { + return; + } + + assert(pid > 0); + if (pid <= 0) { + return; + } + + const size_t total_length = 6 + pid_len + 1 + node_len; + + assert(total_length < NAME_MAX); + if (total_length >= NAME_MAX) { + return; + } + + memcpy(path, "/proc/", 6); + my_itos(path + 6, pid, pid_len); + memcpy(path + 6 + pid_len, "/", 1); + memcpy(path + 6 + pid_len + 1, node, node_len); + memcpy(path + total_length, "\0", 1); +} + +void* +LinuxDumper::FindBeginningOfLinuxGateSharedLibrary(const pid_t pid) const { + char auxv_path[80]; + BuildProcPath(auxv_path, pid, "auxv"); + + // If BuildProcPath errors out due to invalid input, we'll handle it when + // we try to sys_open the file. + + // Find the AT_SYSINFO_EHDR entry for linux-gate.so + // See http://www.trilithium.com/johan/2005/08/linux-gate/ for more + // information. + int fd = sys_open(auxv_path, O_RDONLY, 0); + if (fd < 0) { + return NULL; + } + + elf_aux_entry one_aux_entry; + while (sys_read(fd, + &one_aux_entry, + sizeof(elf_aux_entry)) == sizeof(elf_aux_entry) && + one_aux_entry.a_type != AT_NULL) { + if (one_aux_entry.a_type == AT_SYSINFO_EHDR) { + close(fd); + return reinterpret_cast<void*>(one_aux_entry.a_un.a_val); + } + } + close(fd); + return NULL; +} + bool -LinuxDumper::EnumerateMappings(wasteful_vector<MappingInfo*> *result) const { +LinuxDumper::EnumerateMappings(wasteful_vector<MappingInfo*>* result) const { char maps_path[80]; - memcpy(maps_path, "/proc/", 6); - const unsigned pid_len = my_int_len(pid_); - my_itos(maps_path + 6, pid_, pid_len); - memcpy(maps_path + 6 + pid_len, "/maps", 6); + BuildProcPath(maps_path, pid_, "maps"); + + // linux_gate_loc is the beginning of the kernel's mapping of + // linux-gate.so in the process. It doesn't actually show up in the + // maps list as a filename, so we use the aux vector to find it's + // load location and special case it's entry when creating the list + // of mappings. + const void* linux_gate_loc; + linux_gate_loc = FindBeginningOfLinuxGateSharedLibrary(pid_); const int fd = sys_open(maps_path, O_RDONLY, 0); if (fd < 0) return false; - LineReader *const line_reader = new(allocator_) LineReader(fd); + LineReader* const line_reader = new(allocator_) LineReader(fd); - const char *line; + const char* line; unsigned line_len; while (line_reader->GetNextLine(&line, &line_len)) { uintptr_t start_addr, end_addr, offset; const char* i1 = my_read_hex_ptr(&start_addr, line); if (*i1 == '-') { - const char *i2 = my_read_hex_ptr(&end_addr, i1 + 1); + const char* i2 = my_read_hex_ptr(&end_addr, i1 + 1); if (*i2 == ' ') { - const char *i3 = my_read_hex_ptr(&offset, i2 + 6 /* skip ' rwxp ' */); + const char* i3 = my_read_hex_ptr(&offset, i2 + 6 /* skip ' rwxp ' */); if (*i3 == ' ') { - MappingInfo *const module = new(allocator_) MappingInfo; + 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. + const char* name = NULL; + // Only copy name if the name is a valid path name, or if + // we've found the VDSO image if ((name = my_strchr(line, '/')) != 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); } } } - line_reader->PopLine(line_len); } sys_close(fd); + return result->size() > 0; } // Parse /proc/$pid/task to list all the threads of the process identified by // pid. -bool LinuxDumper::EnumerateThreads(wasteful_vector<pid_t> *result) const { +bool LinuxDumper::EnumerateThreads(wasteful_vector<pid_t>* result) const { char task_path[80]; - memcpy(task_path, "/proc/", 6); - const unsigned pid_len = my_int_len(pid_); - my_itos(task_path + 6, pid_, pid_len); - memcpy(task_path + 6 + pid_len, "/task", 6); + BuildProcPath(task_path, pid_, "task"); const int fd = sys_open(task_path, O_RDONLY | O_DIRECTORY, 0); if (fd < 0) return false; - DirectoryReader *dir_reader = new(allocator_) DirectoryReader(fd); + DirectoryReader* dir_reader = new(allocator_) DirectoryReader(fd); // The directory may contain duplicate entries which we filter by assuming - // that they are consecutive + // that they are consecutive. int last_tid = -1; - const char *dent_name; + const char* dent_name; while (dir_reader->GetNextEntry(&dent_name)) { if (my_strcmp(dent_name, ".") && my_strcmp(dent_name, "..")) { @@ -198,19 +283,17 @@ bool LinuxDumper::EnumerateThreads(wasteful_vector<pid_t> *result) const { // Fill out the |tgid|, |ppid| and |pid| members of |info|. If unavailible, // these members are set to -1. Returns true iff all three members are // availible. -bool LinuxDumper::ThreadInfoGet(pid_t tid, ThreadInfo *info) { +bool LinuxDumper::ThreadInfoGet(pid_t tid, ThreadInfo* info) { assert(info != NULL); char status_path[80]; - memcpy(status_path, "/proc/", 6); - const unsigned tid_len = my_int_len(tid); - my_itos(status_path + 6, tid, tid_len); - memcpy(status_path + 6 + tid_len, "/status", 8); + BuildProcPath(status_path, tid, "status"); + const int fd = open(status_path, O_RDONLY); if (fd < 0) return false; - LineReader *const line_reader = new(allocator_) LineReader(fd); - const char *line; + LineReader* const line_reader = new(allocator_) LineReader(fd); + const char* line; unsigned line_len; info->ppid = info->tgid = -1; @@ -240,15 +323,16 @@ bool LinuxDumper::ThreadInfoGet(pid_t tid, ThreadInfo *info) { for (unsigned i = 0; i < ThreadInfo::kNumDebugRegisters; ++i) { if (sys_ptrace( PTRACE_PEEKUSER, tid, - (void *) (offsetof(struct user, - u_debugreg[0]) + i * sizeof(debugreg_t)), + reinterpret_cast<void*> (offsetof(struct user, + u_debugreg[0]) + i * + sizeof(debugreg_t)), &info->dregs[i]) == -1) { return false; } } #endif - const uint8_t *stack_pointer; + const uint8_t* stack_pointer; #if defined(__i386) memcpy(&stack_pointer, &info->regs.esp, sizeof(info->regs.esp)); #elif defined(__x86_64) @@ -277,12 +361,12 @@ bool LinuxDumper::GetStackInfo(const void** stack, size_t* stack_len, #endif // Move the stack pointer to the bottom of the page that it's in. uint8_t* const stack_pointer = - (uint8_t *) (int_stack_pointer & ~(page_size - 1)); + reinterpret_cast<uint8_t*>(int_stack_pointer & ~(page_size - 1)); // The number of bytes of stack which we try to capture. static unsigned kStackToCapture = 32 * 1024; - const MappingInfo *mapping = FindMapping(stack_pointer); + const MappingInfo* mapping = FindMapping(stack_pointer); if (!mapping) return false; if (stack_grows_down) { @@ -320,7 +404,7 @@ void LinuxDumper::CopyFromProcess(void* dest, pid_t child, const void* src, } // Find the mapping which the given memory address falls in. -const MappingInfo *LinuxDumper::FindMapping(const void *address) const { +const MappingInfo* LinuxDumper::FindMapping(const void* address) const { const uintptr_t addr = (uintptr_t) address; for (size_t i = 0; i < mappings_.size(); ++i) { diff --git a/breakpad/linux/linux_dumper.h b/breakpad/linux/linux_dumper.h index b6fc58a..ac4a470 100644 --- a/breakpad/linux/linux_dumper.h +++ b/breakpad/linux/linux_dumper.h @@ -30,6 +30,7 @@ #ifndef CLIENT_LINUX_HANDLER_LINUX_DUMPER_H_ #define CLIENT_LINUX_HANDLER_LINUX_DUMPER_H_ +#include <elf.h> #include <stdint.h> #include <sys/user.h> #include <linux/limits.h> @@ -38,7 +39,18 @@ namespace google_breakpad { -typedef typeof(((struct user *) 0)->u_debugreg[0]) debugreg_t; +typedef typeof(((struct user*) 0)->u_debugreg[0]) debugreg_t; + +// Typedef for our parsing of the auxv variables in /proc/pid/auxv. +#if defined(__i386) +typedef Elf32_auxv_t elf_aux_entry; +#elif defined(__x86_64__) +typedef Elf64_auxv_t elf_aux_entry; +#endif +// When we find the VDSO mapping in the process's address space, this +// is the name we use for it when writing it to the minidump. +// This should always be less than NAME_MAX! +const char kLinuxGateLibraryName[] = "linux-gate.so"; // We produce one of these structures for each thread in the crashed process. struct ThreadInfo { @@ -47,7 +59,7 @@ struct ThreadInfo { // Even on platforms where the stack grows down, the following will point to // the smallest address in the stack. - const void *stack; // pointer to the stack area + const void* stack; // pointer to the stack area size_t stack_len; // length of the stack to copy user_regs_struct regs; @@ -82,12 +94,12 @@ class LinuxDumper { // Read information about the given thread. Returns true on success. One must // have called |ThreadsSuspend| first. - bool ThreadInfoGet(pid_t tid, ThreadInfo *info); + bool ThreadInfoGet(pid_t tid, ThreadInfo* info); // These are only valid after a call to |Init|. const wasteful_vector<pid_t> &threads() { return threads_; } const wasteful_vector<MappingInfo*> &mappings() { return mappings_; } - const MappingInfo *FindMapping(const void *address) const; + const MappingInfo* FindMapping(const void* address) const; // Find a block of memory to take as the stack given the top of stack pointer. // stack: (output) the lowest address in the memory area @@ -95,15 +107,26 @@ class LinuxDumper { // stack_top: the current top of the stack bool GetStackInfo(const void** stack, size_t* stack_len, uintptr_t stack_top); - PageAllocator *allocator() { return &allocator_; } + PageAllocator* allocator() { return &allocator_; } // memcpy from a remote process. static void CopyFromProcess(void* dest, pid_t child, const void* src, size_t length); + // Builds a proc path for a certain pid for a node. path is a + // character array that is overwritten, and node is the final node + // without any slashes. + void BuildProcPath(char* path, pid_t pid, const char* node) const; + + // Utility method to find the location of where the kernel has + // mapped linux-gate.so in memory(shows up in /proc/pid/maps as + // [vdso], but we can't guarantee that it's the only virtual dynamic + // shared object. Parsing the auxilary vector for AT_SYSINFO_EHDR + // is the safest way to go.) + void* FindBeginningOfLinuxGateSharedLibrary(const pid_t pid) const; private: - bool EnumerateMappings(wasteful_vector<MappingInfo*> *result) const; - bool EnumerateThreads(wasteful_vector<pid_t> *result) const; + bool EnumerateMappings(wasteful_vector<MappingInfo*>* result) const; + bool EnumerateThreads(wasteful_vector<pid_t>* result) const; const pid_t pid_; diff --git a/breakpad/linux/linux_dumper_unittest.cc b/breakpad/linux/linux_dumper_unittest.cc index 692c696..34b4e94 100644 --- a/breakpad/linux/linux_dumper_unittest.cc +++ b/breakpad/linux/linux_dumper_unittest.cc @@ -64,3 +64,55 @@ TEST(LinuxDumperTest, ThreadList) { } } } + +TEST(LinuxDumperTest, BuildProcPath) { + const pid_t pid = getpid(); + LinuxDumper dumper(pid); + + char maps_path[256] = "dummymappath"; + char maps_path_expected[256]; + snprintf(maps_path_expected, sizeof(maps_path_expected), + "/proc/%d/maps", pid); + dumper.BuildProcPath(maps_path, pid, "maps"); + ASSERT_STREQ(maps_path, maps_path_expected); + + // In release mode, we expect BuildProcPath to handle the invalid + // parameters correctly and fill map_path with an empty + // NULL-terminated string. +#ifdef NDEBUG + snprintf(maps_path, sizeof(maps_path), "dummymappath"); + dumper.BuildProcPath(maps_path, 0, "maps"); + EXPECT_STREQ(maps_path, ""); + + snprintf(maps_path, sizeof(maps_path), "dummymappath"); + dumper.BuildProcPath(maps_path, getpid(), ""); + EXPECT_STREQ(maps_path, ""); + + snprintf(maps_path, sizeof(maps_path), "dummymappath"); + dumper.BuildProcPath(maps_path, getpid(), NULL); + EXPECT_STREQ(maps_path, ""); +#endif +} + +TEST(LinuxDumperTest, MappingsIncludeLinuxGate) { + LinuxDumper dumper(getpid()); + ASSERT_TRUE(dumper.Init()); + + void* linux_gate_loc = dumper.FindBeginningOfLinuxGateSharedLibrary(getpid()); + if (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)); + } +} |