📄 strace.c
字号:
return -1; } if (fcntl(tcp->pfd, F_SETFD, arg|FD_CLOEXEC) < 0) { perror("F_SETFD"); return -1; }#endif#ifdef FREEBSD sprintf(proc, "/proc/%d/regs", tcp->pid); if ((tcp->pfd_reg = open(proc, O_RDONLY)) < 0) { perror("strace: open(\"/proc/.../regs\", ...)"); return -1; } if (cflag) { sprintf(proc, "/proc/%d/status", tcp->pid); if ((tcp->pfd_status = open(proc, O_RDONLY)) < 0) { perror("strace: open(\"/proc/.../status\", ...)"); return -1; } } else tcp->pfd_status = -1;#endif /* FREEBSD */ rebuild_pollv(); if (!attaching) { /* * Wait for the child to pause. Because of a race * condition we have to poll for the event. */ for (;;) { if (IOCTL_STATUS (tcp) < 0) { perror("strace: PIOCSTATUS"); return -1; } if (tcp->status.PR_FLAGS & PR_ASLEEP) break; } }#ifndef FREEBSD /* Stop the process so that we own the stop. */ if (IOCTL(tcp->pfd, PIOCSTOP, (char *)NULL) < 0) { perror("strace: PIOCSTOP"); return -1; }#endif #ifdef PIOCSET /* Set Run-on-Last-Close. */ arg = PR_RLC; if (IOCTL(tcp->pfd, PIOCSET, &arg) < 0) { perror("PIOCSET PR_RLC"); return -1; } /* Set or Reset Inherit-on-Fork. */ arg = PR_FORK; if (IOCTL(tcp->pfd, followfork ? PIOCSET : PIOCRESET, &arg) < 0) { perror("PIOC{SET,RESET} PR_FORK"); return -1; }#else /* !PIOCSET */#ifndef FREEBSD if (ioctl(tcp->pfd, PIOCSRLC) < 0) { perror("PIOCSRLC"); return -1; } if (ioctl(tcp->pfd, followfork ? PIOCSFORK : PIOCRFORK) < 0) { perror("PIOC{S,R}FORK"); return -1; }#else /* FREEBSD */ /* just unset the PF_LINGER flag for the Run-on-Last-Close. */ if (ioctl(tcp->pfd, PIOCGFL, &arg) < 0) { perror("PIOCGFL"); return -1; } arg &= ~PF_LINGER; if (ioctl(tcp->pfd, PIOCSFL, arg) < 0) { perror("PIOCSFL"); return -1; }#endif /* FREEBSD */#endif /* !PIOCSET */#ifndef FREEBSD /* Enable all syscall entries. */ prfillset(&sc_enter); if (IOCTL(tcp->pfd, PIOCSENTRY, &sc_enter) < 0) { perror("PIOCSENTRY"); return -1; } /* Enable all syscall exits. */ prfillset(&sc_exit); if (IOCTL(tcp->pfd, PIOCSEXIT, &sc_exit) < 0) { perror("PIOSEXIT"); return -1; } /* Enable all signals. */ prfillset(&signals); if (IOCTL(tcp->pfd, PIOCSTRACE, &signals) < 0) { perror("PIOCSTRACE"); return -1; } /* Enable all faults. */ prfillset(&faults); if (IOCTL(tcp->pfd, PIOCSFAULT, &faults) < 0) { perror("PIOCSFAULT"); return -1; }#else /* FREEBSD */ /* set events flags. */ arg = S_SIG | S_SCE | S_SCX ; if(ioctl(tcp->pfd, PIOCBIS, arg) < 0) { perror("PIOCBIS"); return -1; }#endif /* FREEBSD */ if (!attaching) {#ifdef MIPS /* * The SGI PRSABORT doesn't work for pause() so * we send it a caught signal to wake it up. */ kill(tcp->pid, SIGINT);#else /* !MIPS */#ifdef PRSABORT /* The child is in a pause(), abort it. */ arg = PRSABORT; if (IOCTL (tcp->pfd, PIOCRUN, &arg) < 0) { perror("PIOCRUN"); return -1; }#endif #endif /* !MIPS*/#ifdef FREEBSD /* wake up the child if it received the SIGSTOP */ kill(tcp->pid, SIGCONT);#endif for (;;) { /* Wait for the child to do something. */ if (IOCTL_WSTOP (tcp) < 0) { perror("PIOCWSTOP"); return -1; } if (tcp->status.PR_WHY == PR_SYSENTRY) { tcp->flags &= ~TCB_INSYSCALL; get_scno(tcp); if (tcp->scno == SYS_execve) break; } /* Set it running: maybe execve will be next. */#ifndef FREEBSD arg = 0; if (IOCTL(tcp->pfd, PIOCRUN, &arg) < 0) {#else /* FREEBSD */ if (IOCTL(tcp->pfd, PIOCRUN, 0) < 0) {#endif /* FREEBSD */ perror("PIOCRUN"); return -1; }#ifdef FREEBSD /* handle the case where we "opened" the child before it did the kill -STOP */ if (tcp->status.PR_WHY == PR_SIGNALLED && tcp->status.PR_WHAT == SIGSTOP) kill(tcp->pid, SIGCONT);#endif }#ifndef FREEBSD }#else /* FREEBSD */ } else { if (attaching < 2) { /* We are attaching to an already running process. * Try to figure out the state of the process in syscalls, * to handle the first event well. * This is done by having a look at the "wchan" property of the * process, which tells where it is stopped (if it is). */ FILE * status; char wchan[20]; /* should be enough */ sprintf(proc, "/proc/%d/status", tcp->pid); status = fopen(proc, "r"); if (status && (fscanf(status, "%*s %*d %*d %*d %*d %*d,%*d %*s %*d,%*d" "%*d,%*d %*d,%*d %19s", wchan) == 1) && strcmp(wchan, "nochan") && strcmp(wchan, "spread") && strcmp(wchan, "stopevent")) { /* The process is asleep in the middle of a syscall. Fake the syscall entry event */ tcp->flags &= ~(TCB_INSYSCALL|TCB_STARTUP); tcp->status.PR_WHY = PR_SYSENTRY; trace_syscall(tcp); } if (status) fclose(status); } /* otherwise it's a fork being followed */ }#endif /* FREEBSD */#ifndef HAVE_POLLABLE_PROCFS if (proc_poll_pipe[0] != -1) proc_poller(tcp->pfd); else if (nprocs > 1) { proc_poll_open(); proc_poller(last_pfd); proc_poller(tcp->pfd); } last_pfd = tcp->pfd;#endif /* !HAVE_POLLABLE_PROCFS */ return 0;}#endif /* USE_PROCFS */static struct tcb *pid2tcb(pid)int pid;{ int i; struct tcb *tcp; for (i = 0, tcp = tcbtab; i < MAX_PROCS; i++, tcp++) { if (pid && tcp->pid != pid) continue; if (tcp->flags & TCB_INUSE) return tcp; } return NULL;}#ifdef USE_PROCFSstatic struct tcb *pfd2tcb(pfd)int pfd;{ int i; struct tcb *tcp; for (i = 0, tcp = tcbtab; i < MAX_PROCS; i++, tcp++) { if (tcp->pfd != pfd) continue; if (tcp->flags & TCB_INUSE) return tcp; } return NULL;}#endif /* USE_PROCFS */voiddroptcb(tcp)struct tcb *tcp;{ if (tcp->pid == 0) return; nprocs--; tcp->pid = 0; tcp->flags = 0; if (tcp->pfd != -1) { close(tcp->pfd); tcp->pfd = -1;#ifdef FREEBSD if (tcp->pfd_reg != -1) { close(tcp->pfd_reg); tcp->pfd_reg = -1; } if (tcp->pfd_status != -1) { close(tcp->pfd_status); tcp->pfd_status = -1; }#endif /* !FREEBSD */ #ifdef USE_PROCFS rebuild_pollv();#endif } if (tcp->parent != NULL) { tcp->parent->nchildren--; tcp->parent = NULL; }#if 0 if (tcp->outf != stderr) fclose(tcp->outf);#endif tcp->outf = 0;}#ifndef USE_PROCFSstatic intresume(tcp)struct tcb *tcp;{ if (tcp == NULL) return -1; if (!(tcp->flags & TCB_SUSPENDED)) { fprintf(stderr, "PANIC: pid %u not suspended\n", tcp->pid); return -1; } tcp->flags &= ~TCB_SUSPENDED; if (ptrace(PTRACE_SYSCALL, tcp->pid, (char *) 1, 0) < 0) { perror("resume: ptrace(PTRACE_SYSCALL, ...)"); return -1; } if (!qflag) fprintf(stderr, "Process %u resumed\n", tcp->pid); return 0;}#endif /* !USE_PROCFS *//* detach traced process; continue with sig */static intdetach(tcp, sig)struct tcb *tcp;int sig;{ int error = 0;#ifdef LINUX int status;#endif if (tcp->flags & TCB_BPTSET) sig = SIGKILL;#ifdef LINUX /* * Linux wrongly insists the child be stopped * before detaching. Arghh. We go through hoops * to make a clean break of things. */#if defined(SPARC)#undef PTRACE_DETACH#define PTRACE_DETACH PTRACE_SUNDETACH#endif if ((error = ptrace(PTRACE_DETACH, tcp->pid, (char *) 1, sig)) == 0) { /* On a clear day, you can see forever. */ } else if (errno != ESRCH) { /* Shouldn't happen. */ perror("detach: ptrace(PTRACE_DETACH, ...)"); } else if (kill(tcp->pid, 0) < 0) { if (errno != ESRCH) perror("detach: checking sanity"); } else if (kill(tcp->pid, SIGSTOP) < 0) { if (errno != ESRCH) perror("detach: stopping child"); } else { for (;;) { if (waitpid(tcp->pid, &status, 0) < 0) { if (errno != ECHILD) perror("detach: waiting"); break; } if (!WIFSTOPPED(status)) { /* Au revoir, mon ami. */ break; } if (WSTOPSIG(status) == SIGSTOP) { if ((error = ptrace(PTRACE_DETACH, tcp->pid, (char *) 1, sig)) < 0) { if (errno != ESRCH) perror("detach: ptrace(PTRACE_DETACH, ...)"); /* I died trying. */ } break; } if ((error = ptrace(PTRACE_CONT, tcp->pid, (char *) 1, WSTOPSIG(status) == SIGTRAP ? 0 : WSTOPSIG(status))) < 0) { if (errno != ESRCH) perror("detach: ptrace(PTRACE_CONT, ...)"); break; } } }#endif /* LINUX */#if defined(SUNOS4) /* PTRACE_DETACH won't respect `sig' argument, so we post it here. */ if (sig && kill(tcp->pid, sig) < 0) perror("detach: kill"); sig = 0; if ((error = ptrace(PTRACE_DETACH, tcp->pid, (char *) 1, sig)) < 0) perror("detach: ptrace(PTRACE_DETACH, ...)");#endif /* SUNOS4 */#ifndef USE_PROCFS if (waiting_parent(tcp)) error = resume(tcp->parent);#endif /* !USE_PROCFS */ if (!qflag) fprintf(stderr, "Process %u detached\n", tcp->pid); droptcb(tcp); return error;}#ifdef USE_PROCFSstatic voidreaper(sig)int sig;{ int pid; int status; while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {#if 0 struct tcb *tcp; tcp = pid2tcb(pid); if (tcp) droptcb(tcp);#endif }}#endif /* USE_PROCFS */static voidcleanup(){ int i; struct tcb *tcp; for (i = 0, tcp = tcbtab; i < MAX_PROCS; i++, tcp++) { if (!(tcp->flags & TCB_INUSE)) continue; if (debug) fprintf(stderr, "cleanup: looking at pid %u\n", tcp->pid); if (tcp_last && (!outfname || followfork < 2 || tcp_last == tcp)) { tprintf(" <unfinished ...>\n"); tcp_last = NULL; } if (tcp->flags & TCB_ATTACHED) detach(tcp, 0); else { kill(tcp->pid, SIGCONT); kill(tcp->pid, SIGTERM); } } if (cflag) call_summary(outf);}static voidinterrupt(sig)int sig;{ interrupted = 1;}#ifndef HAVE_STRERROR#ifndef SYS_ERRLIST_DECLAREDextern int sys_nerr;extern char *sys_errlist[];#endif /* SYS_ERRLIST_DECLARED */const char *strerror(errno)int errno;{ static char buf[64]; if (errno < 1 || errno >= sys_nerr) { sprintf(buf, "Unknown error %d", errno); return buf; } return sys_errlist[errno];}#endif /* HAVE_STERRROR */#ifndef HAVE_STRSIGNAL#ifndef SYS_SIGLIST_DECLARED#ifdef HAVE__SYS_SIGLIST extern char *_sys_siglist[];#else extern char *sys_siglist[];#endif#endif /* SYS_SIGLIST_DECLARED */const char *strsignal(sig)int sig;{ static char buf[64]; if (sig < 1 || sig >= NSIG) { sprintf(buf, "Unknown signal %d", sig); return buf; }#ifdef HAVE__SYS_SIGLIST return _sys_siglist[sig];#else return sys_siglist[sig];#endif}#endif /* HAVE_STRSIGNAL */#ifdef USE_PROCFSstatic voidrebuild_pollv(){ int i, j; struct tcb *tcp; for (i = j = 0, tcp = tcbtab; i < MAX_PROCS; i++, tcp++) { if (!(tcp->flags & TCB_INUSE)) continue; pollv[j].fd = tcp->pfd; pollv[j].events = POLLWANT; j++; } if (j != nprocs) { fprintf(stderr, "strace: proc miscount\n"); exit(1); }}#ifndef HAVE_POLLABLE_PROCFSstatic voidproc_poll_open(){ int arg; int i; if (pipe(proc_poll_pipe) < 0) { perror("pipe"); exit(1); } for (i = 0; i < 2; i++) { if ((arg = fcntl(proc_poll_pipe[i], F_GETFD)) < 0) { perror("F_GETFD"); exit(1); } if (fcntl(proc_poll_pipe[i], F_SETFD, arg|FD_CLOEXEC) < 0) { perror("F_SETFD"); exit(1); } }}static intproc_poll(pollv, nfds, timeout)struct pollfd *pollv;int nfds;int timeout;{ int i; int n; struct proc_pollfd pollinfo; if ((n = read(proc_poll_pipe[0], &pollinfo, sizeof(pollinfo))) < 0) return n; if (n != sizeof(struct proc_pollfd)) { fprintf(stderr, "panic: short read: %d\n", n); exit(1); } for (i = 0; i < nprocs; i++) { if (pollv[i].fd == pollinfo.fd) pollv[i].revents = pollinfo.revents; else pollv[i].revents = 0; } poller_pid = pollinfo.pid; return 1;}static voidwakeup_handler(sig)int sig;{}static voidproc_poller(pfd)int pfd;{ struct proc_pollfd pollinfo; struct sigaction sa; sigset_t blocked_set, empty_set; int i; int n; struct rlimit rl;#ifdef FREEBSD struct procfs_status pfs;#endif /* FREEBSD */ switch (fork()) { case -1: perror("fork"); _exit(0); case 0: break; default: return; } sa.sa_handler = interactive ? SIG_DFL : SIG_IGN; sa.sa_flags = 0; sigemptyset(&sa.sa_mask); sigaction(SIGHUP, &sa, NULL); sigaction(SIGINT, &sa, NULL); sigaction(SIGQUIT, &sa, NULL); sigaction(SIGPIPE, &sa, NULL); sigaction(SIGTERM, &sa, NULL); sa.sa_handler = wakeup_handler; sigaction(SIGUSR1, &sa, NULL); sigemptyset(&blocked_set); sigaddset(&blocked_set, SIGUSR1); sigprocmask(SIG_BLOCK, &blocked_set, NULL); sigemptyset(&empty_set); if (getrlimit(RLIMIT_NOFILE, &rl) < 0) { perror("getrlimit(RLIMIT_NOFILE, ...)"); _exit(0); } n = rl.rlim_cur; for (i = 0; i < n; i++) { if (i != pfd && i != proc_poll_pipe[1]) close(i); } pollinfo.fd = pfd; pollinfo.pid = getpid(); for (;;) {#ifndef FREEBSD if (ioctl(pfd, PIOCWSTOP, NULL) < 0)#else /* FREEBSD */ if (ioctl(pfd, PIOCWSTOP, &pfs) < 0)#endif /* FREEBSD */ { switch (errno) { case EINTR: continue; case EBADF: pollinfo.revents = POLLERR; break; case ENOENT: pollinfo.revents = POLLHUP;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -