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

📄 linuxthreads.c

📁 能把所有线程的数据和环境记录到文件,方便调试.
💻 C
📖 第 1 页 / 共 2 页
字号:
     * Unless the number of threads is increasing very rapidly, we     * should never need to do so, though, as our guestimate is very     * conservative.     */    if (max_threads < proc_sb.st_nlink + 100)      max_threads = proc_sb.st_nlink + 100;        /* scope */ {      pid_t pids[max_threads];      int   added_entries = 0;      sig_num_threads     = num_threads;      sig_pids            = pids;      for (;;) {        struct dirent *entry;        char buf[4096];        ssize_t nbytes = sys_getdents(proc, (struct dirent *)buf,                                      sizeof(buf));        if (nbytes < 0)          goto failure;        else if (nbytes == 0) {          if (added_entries) {            /* Need to keep iterating over "/proc" in multiple             * passes until we no longer find any more threads. This             * algorithm eventually completes, when all threads have             * been suspended.             */            added_entries = 0;            sys_lseek(proc, 0, SEEK_SET);            continue;          }          break;        }        for (entry = (struct dirent *)buf;             entry < (struct dirent *)&buf[nbytes];             entry = (struct dirent *)((char *)entry + entry->d_reclen)) {          if (entry->d_ino != 0) {            const char *ptr = entry->d_name;            pid_t pid;                        /* Some kernels hide threads by preceding the pid with a '.'     */            if (*ptr == '.')              ptr++;                        /* If the directory is not numeric, it cannot be a             * process/thread             */            if (*ptr < '0' || *ptr > '9')              continue;            pid = local_atoi(ptr);            /* Attach (and suspend) all threads                              */            if (pid && pid != clone_pid) {              struct stat tmp_sb;              char fname[entry->d_reclen + 48];              strcat(strcat(strcpy(fname, "/proc/"),                            entry->d_name), marker_path);                            /* Check if the marker is identical to the one we created      */              if (sys_stat(fname, &tmp_sb) >= 0 &&                  marker_sb.st_ino == tmp_sb.st_ino) {                long i, j;                /* Found one of our threads, make sure it is no duplicate    */                for (i = 0; i < num_threads; i++) {                  /* Linear search is slow, but should not matter much for                   * the typically small number of threads.                   */                  if (pids[i] == pid) {                    /* Found a duplicate; most likely on second pass         */                    goto next_entry;                  }                }                                /* Check whether data structure needs growing                */                if (num_threads >= max_threads) {                  /* Back to square one, this time with more memory          */                  NO_INTR(sys_close(proc));                  goto detach_threads;                }                /* Attaching to thread suspends it                           */                pids[num_threads++] = pid;                sig_num_threads     = num_threads;                if (sys_ptrace(PTRACE_ATTACH, pid, (void *)0,                               (void *)0) < 0) {                  /* If operation failed, ignore thread. Maybe it                   * just died?  There might also be a race                   * condition with a concurrent core dumper or                   * with a debugger. In that case, we will just                   * make a best effort, rather than failing                   * entirely.                   */                  num_threads--;                  sig_num_threads = num_threads;                  goto next_entry;                }                while (sys_waitpid(pid, (void *)0, __WALL) < 0) {                  if (errno != EINTR) {                    sys_ptrace_detach(pid);                    num_threads--;                    sig_num_threads = num_threads;                    goto next_entry;                  }                }                                if (sys_ptrace(PTRACE_PEEKDATA, pid, &i, &j) || i++ != j ||                    sys_ptrace(PTRACE_PEEKDATA, pid, &i, &j) || i   != j) {                  /* Address spaces are distinct, even though both                   * processes show the "marker". This is probably                   * a forked child process rather than a thread.                   */                  sys_ptrace_detach(pid);                  num_threads--;                  sig_num_threads = num_threads;                } else {                  found_parent |= pid == ppid;                  added_entries++;                }              }            }          }        next_entry:;        }      }      NO_INTR(sys_close(proc));      sig_proc = proc = -1;      /* If we failed to find any threads, try looking somewhere else in       * /proc. Maybe, threads are reported differently on this system.       */      if (num_threads > 1 || !*++proc_path) {        NO_INTR(sys_close(marker));        sig_marker = marker = -1;        /* If we never found the parent process, something is very wrong.         * Most likely, we are running in debugger. Any attempt to operate         * on the threads would be very incomplete. Let's just report an         * error to the caller.         */        if (!found_parent) {          ResumeAllProcessThreads(num_threads, pids);          sys__exit(3);        }        /* Now we are ready to call the callback,         * which takes care of resuming the threads for us.         */        args->result = args->callback(args->parameter, num_threads,                                      pids, args->ap);        args->err = errno;        /* Callback should have resumed threads, but better safe than sorry  */        if (ResumeAllProcessThreads(num_threads, pids)) {          /* Callback forgot to resume at least one thread, report error     */          args->err    = EINVAL;          args->result = -1;        }        sys__exit(0);      }    detach_threads:      /* Resume all threads prior to retrying the operation                  */      ResumeAllProcessThreads(num_threads, pids);      sig_pids = NULL;      num_threads = 0;      sig_num_threads = num_threads;      max_threads += 100;    }  }}/* This function gets the list of all linux threads of the current process * passes them to the 'callback' along with the 'parameter' pointer; at the * call back call time all the threads are paused via * PTRACE_ATTACH. * The callback is executed from a separate thread which shares only the * address space, the filesystem, and the filehandles with the caller. Most * notably, it does not share the same pid and ppid; and if it terminates, * the rest of the application is still there. 'callback' is supposed to do * or arrange for ResumeAllProcessThreads. This happens automatically, if * the thread raises a synchronous signal (e.g. SIGSEGV); asynchronous * signals are blocked. If the 'callback' decides to unblock them, it must * ensure that they cannot terminate the application, or that * ResumeAllProcessThreads will get called. * It is an error for the 'callback' to make any library calls that could * acquire locks. Most notably, this means that most system calls have to * avoid going through libc. Also, this means that it is not legal to call * exit() or abort(). * We return -1 on error and the return value of 'callback' on success. */int ListAllProcessThreads(void *parameter,                          ListAllProcessThreadsCallBack callback, ...) {  char                altstack_mem[ALT_STACKSIZE];  struct ListerParams args;  pid_t               clone_pid;  int                 dumpable = 1, sig, my_errno;  sigset_t            sig_blocked, sig_old;  va_start(args.ap, callback);  /* If we are short on virtual memory, initializing the alternate stack   * might trigger a SIGSEGV. Let's do this early, before it could get us   * into more trouble (i.e. before signal handlers try to use the alternate   * stack, and before we attach to other threads).   */  memset(altstack_mem, 0, sizeof(altstack_mem));  /* Some of our cleanup functions could conceivable use more stack space.   * Try to touch the stack right now. This could be defeated by the compiler   * being too smart for it's own good, so try really hard.   */  DirtyStack(32768);  /* Make this process "dumpable". This is necessary in order to ptrace()   * after having called setuid().   */  dumpable = sys_prctl(PR_GET_DUMPABLE, 0);  if (!dumpable)    sys_prctl(PR_SET_DUMPABLE, 1);  /* Fill in argument block for dumper thread                                */  args.result       = -1;  args.err          = 0;  args.altstack_mem = altstack_mem;  args.parameter    = parameter;  args.callback     = callback;  /* Before cloning the thread lister, block all asynchronous signals, as we */  /* are not prepared to handle them.                                        */  sigfillset(&sig_blocked);  for (sig = 0; sig < sizeof(sync_signals)/sizeof(*sync_signals); sig++) {    sigdelset(&sig_blocked, sync_signals[sig]);  }  if (sys_sigprocmask(SIG_BLOCK, &sig_blocked, &sig_old)) {    args.err = errno;    args.result = -1;    goto failed;  }  /* scope */ {    /* After cloning, both the parent and the child share the same instance     * of errno. We must make sure that at least one of these processes     * (in our case, the parent) uses modified syscall macros that update     * a local copy of errno, instead.     */    #define SYS_ERRNO my_errno    #define SYS_INLINE inline    #undef  SYS_LINUX_SYSCALL_SUPPORT_H    #define SYS_PREFIX 0    #include "linux_syscall_support.h"      int clone_errno;    clone_pid = local_clone((int (*)(void *))ListerThread, &args);    clone_errno = errno;    sys0_sigprocmask(SIG_SETMASK, &sig_old, &sig_old);    if (clone_pid >= 0) {      int status, rc;      while ((rc = sys0_waitpid(clone_pid, &status, __WALL)) < 0 &&             my_errno == EINTR) {             /* Keep waiting                                                 */      }      if (rc < 0) {        args.err = my_errno;        args.result = -1;      } else if (WIFEXITED(status)) {        switch (WEXITSTATUS(status)) {          case 0: break;             /* Normal process termination           */          case 2: args.err = EFAULT; /* Some fault (e.g. SIGSEGV) detected   */                  args.result = -1;                  break;          case 3: args.err = EPERM;  /* Process is already being traced      */                  args.result = -1;                  break;          default:args.err = ECHILD; /* Child died unexpectedly              */                  args.result = -1;                  break;        }      } else if (!WIFEXITED(status)) {        args.err    = EFAULT;        /* Terminated due to an unhandled signal*/        args.result = -1;      }    } else {      args.result = -1;      args.err    = clone_errno;    }  }  /* Restore the "dumpable" state of the process                             */failed:  if (!dumpable)    sys_prctl(PR_SET_DUMPABLE, dumpable);  va_end(args.ap);  errno = args.err;  return args.result;}/* This function resumes the list of all linux threads that * ListAllProcessThreads pauses before giving to its callback. * The function returns non-zero if at least one thread was * suspended and has now been resumed. */int ResumeAllProcessThreads(int num_threads, pid_t *thread_pids) {  int detached_at_least_one = 0;  while (num_threads-- > 0) {    detached_at_least_one |= sys_ptrace_detach(thread_pids[num_threads]) >= 0;  }  return detached_at_least_one;}#endif

⌨️ 快捷键说明

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