diff options
author | benchan@chromium.org <benchan@chromium.org@4c0a9323-5329-0410-9bdc-e9ce6186880e> | 2012-01-19 07:14:51 +0000 |
---|---|---|
committer | benchan@chromium.org <benchan@chromium.org@4c0a9323-5329-0410-9bdc-e9ce6186880e> | 2012-01-19 07:14:51 +0000 |
commit | 30566abed8d95b752f31f67fde94c0ae0a1502d2 (patch) | |
tree | 90f98f074cebf21102ad8f8717a2e70e9f4438a9 /src/client/linux/minidump_writer/linux_dumper.cc | |
parent | Skip ElfCoreDumpTest.ValidCoreFile test if no core dump is generated. (diff) | |
download | breakpad-30566abed8d95b752f31f67fde94c0ae0a1502d2.tar.xz |
Implement core dump to minidump conversion.
This patch is part of a bigger patch that helps merging the breakpad code
with the modified version in Chromium OS.
Specifically, this patch makes the following changes:
1. Turn the LinuxDumper class into a base class and move ptrace related
code into a new derived class, LinuxPtraceDumper.
2. Add a LinuxCoreDumper class, which is derived from LinuxDumper, to
extract information from a crashed process via a core dump file instead
of ptrace.
3. Add a WriteMinidumpFromCore function to
src/client/linux/minidump_writer/minidump_writer.h,
which uses LinuxCoreDumper to extract information from a core dump file.
4. Add a core2md utility, which simply wraps WriteMinidumpFromCore, for
converting a core dump to a minidump.
BUG=455
TEST=Tested the following:
1. Build on 32-bit and 64-bit Linux with gcc 4.4.3 and gcc 4.6.
2. Build on Mac OS X 10.6.8 with gcc 4.2 and clang 3.0 (with latest gmock).
3. All unit tests pass.
4. Run Chromium OS tests to test core2md.
Review URL: http://breakpad.appspot.com/343001
git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@905 4c0a9323-5329-0410-9bdc-e9ce6186880e
Diffstat (limited to 'src/client/linux/minidump_writer/linux_dumper.cc')
-rw-r--r-- | src/client/linux/minidump_writer/linux_dumper.cc | 275 |
1 files changed, 9 insertions, 266 deletions
diff --git a/src/client/linux/minidump_writer/linux_dumper.cc b/src/client/linux/minidump_writer/linux_dumper.cc index e1eb9847..2cf1b40f 100644 --- a/src/client/linux/minidump_writer/linux_dumper.cc +++ b/src/client/linux/minidump_writer/linux_dumper.cc @@ -27,6 +27,9 @@ // (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_dumper.cc: Implement google_breakpad::LinuxDumper. +// See linux_dumper.h for details. + // This code deals with the mechanics of getting information about a crashed // process. Since this code may run in a compromised address space, the same // rules apply as detailed at the top of minidump_writer.h: no libc calls and @@ -34,25 +37,12 @@ #include "client/linux/minidump_writer/linux_dumper.h" -#include <asm/ptrace.h> #include <assert.h> -#include <errno.h> #include <fcntl.h> #include <limits.h> -#if !defined(__ANDROID__) -#include <link.h> -#endif #include <stddef.h> -#include <stdlib.h> -#include <stdio.h> #include <string.h> -#include <sys/ptrace.h> -#include <sys/wait.h> -#include <unistd.h> - -#include <algorithm> -#include "client/linux/minidump_writer/directory_reader.h" #include "client/linux/minidump_writer/line_reader.h" #include "common/linux/file_id.h" #include "common/linux/linux_libc_support.h" @@ -63,48 +53,6 @@ static const char kMappedFileUnsafePrefix[] = "/dev/"; static const char kDeletedSuffix[] = " (deleted)"; -// Suspend a thread by attaching to it. -static bool SuspendThread(pid_t pid) { - // This may fail if the thread has just died or debugged. - errno = 0; - if (sys_ptrace(PTRACE_ATTACH, pid, NULL, NULL) != 0 && - errno != 0) { - return false; - } - while (sys_waitpid(pid, NULL, __WALL) < 0) { - if (errno != EINTR) { - sys_ptrace(PTRACE_DETACH, pid, NULL, NULL); - return false; - } - } -#if defined(__i386) || defined(__x86_64) - // On x86, the stack pointer is NULL or -1, when executing trusted code in - // the seccomp sandbox. Not only does this cause difficulties down the line - // when trying to dump the thread's stack, it also results in the minidumps - // containing information about the trusted threads. This information is - // generally completely meaningless and just pollutes the minidumps. - // We thus test the stack pointer and exclude any threads that are part of - // the seccomp sandbox's trusted code. - user_regs_struct regs; - if (sys_ptrace(PTRACE_GETREGS, pid, NULL, ®s) == -1 || -#if defined(__i386) - !regs.esp -#elif defined(__x86_64) - !regs.rsp -#endif - ) { - sys_ptrace(PTRACE_DETACH, pid, NULL, NULL); - return false; - } -#endif - return true; -} - -// Resume a thread by detaching from it. -static bool ResumeThread(pid_t pid) { - return sys_ptrace(PTRACE_DETACH, pid, NULL, NULL) >= 0; -} - inline static bool IsMappedFileOpenUnsafe( const google_breakpad::MappingInfo& mapping) { // It is unsafe to attempt to open a mapped file that lives under /dev, @@ -123,7 +71,6 @@ LinuxDumper::LinuxDumper(pid_t pid) crash_address_(0), crash_signal_(0), crash_thread_(0), - threads_suspended_(false), threads_(&allocator_, 8), mappings_(&allocator_) { } @@ -135,80 +82,6 @@ bool LinuxDumper::Init() { return EnumerateThreads() && EnumerateMappings(); } -bool LinuxDumper::ThreadsSuspend() { - if (threads_suspended_) - return true; - for (size_t i = 0; i < threads_.size(); ++i) { - if (!SuspendThread(threads_[i])) { - // If the thread either disappeared before we could attach to it, or if - // it was part of the seccomp sandbox's trusted code, it is OK to - // silently drop it from the minidump. - memmove(&threads_[i], &threads_[i+1], - (threads_.size() - i - 1) * sizeof(threads_[i])); - threads_.resize(threads_.size() - 1); - --i; - } - } - threads_suspended_ = true; - return threads_.size() > 0; -} - -bool LinuxDumper::ThreadsResume() { - if (!threads_suspended_) - return false; - bool good = true; - for (size_t i = 0; i < threads_.size(); ++i) - good &= ResumeThread(threads_[i]); - threads_suspended_ = false; - 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); - path[total_length] = '\0'; -} - bool LinuxDumper::ElfFileIdentifierForMapping(const MappingInfo& mapping, bool member, @@ -261,10 +134,8 @@ LinuxDumper::ElfFileIdentifierForMapping(const MappingInfo& mapping, void* LinuxDumper::FindBeginningOfLinuxGateSharedLibrary(const pid_t pid) const { char auxv_path[NAME_MAX]; - 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. + if (!BuildProcPath(auxv_path, pid, "auxv")) + return NULL; // Find the AT_SYSINFO_EHDR entry for linux-gate.so // See http://www.trilithium.com/johan/2005/08/linux-gate/ for more @@ -290,7 +161,8 @@ LinuxDumper::FindBeginningOfLinuxGateSharedLibrary(const pid_t pid) const { bool LinuxDumper::EnumerateMappings() { char maps_path[NAME_MAX]; - BuildProcPath(maps_path, pid_, "maps"); + if (!BuildProcPath(maps_path, pid_, "maps")) + return false; // 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 @@ -359,118 +231,6 @@ bool LinuxDumper::EnumerateMappings() { return !mappings_.empty(); } -// Parse /proc/$pid/task to list all the threads of the process identified by -// pid. -bool LinuxDumper::EnumerateThreads() { - char task_path[NAME_MAX]; - 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); - - // The directory may contain duplicate entries which we filter by assuming - // that they are consecutive. - int last_tid = -1; - const char* dent_name; - while (dir_reader->GetNextEntry(&dent_name)) { - if (my_strcmp(dent_name, ".") && - my_strcmp(dent_name, "..")) { - int tid = 0; - if (my_strtoui(&tid, dent_name) && - last_tid != tid) { - last_tid = tid; - threads_.push_back(tid); - } - } - dir_reader->PopEntry(); - } - - sys_close(fd); - return true; -} - -// Read thread info from /proc/$pid/status. -// Fill out the |tgid|, |ppid| and |pid| members of |info|. If unavailable, -// these members are set to -1. Returns true iff all three members are -// available. -bool LinuxDumper::GetThreadInfoByIndex(size_t index, ThreadInfo* info) { - if (index >= threads_.size()) - return false; - - pid_t tid = threads_[index]; - - assert(info != NULL); - char status_path[NAME_MAX]; - BuildProcPath(status_path, tid, "status"); - - const int fd = sys_open(status_path, O_RDONLY, 0); - if (fd < 0) - return false; - - LineReader* const line_reader = new(allocator_) LineReader(fd); - const char* line; - unsigned line_len; - - info->ppid = info->tgid = -1; - - while (line_reader->GetNextLine(&line, &line_len)) { - if (my_strncmp("Tgid:\t", line, 6) == 0) { - my_strtoui(&info->tgid, line + 6); - } else if (my_strncmp("PPid:\t", line, 6) == 0) { - my_strtoui(&info->ppid, line + 6); - } - - line_reader->PopLine(line_len); - } - - if (info->ppid == -1 || info->tgid == -1) - return false; - - if (sys_ptrace(PTRACE_GETREGS, tid, NULL, &info->regs) == -1) { - return false; - } - -#if !defined(__ANDROID__) - if (sys_ptrace(PTRACE_GETFPREGS, tid, NULL, &info->fpregs) == -1) { - return false; - } -#endif - -#if defined(__i386) - if (sys_ptrace(PTRACE_GETFPXREGS, tid, NULL, &info->fpxregs) == -1) - return false; -#endif - -#if defined(__i386) || defined(__x86_64) - for (unsigned i = 0; i < ThreadInfo::kNumDebugRegisters; ++i) { - if (sys_ptrace( - PTRACE_PEEKUSER, tid, - 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; -#if defined(__i386) - memcpy(&stack_pointer, &info->regs.esp, sizeof(info->regs.esp)); -#elif defined(__x86_64) - memcpy(&stack_pointer, &info->regs.rsp, sizeof(info->regs.rsp)); -#elif defined(__ARM_EABI__) - memcpy(&stack_pointer, &info->regs.ARM_sp, sizeof(info->regs.ARM_sp)); -#else -#error "This code hasn't been ported to your platform yet." -#endif - - return GetStackInfo(&info->stack, &info->stack_len, - (uintptr_t) stack_pointer); -} - // Get information about the stack, given the stack pointer. We don't try to // walk the stack since we might not have all the information needed to do // unwind. So we just grab, up to, 32k of stack. @@ -497,24 +257,6 @@ bool LinuxDumper::GetStackInfo(const void** stack, size_t* stack_len, return true; } -void LinuxDumper::CopyFromProcess(void* dest, pid_t child, const void* src, - size_t length) { - unsigned long tmp = 55; - size_t done = 0; - static const size_t word_size = sizeof(tmp); - uint8_t* const local = (uint8_t*) dest; - uint8_t* const remote = (uint8_t*) src; - - while (done < length) { - const size_t l = length - done > word_size ? word_size : length - done; - if (sys_ptrace(PTRACE_PEEKDATA, child, remote + done, &tmp) == -1) { - tmp = 0; - } - memcpy(local + done, &tmp, l); - done += l; - } -} - // Find the mapping which the given memory address falls in. const MappingInfo* LinuxDumper::FindMapping(const void* address) const { const uintptr_t addr = (uintptr_t) address; @@ -544,7 +286,8 @@ bool LinuxDumper::HandleDeletedFileInMapping(char* path) const { // Check |path| against the /proc/pid/exe 'symlink'. char exe_link[NAME_MAX]; char new_path[NAME_MAX]; - BuildProcPath(exe_link, pid_, "exe"); + if (!BuildProcPath(exe_link, pid_, "exe")) + return false; if (!SafeReadLink(exe_link, new_path)) return false; if (my_strcmp(path, new_path) != 0) |