⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 linuxthreads.c

📁 能把所有线程的数据和环境记录到文件,方便调试.
💻 C
📖 第 1 页 / 共 2 页
字号:
/* Copyright (c) 2005-2007, Google Inc. * All rights reserved. * * 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. * * --- * Author: Markus Gutschke */#include "linuxthreads.h"#ifdef THREADS#include <asm/stat.h>#include <fcntl.h>#include <sched.h>#include <signal.h>#include <stdlib.h>#include <string.h>#include <sys/socket.h>#include <sys/wait.h>#include <asm/posix_types.h>#include <asm/types.h>#include <linux/dirent.h>#include "linux_syscall_support.h"#include "thread_lister.h"#ifndef CLONE_UNTRACED#define CLONE_UNTRACED 0x00800000#endif/* Synchronous signals that should not be blocked while in the lister thread. */static const int sync_signals[]  = { SIGABRT, SIGILL, SIGFPE, SIGSEGV, SIGBUS,                                     SIGXCPU, SIGXFSZ };/* itoa() is not a standard function, and we cannot safely call printf() * after suspending threads. So, we just implement our own copy. A * recursive approach is the easiest here. */static char *local_itoa(char *buf, int i) {  if (i < 0) {    *buf++ = '-';    return local_itoa(buf, -i);  } else {    if (i >= 10)      buf = local_itoa(buf, i/10);    *buf++ = (i%10) + '0';    *buf   = '\000';    return buf;  }}/* Wrapper around clone() that runs "fn" on the same stack as the * caller! Unlike fork(), the cloned thread shares the same address space. * The caller must be careful to use only minimal amounts of stack until * the cloned thread has returned. * There is a good chance that the cloned thread and the caller will share * the same copy of errno! */#ifdef __GNUC__#if __GNUC__ == 3 && __GNUC_MINOR__ >= 1 || __GNUC__ > 3/* Try to force this function into a separate stack frame, and make sure * that arguments are passed on the stack. */static int local_clone (int (*fn)(void *), void *arg, ...)  __attribute__ ((noinline));#endif#endifstatic int local_clone (int (*fn)(void *), void *arg, ...) {  /* Leave 4kB of gap between the callers stack and the new clone. This   * should be more than sufficient for the caller to call waitpid() until   * the cloned thread terminates.   *   * It is important that we set the CLONE_UNTRACED flag, because newer   * versions of "gdb" otherwise attempt to attach to our thread, and will   * attempt to reap its status codes. This subsequently results in the   * caller hanging indefinitely in waitpid(), waiting for a change in   * status that will never happen. By setting the CLONE_UNTRACED flag, we   * prevent "gdb" from stealing events, but we still expect the thread   * lister to fail, because it cannot PTRACE_ATTACH to the process that   * is being debugged. This is OK and the error code will be reported   * correctly.   */  return sys_clone(fn, (char *)&arg - 4096,                   CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_UNTRACED, arg, 0, 0, 0);}/* Local substitute for the atoi() function, which is not necessarily safe * to call once threads are suspended (depending on whether libc looks up * locale information,  when executing atoi()). */static int local_atoi(const char *s) {  int n   = 0;  int neg = *s == '-';  if (neg)    s++;  while (*s >= '0' && *s <= '9')    n = 10*n + (*s++ - '0');  return neg ? -n : n;}/* Re-runs fn until it doesn't cause EINTR */#define NO_INTR(fn)   do {} while ((fn) < 0 && errno == EINTR)/* Wrapper for open() which is guaranteed to never return EINTR. */static int c_open(const char *fname, int flags, int mode) {  ssize_t rc;  NO_INTR(rc = sys_open(fname, flags, mode));  return rc;}/* abort() is not safely reentrant, and changes it's behavior each time * it is called. This means, if the main application ever called abort() * we cannot safely call it again. This would happen if we were called * from a SIGABRT signal handler in the main application. So, document * that calling SIGABRT from the thread lister makes it not signal safe * (and vice-versa). * Also, since we share address space with the main application, we * cannot call abort() from the callback and expect the main application * to behave correctly afterwards. In fact, the only thing we can do, is * to terminate the main application with extreme prejudice (aka * PTRACE_KILL). * We set up our own SIGABRT handler to do this. * In order to find the main application from the signal handler, we * need to store information about it in global variables. This is * safe, because the main application should be suspended at this * time. If the callback ever called ResumeAllProcessThreads(), then * we are running a higher risk, though. So, try to avoid calling * abort() after calling ResumeAllProcessThreads. */static volatile int *sig_pids, sig_num_threads, sig_proc, sig_marker;/* Signal handler to help us recover from dying while we are attached to * other threads. */static void SignalHandler(int signum, siginfo_t *si, void *data) {  if (sig_pids != NULL) {    if (signum == SIGABRT) {      while (sig_num_threads-- > 0) {        /* Not sure if sched_yield is really necessary here, but it does not */        /* hurt, and it might be necessary for the same reasons that we have */        /* to do so in sys_ptrace_detach().                                  */        sys_sched_yield();        sys_ptrace(PTRACE_KILL, sig_pids[sig_num_threads], 0, 0);      }    } else if (sig_num_threads > 0) {      ResumeAllProcessThreads(sig_num_threads, (int *)sig_pids);    }  }  sig_pids = NULL;  if (sig_marker >= 0)    NO_INTR(sys_close(sig_marker));  sig_marker = -1;  if (sig_proc >= 0)    NO_INTR(sys_close(sig_proc));  sig_proc = -1;  sys__exit(signum == SIGABRT ? 1 : 2);}/* Try to dirty the stack, and hope that the compiler is not smart enough * to optimize this function away. Or worse, the compiler could inline the * function and permanently allocate the data on the stack. */static void DirtyStack(size_t amount) {  char buf[amount];  memset(buf, 0, amount);  sys_read(-1, buf, amount);}/* Data structure for passing arguments to the lister thread. */#define ALT_STACKSIZE (MINSIGSTKSZ + 4096)struct ListerParams {  int         result, err;  char        *altstack_mem;  ListAllProcessThreadsCallBack callback;  void        *parameter;  va_list     ap;};static void ListerThread(struct ListerParams *args) {  int               found_parent = 0;  pid_t             clone_pid  = sys_gettid(), ppid = sys_getppid();  char              proc_self_task[80], marker_name[48], *marker_path;  const char        *proc_paths[3];  const char *const *proc_path = proc_paths;  int               proc = -1, marker = -1, num_threads = 0;  int               max_threads = 0, sig;  struct stat       marker_sb, proc_sb;  stack_t           altstack;  /* Create "marker" that we can use to detect threads sharing the same   * address space and the same file handles. By setting the FD_CLOEXEC flag   * we minimize the risk of misidentifying child processes as threads;   * and since there is still a race condition,  we will filter those out   * later, anyway.   */  if ((marker = sys_socket(PF_LOCAL, SOCK_DGRAM, 0)) < 0 ||      sys_fcntl(marker, F_SETFD, FD_CLOEXEC) < 0) {  failure:    args->result = -1;    args->err    = errno;    if (marker >= 0)      NO_INTR(sys_close(marker));    sig_marker = marker = -1;    if (proc >= 0)      NO_INTR(sys_close(proc));    sig_proc = proc = -1;    sys__exit(1);  }  /* Compute search paths for finding thread directories in /proc            */  local_itoa(strrchr(strcpy(proc_self_task, "/proc/"), '\000'), ppid);  strcpy(marker_name, proc_self_task);  marker_path = marker_name + strlen(marker_name);  strcat(proc_self_task, "/task/");  proc_paths[0] = proc_self_task; /* /proc/$$/task/                          */  proc_paths[1] = "/proc/";       /* /proc/                                  */  proc_paths[2] = NULL;  /* Compute path for marker socket in /proc                                 */  local_itoa(strcpy(marker_path, "/fd/") + 4, marker);  if (sys_stat(marker_name, &marker_sb) < 0) {    goto failure;  }  /* Catch signals on an alternate pre-allocated stack. This way, we can   * safely execute the signal handler even if we ran out of memory.   */  memset(&altstack, 0, sizeof(altstack));  altstack.ss_sp    = args->altstack_mem;  altstack.ss_flags = 0;  altstack.ss_size  = ALT_STACKSIZE;  sys_sigaltstack(&altstack, (void *)NULL);  /* Some kernels forget to wake up traced processes, when the   * tracer dies.  So, intercept synchronous signals and make sure   * that we wake up our tracees before dying. It is the caller's   * responsibility to ensure that asynchronous signals do not   * interfere with this function.   */  sig_marker = marker;  sig_proc   = -1;  for (sig = 0; sig < sizeof(sync_signals)/sizeof(*sync_signals); sig++) {    struct sigaction sa;    memset(&sa, 0, sizeof(sa));    sa.sa_sigaction = SignalHandler;    sigfillset(&sa.sa_mask);    sa.sa_flags     = SA_ONSTACK|SA_SIGINFO|SA_RESETHAND;    sys_sigaction(sync_signals[sig], &sa, (void *)NULL);  }    /* Read process directories in /proc/...                                   */  for (;;) {    /* Some kernels know about threads, and hide them in "/proc"     * (although they are still there, if you know the process     * id). Threads are moved into a separate "task" directory. We     * check there first, and then fall back on the older naming     * convention if necessary.     */    if ((sig_proc = proc = c_open(*proc_path, O_RDONLY|O_DIRECTORY, 0)) < 0) {      if (*++proc_path != NULL)        continue;      goto failure;    }    if (sys_fstat(proc, &proc_sb) < 0)      goto failure;        /* Since we are suspending threads, we cannot call any libc     * functions that might acquire locks. Most notably, we cannot     * call malloc(). So, we have to allocate memory on the stack,     * instead. Since we do not know how much memory we need, we     * make a best guess. And if we guessed incorrectly we retry on     * a second iteration (by jumping to "detach_threads").     *

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -