summaryrefslogtreecommitdiffstats
path: root/breakpad
diff options
context:
space:
mode:
authoragl@chromium.org <agl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-06-25 01:25:54 +0000
committeragl@chromium.org <agl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-06-25 01:25:54 +0000
commitbaedff6a7b5271b4b5319371b1c2d4ad47ee6a63 (patch)
tree1d99a00bd98f51ed16cc2bbd274b395ce1a1234c /breakpad
parentf808dd458cc05ad47de5448669daaeae6bb236c9 (diff)
downloadchromium_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.cc154
-rw-r--r--breakpad/linux/linux_dumper.h37
-rw-r--r--breakpad/linux/linux_dumper_unittest.cc52
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));
+ }
+}