📄 strace.c
字号:
break; default: perror("proc_poller: PIOCWSTOP"); } write(proc_poll_pipe[1], &pollinfo, sizeof(pollinfo)); _exit(0); } pollinfo.revents = POLLWANT; write(proc_poll_pipe[1], &pollinfo, sizeof(pollinfo)); sigsuspend(&empty_set); }}#endif /* !HAVE_POLLABLE_PROCFS */static intchoose_pfd(){ int i, j; struct tcb *tcp; static int last; if (followfork < 2 && last < nprocs && (pollv[last].revents & POLLWANT)) { /* * The previous process is ready to run again. We'll * let it do so if it is currently in a syscall. This * heuristic improves the readability of the trace. */ tcp = pfd2tcb(pollv[last].fd); if (tcp && (tcp->flags & TCB_INSYSCALL)) return pollv[last].fd; } for (i = 0; i < nprocs; i++) { /* Let competing children run round robin. */ j = (i + last + 1) % nprocs; if (pollv[j].revents & (POLLHUP | POLLERR)) { tcp = pfd2tcb(pollv[j].fd); if (!tcp) { fprintf(stderr, "strace: lost proc\n"); exit(1); } droptcb(tcp); return -1; } if (pollv[j].revents & POLLWANT) { last = j; return pollv[j].fd; } } fprintf(stderr, "strace: nothing ready\n"); exit(1);}static inttrace(){#ifdef POLL_HACK struct tcb *in_syscall;#endif struct tcb *tcp; int pfd; int what; int ioctl_result = 0, ioctl_errno = 0; long arg; for (;;) { if (interactive) sigprocmask(SIG_SETMASK, &empty_set, NULL); if (nprocs == 0) break; switch (nprocs) { case 1:#ifndef HAVE_POLLABLE_PROCFS if (proc_poll_pipe[0] == -1) {#endif tcp = pid2tcb(0); if (!tcp) continue; pfd = tcp->pfd; if (pfd == -1) continue; break;#ifndef HAVE_POLLABLE_PROCFS } /* fall through ... */#endif /* !HAVE_POLLABLE_PROCFS */ default:#ifdef HAVE_POLLABLE_PROCFS#ifdef POLL_HACK /* On some systems (e.g. UnixWare) we get too much ugly "unfinished..." stuff when multiple proceses are in syscalls. Here's a nasty hack */ if (in_syscall) { struct pollfd pv; tcp = in_syscall; in_syscall = NULL; pv.fd = tcp->pfd; pv.events = POLLWANT; if ((what = poll (&pv, 1, 1)) < 0) { if (interrupted) return 0; continue; } else if (what == 1 && pv.revents & POLLWANT) { goto FOUND; } }#endif if (poll(pollv, nprocs, INFTIM) < 0) { if (interrupted) return 0; continue; }#else /* !HAVE_POLLABLE_PROCFS */ if (proc_poll(pollv, nprocs, INFTIM) < 0) { if (interrupted) return 0; continue; }#endif /* !HAVE_POLLABLE_PROCFS */ pfd = choose_pfd(); if (pfd == -1) continue; break; } /* Look up `pfd' in our table. */ if ((tcp = pfd2tcb(pfd)) == NULL) { fprintf(stderr, "unknown pfd: %u\n", pfd); exit(1); } FOUND: /* Get the status of the process. */ if (!interrupted) {#ifndef FREEBSD ioctl_result = IOCTL_WSTOP (tcp);#else /* FREEBSD */ /* Thanks to some scheduling mystery, the first poller sometimes waits for the already processed end of fork event. Doing a non blocking poll here solves the problem. */ if (proc_poll_pipe[0] != -1) ioctl_result = IOCTL_STATUS (tcp); else ioctl_result = IOCTL_WSTOP (tcp);#endif /* FREEBSD */ ioctl_errno = errno;#ifndef HAVE_POLLABLE_PROCFS if (proc_poll_pipe[0] != -1) { if (ioctl_result < 0) kill(poller_pid, SIGKILL); else kill(poller_pid, SIGUSR1); }#endif /* !HAVE_POLLABLE_PROCFS */ } if (interrupted) return 0; if (interactive) sigprocmask(SIG_BLOCK, &blocked_set, NULL); if (ioctl_result < 0) { /* Find out what happened if it failed. */ switch (ioctl_errno) { case EINTR: case EBADF: continue;#ifdef FREEBSD case ENOTTY:#endif case ENOENT: droptcb(tcp); continue; default: perror("PIOCWSTOP"); exit(1); } }#ifdef FREEBSD if ((tcp->flags & TCB_STARTUP) && (tcp->status.PR_WHY == PR_SYSEXIT)) { /* discard first event for a syscall we never entered */ IOCTL (tcp->pfd, PIOCRUN, 0); continue; }#endif /* clear the just started flag */ tcp->flags &= ~TCB_STARTUP; /* set current output file */ outf = tcp->outf; if (cflag) { struct timeval stime;#ifdef FREEBSD char buf[1024]; int len; if ((len = pread(tcp->pfd_status, buf, sizeof(buf) - 1, 0)) > 0) { buf[len] = '\0'; sscanf(buf, "%*s %*d %*d %*d %*d %*d,%*d %*s %*d,%*d %*d,%*d %ld,%ld", &stime.tv_sec, &stime.tv_usec); } else stime.tv_sec = stime.tv_usec = 0;#else /* !FREEBSD */ stime.tv_sec = tcp->status.pr_stime.tv_sec; stime.tv_usec = tcp->status.pr_stime.tv_nsec/1000;#endif /* !FREEBSD */ tv_sub(&tcp->dtime, &stime, &tcp->stime); tcp->stime = stime; } what = tcp->status.PR_WHAT; switch (tcp->status.PR_WHY) {#ifndef FREEBSD case PR_REQUESTED: if (tcp->status.PR_FLAGS & PR_ASLEEP) { tcp->status.PR_WHY = PR_SYSENTRY; if (trace_syscall(tcp) < 0) { fprintf(stderr, "syscall trouble\n"); exit(1); } } break;#endif /* !FREEBSD */ case PR_SYSENTRY:#ifdef POLL_HACK in_syscall = tcp;#endif case PR_SYSEXIT: if (trace_syscall(tcp) < 0) { fprintf(stderr, "syscall trouble\n"); exit(1); } break; case PR_SIGNALLED: if (!cflag && (qual_flags[what] & QUAL_SIGNAL)) { printleader(tcp); tprintf("--- %s (%s) ---", signame(what), strsignal(what)); printtrailer(tcp); } break; case PR_FAULTED: if (!cflag && (qual_flags[what] & QUAL_FAULT)) { printleader(tcp); tprintf("=== FAULT %d ===", what); printtrailer(tcp); } break;#ifdef FREEBSD case 0: /* handle case we polled for nothing */ continue;#endif default: fprintf(stderr, "odd stop %d\n", tcp->status.PR_WHY); exit(1); break; } arg = 0;#ifndef FREEBSD if (IOCTL (tcp->pfd, PIOCRUN, &arg) < 0) {#else if (IOCTL (tcp->pfd, PIOCRUN, 0) < 0) {#endif perror("PIOCRUN"); exit(1); } } return 0;}#else /* !USE_PROCFS */static inttrace(){ int pid; int wait_errno; int status; struct tcb *tcp;#ifdef LINUX struct rusage ru;#ifdef __WALL static int wait4_options = __WALL;#endif#endif /* LINUX */ while (nprocs != 0) { if (interactive) sigprocmask(SIG_SETMASK, &empty_set, NULL);#ifdef LINUX#ifdef __WALL pid = wait4(-1, &status, wait4_options, cflag ? &ru : NULL); if ((wait4_options & __WALL) && errno == EINVAL) { /* this kernel does not support __WALL */ wait4_options &= ~__WALL; errno = 0; pid = wait4(-1, &status, wait4_options, cflag ? &ru : NULL); } if (!(wait4_options & __WALL) && errno == ECHILD) { /* most likely a "cloned" process */ pid = wait4(-1, &status, __WCLONE, cflag ? &ru : NULL); if (pid == -1) { fprintf(stderr, "strace: clone wait4 " "failed: %s\n", strerror(errno)); } }#else pid = wait4(-1, &status, 0, cflag ? &ru : NULL);#endif /* __WALL */#endif /* LINUX */#ifdef SUNOS4 pid = wait(&status);#endif /* SUNOS4 */ wait_errno = errno; if (interactive) sigprocmask(SIG_BLOCK, &blocked_set, NULL); if (interrupted) return 0; if (pid == -1) { switch (wait_errno) { case EINTR: continue; case ECHILD: /* * We would like to verify this case * but sometimes a race in Solbourne's * version of SunOS sometimes reports * ECHILD before sending us SIGCHILD. */#if 0 if (nprocs == 0) return 0; fprintf(stderr, "strace: proc miscount\n"); exit(1);#endif return 0; default: errno = wait_errno; perror("strace: wait"); return -1; } } if (debug) fprintf(stderr, " [wait(%#x) = %u]\n", status, pid); /* Look up `pid' in our table. */ if ((tcp = pid2tcb(pid)) == NULL) {#if 0 /* XXX davidm */ /* WTA: disabled again */ struct tcb *tcpchild; if ((tcpchild = alloctcb(pid)) == NULL) { fprintf(stderr, " [tcb table full]\n"); kill(pid, SIGKILL); /* XXX */ return 0; } tcpchild->flags |= TCB_ATTACHED; newoutf(tcpchild); tcp->nchildren++; if (!qflag) fprintf(stderr, "Process %d attached\n", pid);#else fprintf(stderr, "unknown pid: %u\n", pid); if (WIFSTOPPED(status)) ptrace(PTRACE_CONT, pid, (char *) 1, 0); exit(1);#endif } /* set current output file */ outf = tcp->outf; if (cflag) {#ifdef LINUX tv_sub(&tcp->dtime, &ru.ru_stime, &tcp->stime); tcp->stime = ru.ru_stime;#endif /* !LINUX */ } if (tcp->flags & TCB_SUSPENDED) { /* * Apparently, doing any ptrace() call on a stopped * process, provokes the kernel to report the process * status again on a subsequent wait(), even if the * process has not been actually restarted. * Since we have inspected the arguments of suspended * processes we end up here testing for this case. */ continue; } if (WIFSIGNALED(status)) { if (!cflag && (qual_flags[WTERMSIG(status)] & QUAL_SIGNAL)) { printleader(tcp); tprintf("+++ killed by %s +++", signame(WTERMSIG(status))); printtrailer(tcp); } droptcb(tcp); continue; } if (WIFEXITED(status)) { if (debug) fprintf(stderr, "pid %u exited\n", pid); if (tcp->flags & TCB_ATTACHED) fprintf(stderr, "PANIC: attached pid %u exited\n", pid); droptcb(tcp); continue; } if (!WIFSTOPPED(status)) { fprintf(stderr, "PANIC: pid %u not stopped\n", pid); droptcb(tcp); continue; } if (debug) fprintf(stderr, "pid %u stopped, [%s]\n", pid, signame(WSTOPSIG(status))); if (tcp->flags & TCB_STARTUP) { /* * This flag is there to keep us in sync. * Next time this process stops it should * really be entering a system call. */ tcp->flags &= ~TCB_STARTUP; if (tcp->flags & TCB_ATTACHED) { /* * Interestingly, the process may stop * with STOPSIG equal to some other signal * than SIGSTOP if we happend to attach * just before the process takes a signal. */ if (!WIFSTOPPED(status)) { fprintf(stderr, "pid %u not stopped\n", pid); detach(tcp, WSTOPSIG(status)); continue; } } else {#ifdef SUNOS4 /* A child of us stopped at exec */ if (WSTOPSIG(status) == SIGTRAP && followvfork) fixvfork(tcp);#endif /* SUNOS4 */ } if (tcp->flags & TCB_BPTSET) { if (clearbpt(tcp) < 0) /* Pretty fatal */ { droptcb(tcp); cleanup(); return -1; } } goto tracing; } if (WSTOPSIG(status) != SIGTRAP) { if (WSTOPSIG(status) == SIGSTOP && (tcp->flags & TCB_SIGTRAPPED)) { /* * Trapped attempt to block SIGTRAP * Hope we are back in control now. */ tcp->flags &= ~(TCB_INSYSCALL | TCB_SIGTRAPPED); if (ptrace(PTRACE_SYSCALL, pid, (char *) 1, 0) < 0) { perror("trace: ptrace(PTRACE_SYSCALL, ...)"); cleanup(); return -1; } continue; } if (!cflag && (qual_flags[WSTOPSIG(status)] & QUAL_SIGNAL)) { printleader(tcp); tprintf("--- %s (%s) ---", signame(WSTOPSIG(status)), strsignal(WSTOPSIG(status))); printtrailer(tcp); } if ((tcp->flags & TCB_ATTACHED) && !sigishandled(tcp, WSTOPSIG(status))) { detach(tcp, WSTOPSIG(status)); continue; } if (ptrace(PTRACE_SYSCALL, pid, (char *) 1, WSTOPSIG(status)) < 0) { perror("trace: ptrace(PTRACE_SYSCALL, ...)"); cleanup(); return -1; } tcp->flags &= ~TCB_SUSPENDED; continue; } if (trace_syscall(tcp) < 0) { if (tcp->flags & TCB_ATTACHED) detach(tcp, 0); else { ptrace(PTRACE_KILL, tcp->pid, (char *) 1, SIGTERM); droptcb(tcp); } continue; } if (tcp->flags & TCB_EXITING) { if (tcp->flags & TCB_ATTACHED) detach(tcp, 0); else if (ptrace(PTRACE_CONT, pid, (char *) 1, 0) < 0) { perror("strace: ptrace(PTRACE_CONT, ...)"); cleanup(); return -1; } continue; } if (tcp->flags & TCB_SUSPENDED) { if (!qflag) fprintf(stderr, "Process %u suspended\n", pid); continue; } tracing: if (ptrace(PTRACE_SYSCALL, pid, (char *) 1, 0) < 0) { perror("trace: ptrace(PTRACE_SYSCALL, ...)"); cleanup(); return -1; } } return 0;}#endif /* !USE_PROCFS */static int curcol;#ifdef __STDC__#include <stdarg.h>#define VA_START(a, b) va_start(a, b)#else#include <varargs.h>#define VA_START(a, b) va_start(a)#endifvoid#ifdef __STDC__tprintf(const char *fmt, ...)#elsetprintf(fmt, va_alist)char *fmt;va_dcl#endif{ va_list args; VA_START(args, fmt); if (outf) curcol += vfprintf(outf, fmt, args); va_end(args); return;}voidprintleader(tcp)struct tcb *tcp;{ if (tcp_last && (!outfname || followfork < 2 || tcp_last == tcp)) { tcp_last->flags |= TCB_REPRINT; tprintf(" <unfinished ...>\n"); } curcol = 0; if ((followfork == 1 || pflag_seen > 1) && outfname) tprintf("%-5d ", tcp->pid); else if (nprocs > 1 && !outfname) tprintf("[pid %5u] ", tcp->pid); if (tflag) { char str[sizeof("HH:MM:SS")]; struct timeval tv, dtv; static struct timeval otv; gettimeofday(&tv, NULL); if (rflag) { if (otv.tv_sec == 0) otv = tv; tv_sub(&dtv, &tv, &otv); tprintf("%6ld.%06ld ", (long) dtv.tv_sec, (long) dtv.tv_usec); otv = tv; } else if (tflag > 2) { tprintf("%ld.%06ld ", (long) tv.tv_sec, (long) tv.tv_usec); } else { time_t local = tv.tv_sec; strftime(str, sizeof(str), "%T", localtime(&local)); if (tflag > 1) tprintf("%s.%06ld ", str, (long) tv.tv_usec); else tprintf("%s ", str); } } if (iflag) printcall(tcp);}voidtabto(col)int col;{ if (curcol < col) tprintf("%*s", col - curcol, "");}voidprinttrailer(tcp)struct tcb *tcp;{ tprintf("\n"); tcp_last = NULL;}#ifdef HAVE_MP_PROCFSint mp_ioctl (int fd, int cmd, void *arg, int size) { struct iovec iov[2]; int n = 1; iov[0].iov_base = &cmd; iov[0].iov_len = sizeof cmd; if (arg) { ++n; iov[1].iov_base = arg; iov[1].iov_len = size; } return writev (fd, iov, n);}#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -