aboutsummaryrefslogtreecommitdiff
path: root/src/client/linux/handler/linux_thread.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/linux/handler/linux_thread.cc')
-rw-r--r--src/client/linux/handler/linux_thread.cc411
1 files changed, 0 insertions, 411 deletions
diff --git a/src/client/linux/handler/linux_thread.cc b/src/client/linux/handler/linux_thread.cc
index c8ac4926..e69de29b 100644
--- a/src/client/linux/handler/linux_thread.cc
+++ b/src/client/linux/handler/linux_thread.cc
@@ -1,411 +0,0 @@
-// Copyright (c) 2006, Google Inc.
-// All rights reserved.
-//
-// Author: Li Liu
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-//
-#include <errno.h>
-#include <dirent.h>
-#include <fcntl.h>
-#include <sys/ptrace.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <sys/wait.h>
-#include <string.h>
-
-#include <algorithm>
-#include <cassert>
-#include <cstdio>
-#include <cstdlib>
-#include <functional>
-
-#include "client/linux/handler/linux_thread.h"
-
-using namespace google_breakpad;
-
-// This unamed namespace contains helper function.
-namespace {
-
-// Context information for the callbacks when validating address by listing
-// modules.
-struct AddressValidatingContext {
- uintptr_t address;
- bool is_mapped;
-
- AddressValidatingContext() : address(0UL), is_mapped(false) {
- }
-};
-
-// Convert from string to int.
-bool LocalAtoi(char *s, int *r) {
- assert(s != NULL);
- assert(r != NULL);
- char *endptr = NULL;
- int ret = strtol(s, &endptr, 10);
- if (endptr == s)
- return false;
- *r = ret;
- return true;
-}
-
-// Fill the proc path of a thread given its id.
-void FillProcPath(int pid, char *path, int path_size) {
- char pid_str[32];
- snprintf(pid_str, sizeof(pid_str), "%d", pid);
- snprintf(path, path_size, "/proc/%s/", pid_str);
-}
-
-// Read thread info from /proc/$pid/status.
-bool ReadThreadInfo(int pid, ThreadInfo *info) {
- assert(info != NULL);
- char status_path[80];
- // Max size we want to read from status file.
- static const int kStatusMaxSize = 1024;
- char status_content[kStatusMaxSize];
-
- FillProcPath(pid, status_path, sizeof(status_path));
- strcat(status_path, "status");
- int fd = open(status_path, O_RDONLY, 0);
- if (fd < 0)
- return false;
-
- int num_read = read(fd, status_content, kStatusMaxSize - 1);
- if (num_read < 0) {
- close(fd);
- return false;
- }
- close(fd);
- status_content[num_read] = '\0';
-
- char *tgid_start = strstr(status_content, "Tgid:");
- if (tgid_start)
- sscanf(tgid_start, "Tgid:\t%d\n", &(info->tgid));
- else
- // tgid not supported by kernel??
- info->tgid = 0;
-
- tgid_start = strstr(status_content, "Pid:");
- if (tgid_start) {
- sscanf(tgid_start, "Pid:\t%d\n" "PPid:\t%d\n", &(info->pid),
- &(info->ppid));
- return true;
- }
- return false;
-}
-
-// Callback invoked for each mapped module.
-// It use the module's adderss range to validate the address.
-bool IsAddressInModuleCallback(const ModuleInfo &module_info,
- void *context) {
- AddressValidatingContext *addr =
- reinterpret_cast<AddressValidatingContext *>(context);
- addr->is_mapped = ((addr->address >= module_info.start_addr) &&
- (addr->address <= module_info.start_addr +
- module_info.size));
- return !addr->is_mapped;
-}
-
-#if defined(__i386__) && !defined(NO_FRAME_POINTER)
-void *GetNextFrame(void **last_ebp) {
- void *sp = *last_ebp;
- if ((unsigned long)sp == (unsigned long)last_ebp)
- return NULL;
- if ((unsigned long)sp & (sizeof(void *) - 1))
- return NULL;
- if ((unsigned long)sp - (unsigned long)last_ebp > 100000)
- return NULL;
- return sp;
-}
-#else
-void *GetNextFrame(void **last_ebp) {
- return reinterpret_cast<void*>(last_ebp);
-}
-#endif
-
-// Suspend a thread by attaching to it.
-bool SuspendThread(int pid, void *context) {
- // This may fail if the thread has just died or debugged.
- errno = 0;
- if (ptrace(PTRACE_ATTACH, pid, NULL, NULL) != 0 &&
- errno != 0) {
- return false;
- }
- while (waitpid(pid, NULL, __WALL) < 0) {
- if (errno != EINTR) {
- ptrace(PTRACE_DETACH, pid, NULL, NULL);
- return false;
- }
- }
- return true;
-}
-
-// Resume a thread by detaching from it.
-bool ResumeThread(int pid, void *context) {
- return ptrace(PTRACE_DETACH, pid, NULL, NULL) >= 0;
-}
-
-// Callback to get the thread information.
-// Will be called for each thread found.
-bool ThreadInfoCallback(int pid, void *context) {
- CallbackParam<ThreadCallback> *thread_callback =
- reinterpret_cast<CallbackParam<ThreadCallback> *>(context);
- ThreadInfo thread_info;
- if (ReadThreadInfo(pid, &thread_info) && thread_callback) {
- // Invoke callback from caller.
- return (thread_callback->call_back)(thread_info, thread_callback->context);
- }
- return false;
-}
-
-} // namespace
-
-namespace google_breakpad {
-
-LinuxThread::LinuxThread(int pid) : pid_(pid) , threads_suspened_(false) {
-}
-
-LinuxThread::~LinuxThread() {
- if (threads_suspened_)
- ResumeAllThreads();
-}
-
-int LinuxThread::SuspendAllThreads() {
- CallbackParam<PidCallback> callback_param(SuspendThread, NULL);
- int thread_count = 0;
- if ((thread_count = IterateProcSelfTask(pid_, &callback_param)) > 0)
- threads_suspened_ = true;
- return thread_count;
-}
-
-void LinuxThread::ResumeAllThreads() const {
- CallbackParam<PidCallback> callback_param(ResumeThread, NULL);
- IterateProcSelfTask(pid_, &callback_param);
-}
-
-int LinuxThread::GetThreadCount() const {
- return IterateProcSelfTask(pid_, NULL);
-}
-
-int LinuxThread::ListThreads(
- CallbackParam<ThreadCallback> *thread_callback_param) const {
- CallbackParam<PidCallback> callback_param(ThreadInfoCallback,
- thread_callback_param);
- return IterateProcSelfTask(pid_, &callback_param);
-}
-
-bool LinuxThread::GetRegisters(int pid, user_regs_struct *regs) const {
- assert(regs);
- return (regs != NULL &&
- (ptrace(PTRACE_GETREGS, pid, NULL, regs) == 0) &&
- errno == 0);
-}
-
-// Get the floating-point registers of a thread.
-// The caller must get the thread pid by ListThreads.
-bool LinuxThread::GetFPRegisters(int pid, user_fpregs_struct *regs) const {
- assert(regs);
- return (regs != NULL &&
- (ptrace(PTRACE_GETREGS, pid, NULL, regs) ==0) &&
- errno == 0);
-}
-
-bool LinuxThread::GetFPXRegisters(int pid, user_fpxregs_struct *regs) const {
- assert(regs);
- return (regs != NULL &&
- (ptrace(PTRACE_GETFPREGS, pid, NULL, regs) != 0) &&
- errno == 0);
-}
-
-bool LinuxThread::GetDebugRegisters(int pid, DebugRegs *regs) const {
- assert(regs);
-
-#define GET_DR(name, num)\
- name->dr##num = ptrace(PTRACE_PEEKUSER, pid,\
- offsetof(struct user, u_debugreg[num]), NULL)
- GET_DR(regs, 0);
- GET_DR(regs, 1);
- GET_DR(regs, 2);
- GET_DR(regs, 3);
- GET_DR(regs, 4);
- GET_DR(regs, 5);
- GET_DR(regs, 6);
- GET_DR(regs, 7);
- return true;
-}
-
-int LinuxThread::GetThreadStackDump(uintptr_t current_ebp,
- uintptr_t current_esp,
- void *buf,
- int buf_size) const {
- assert(buf);
- assert(buf_size > 0);
-
- uintptr_t stack_bottom = GetThreadStackBottom(current_ebp);
- int size = stack_bottom - current_esp;
- size = buf_size > size ? size : buf_size;
- if (size > 0)
- memcpy(buf, reinterpret_cast<void*>(current_esp), size);
- return size;
-}
-
-// Get the stack bottom of a thread by stack walking. It works
-// unless the stack has been corrupted or the frame pointer has been omited.
-// This is just a temporary solution before we get better ideas about how
-// this can be done.
-//
-// We will check each frame address by checking into module maps.
-// TODO(liuli): Improve it.
-uintptr_t LinuxThread::GetThreadStackBottom(uintptr_t current_ebp) const {
- void **sp = reinterpret_cast<void **>(current_ebp);
- void **previous_sp = sp;
- while (sp && IsAddressMapped((uintptr_t)sp)) {
- previous_sp = sp;
- sp = reinterpret_cast<void **>(GetNextFrame(sp));
- }
- return (uintptr_t)previous_sp;
-}
-
-int LinuxThread::GetModuleCount() const {
- return ListModules(NULL);
-}
-
-int LinuxThread::ListModules(
- CallbackParam<ModuleCallback> *callback_param) const {
- char line[512];
- const char *maps_path = "/proc/self/maps";
-
- int module_count = 0;
- FILE *fp = fopen(maps_path, "r");
- if (fp == NULL)
- return -1;
-
- uintptr_t start_addr;
- uintptr_t end_addr;
- while (fgets(line, sizeof(line), fp) != NULL) {
- if (sscanf(line, "%x-%x", &start_addr, &end_addr) == 2) {
- ModuleInfo module;
- memset(&module, 0, sizeof(module));
- module.start_addr = start_addr;
- module.size = end_addr - start_addr;
- char *name = NULL;
- assert(module.size > 0);
- // Only copy name if the name is a valid path name.
- if ((name = strchr(line, '/')) != NULL) {
- // Get rid of the last '\n' in line
- char *last_return = strchr(line, '\n');
- if (last_return != NULL)
- *last_return = '\0';
- // Keep a space for the ending 0.
- strncpy(module.name, name, sizeof(module.name) - 1);
- ++module_count;
- }
- if (callback_param &&
- !(callback_param->call_back(module, callback_param->context)))
- break;
- }
- }
- fclose(fp);
- return module_count;
-}
-
-// Parse /proc/$pid/tasks to list all the threads of the process identified by
-// pid.
-int LinuxThread::IterateProcSelfTask(int pid,
- CallbackParam<PidCallback> *callback_param) const {
- char task_path[80];
- FillProcPath(pid, task_path, sizeof(task_path));
- strcat(task_path, "task");
-
- DIR *dir = opendir(task_path);
- if (dir == NULL)
- return -1;
-
- int pid_number = 0;
- // Record the last pid we've found. This is used for duplicated thread
- // removal. Duplicated thread information can be found in /proc/$pid/tasks.
- int last_pid = -1;
- struct dirent *entry = NULL;
- while ((entry = readdir(dir)) != NULL) {
- if (strcmp(entry->d_name, ".") &&
- strcmp(entry->d_name, "..")) {
- int tpid = 0;
- if (LocalAtoi(entry->d_name, &tpid) &&
- last_pid != tpid) {
- last_pid = tpid;
- ++pid_number;
- // Invoke the callback.
- if (callback_param &&
- !(callback_param->call_back)(tpid, callback_param->context))
- break;
- }
- }
- }
- closedir(dir);
- return pid_number;
-}
-
-// Check if the address is a valid virtual address.
-// If the address is in any of the mapped modules, we take it as valid.
-// Otherwise it is invalid.
-bool LinuxThread::IsAddressMapped(uintptr_t address) const {
- AddressValidatingContext addr;
- addr.address = address;
- CallbackParam<ModuleCallback> callback_param(IsAddressInModuleCallback,
- &addr);
- ListModules(&callback_param);
- return addr.is_mapped;
-}
-
-bool LinuxThread::FindSigContext(uintptr_t sighandler_ebp,
- struct sigcontext **sig_ctx) {
- uintptr_t previous_ebp;
- const int MAX_STACK_DEPTH = 10;
- int depth_counter = 0;
-
- do {
- // We're looking for a |struct sigcontext| as the second parameter
- // to a signal handler function call. Luckily, the sigcontext
- // has an ebp member which should match the ebp pointed to
- // by the ebp of the signal handler frame.
- previous_ebp = reinterpret_cast<uintptr_t>(GetNextFrame(
- reinterpret_cast<void**>(sighandler_ebp)));
- // The stack looks like this:
- // | previous ebp | previous eip | first param | second param |,
- // so we need to offset by 3 to get to the second parameter.
- *sig_ctx = reinterpret_cast<struct sigcontext*>(sighandler_ebp +
- 3 * sizeof(uintptr_t));
- sighandler_ebp = previous_ebp;
- depth_counter++;
- } while(previous_ebp != (*sig_ctx)->ebp && sighandler_ebp != 0 &&
- IsAddressMapped(sighandler_ebp) && depth_counter < MAX_STACK_DEPTH);
-
- return previous_ebp == (*sig_ctx)->ebp && previous_ebp != 0;
-}
-
-} // namespace google_breakpad