📄 strace.c
字号:
/* * Copyright (c) 1991, 1992 Paul Kranenburg <pk@cs.few.eur.nl> * Copyright (c) 1993 Branko Lankester <branko@hacktic.nl> * Copyright (c) 1993, 1994, 1995, 1996 Rick Sladkey <jrs@world.std.com> * Copyright (c) 1996-1999 Wichert Akkerman <wichert@cistron.nl> * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. * * $Id: strace.c,v 1.21 2001/03/28 14:40:14 wichert Exp $ */#include <sys/types.h>#include "defs.h"#include <signal.h>#include <errno.h>#include <sys/param.h>#include <fcntl.h>#include <sys/resource.h>#include <sys/wait.h>#include <sys/stat.h>#include <pwd.h>#include <grp.h>#include <string.h>#ifdef USE_PROCFS#include <poll.h>#endif#ifdef SVR4#include <sys/stropts.h>#ifdef HAVE_MP_PROCFS#include <sys/uio.h>#endif#endifint debug = 0, followfork = 0, followvfork = 0, interactive = 0;int rflag = 0, tflag = 0, dtime = 0, cflag = 0;int iflag = 0, xflag = 0, qflag = 0;int pflag_seen = 0;char *username = NULL;uid_t run_uid;gid_t run_gid;int acolumn = DEFAULT_ACOLUMN;int max_strlen = DEFAULT_STRLEN;char *outfname = NULL;FILE *outf;struct tcb tcbtab[MAX_PROCS];int nprocs;char *progname;extern char version[];extern char **environ;static struct tcb *pid2tcb P((int pid));static int trace P((void));static void cleanup P((void));static void interrupt P((int sig));static sigset_t empty_set, blocked_set;#ifdef HAVE_SIG_ATOMIC_Tstatic volatile sig_atomic_t interrupted;#else /* !HAVE_SIG_ATOMIC_T */#ifdef __STDC__static volatile int interrupted;#else /* !__STDC__ */static int interrupted;#endif /* !__STDC__ */#endif /* !HAVE_SIG_ATOMIC_T */#ifdef USE_PROCFSstatic struct tcb *pfd2tcb P((int pfd));static void reaper P((int sig));static void rebuild_pollv P((void));struct pollfd pollv[MAX_PROCS];#ifndef HAVE_POLLABLE_PROCFSstatic void proc_poll_open P((void));static void proc_poller P((int pfd));struct proc_pollfd { int fd; int revents; int pid;};static int poller_pid;static int proc_poll_pipe[2] = { -1, -1 };#endif /* !HAVE_POLLABLE_PROCFS */#ifdef HAVE_MP_PROCFS#define POLLWANT POLLWRNORM#else#define POLLWANT POLLPRI#endif#endif /* USE_PROCFS */static voidusage(ofp, exitval)FILE *ofp;int exitval;{ fprintf(ofp, "\usage: strace [-dffhiqrtttTvVxx] [-a column] [-e expr] ... [-o file]\n\ [-p pid] ... [-s strsize] [-u username] [command [arg ...]]\n\ or: strace -c [-e expr] ... [-O overhead] [-S sortby] [command [arg ...]]\n\-c -- count time, calls, and errors for each syscall and report summary\n\-f -- follow forks, -ff -- with output into separate files\n\-F -- attempt to follow vforks, -h -- print help message\n\-i -- print instruction pointer at time of syscall\n\-q -- suppress messages about attaching, detaching, etc.\n\-r -- print relative timestamp, -t -- absolute timestamp, -tt -- with usecs\n\-T -- print time spent in each syscall, -V -- print version\n\-v -- verbose mode: print unabbreviated argv, stat, termio[s], etc. args\n\-x -- print non-ascii strings in hex, -xx -- print all strings in hex\n\-a column -- alignment COLUMN for printing syscall results (default %d)\n\-e expr -- a qualifying expression: option=[!]all or option=[!]val1[,val2]...\n\ options: trace, abbrev, verbose, raw, signal, read, or write\n\-o file -- send trace output to FILE instead of stderr\n\-O overhead -- set overhead for tracing syscalls to OVERHEAD usecs\n\-p pid -- trace process with process id PID, may be repeated\n\-s strsize -- limit length of print strings to STRSIZE chars (default %d)\n\-S sortby -- sort syscall counts by: time, calls, name, nothing (default %s)\n\-u username -- run command as username handling setuid and/or setgid\n\", DEFAULT_ACOLUMN, DEFAULT_STRLEN, DEFAULT_SORTBY); exit(exitval);}#ifdef SVR4#ifdef MIPSvoidfoobar(){}#endif /* MIPS */#endif /* SVR4 */intmain(argc, argv)int argc;char *argv[];{ extern int optind; extern char *optarg; struct tcb *tcp; int c, pid = 0; struct sigaction sa; static char buf[BUFSIZ]; progname = argv[0]; outf = stderr; interactive = 1; qualify("trace=all"); qualify("abbrev=all"); qualify("verbose=all"); qualify("signal=all"); set_sortby(DEFAULT_SORTBY); set_personality(DEFAULT_PERSONALITY); while ((c = getopt(argc, argv, "+cdfFhiqrtTvVxa:e:o:O:p:s:S:u:")) != EOF) { switch (c) { case 'c': cflag++; dtime++; break; case 'd': debug++; break; case 'f': followfork++; break; case 'F': followvfork++; break; case 'h': usage(stdout, 0); break; case 'i': iflag++; break; case 'q': qflag++; break; case 'r': rflag++; tflag++; break; case 't': tflag++; break; case 'T': dtime++; break; case 'x': xflag++; break; case 'v': qualify("abbrev=none"); break; case 'V': printf("%s\n", version); exit(0); break; case 'a': acolumn = atoi(optarg); break; case 'e': qualify(optarg); break; case 'o': outfname = strdup(optarg); break; case 'O': set_overhead(atoi(optarg)); break; case 'p': if ((pid = atoi(optarg)) == 0) { fprintf(stderr, "%s: Invalid process id: %s\n", progname, optarg); break; } if (pid == getpid()) { fprintf(stderr, "%s: I'm sorry, I can't let you do that, Dave.\n", progname); break; } if ((tcp = alloctcb(pid)) == NULL) { fprintf(stderr, "%s: tcb table full, please recompile strace\n", progname); exit(1); } tcp->flags |= TCB_ATTACHED; pflag_seen++; break; case 's': max_strlen = atoi(optarg); break; case 'S': set_sortby(optarg); break; case 'u': username = strdup(optarg); break; default: usage(stderr, 1); break; } } /* See if they want to run as another user. */ if (username != NULL) { struct passwd *pent; if (getuid() != 0 || geteuid() != 0) { fprintf(stderr, "%s: you must be root to use the -u option\n", progname); exit(1); } if ((pent = getpwnam(username)) == NULL) { fprintf(stderr, "%s: cannot find user `%s'\n", progname, optarg); exit(1); } run_uid = pent->pw_uid; run_gid = pent->pw_gid; } else { run_uid = getuid(); run_gid = getgid(); }#ifndef SVR4 setreuid(geteuid(), getuid());#endif /* See if they want to pipe the output. */ if (outfname && (outfname[0] == '|' || outfname[0] == '!')) { if ((outf = popen(outfname + 1, "w")) == NULL) { fprintf(stderr, "%s: can't popen '%s': %s\n", progname, outfname + 1, strerror(errno)); exit(1); } free(outfname); outfname = NULL; } /* Check if they want to redirect the output. */ if (outfname) { if ((outf = fopen(outfname, "w")) == NULL) { fprintf(stderr, "%s: can't fopen '%s': %s\n", progname, outfname, strerror(errno)); exit(1); } }#ifndef SVR4 setreuid(geteuid(), getuid());#endif if (!outfname) { qflag = 1; setvbuf(outf, buf, _IOLBF, BUFSIZ); } else if (optind < argc) interactive = 0; else qflag = 1; for (c = 0, tcp = tcbtab; c < MAX_PROCS; c++, tcp++) { /* Reinitialize the output since it may have changed. */ tcp->outf = outf; if (!(tcp->flags & TCB_INUSE) || !(tcp->flags & TCB_ATTACHED)) continue;#ifdef USE_PROCFS if (proc_open(tcp, 1) < 0) { fprintf(stderr, "trouble opening proc file\n"); droptcb(tcp); continue; }#else /* !USE_PROCFS */ if (ptrace(PTRACE_ATTACH, tcp->pid, (char *) 1, 0) < 0) { perror("attach: ptrace(PTRACE_ATTACH, ...)"); droptcb(tcp); continue; }#endif /* !USE_PROCFS */ if (!qflag) fprintf(stderr, "Process %u attached - interrupt to quit\n", pid); } if (optind < argc) { struct stat statbuf; char *filename; char pathname[MAXPATHLEN]; filename = argv[optind]; if (strchr(filename, '/')) strcpy(pathname, filename);#ifdef USE_DEBUGGING_EXEC /* * Debuggers customarily check the current directory * first regardless of the path but doing that gives * security geeks a panic attack. */ else if (stat(filename, &statbuf) == 0) strcpy(pathname, filename);#endif /* USE_DEBUGGING_EXEC */ else { char *path; int m, n, len; for (path = getenv("PATH"); path && *path; path += m) { if (strchr(path, ':')) { n = strchr(path, ':') - path; m = n + 1; } else m = n = strlen(path); if (n == 0) { getcwd(pathname, MAXPATHLEN); len = strlen(pathname); } else { strncpy(pathname, path, n); len = n; } if (len && pathname[len - 1] != '/') pathname[len++] = '/'; strcpy(pathname + len, filename); if (stat(pathname, &statbuf) == 0) break; } } if (stat(pathname, &statbuf) < 0) { fprintf(stderr, "%s: %s: command not found\n", progname, filename); exit(1); } switch (pid = fork()) { case -1: perror("strace: fork"); cleanup(); exit(1); break; case 0: {#ifdef USE_PROCFS if (outf != stderr) close (fileno (outf));#ifdef MIPS /* Kludge for SGI, see proc_open for details. */ sa.sa_handler = foobar; sa.sa_flags = 0; sigemptyset(&sa.sa_mask); sigaction(SIGINT, &sa, NULL);#endif /* MIPS */#ifndef FREEBSD pause();#else /* FREEBSD */ kill(getpid(), SIGSTOP); /* stop HERE */#endif /* FREEBSD */ #else /* !USE_PROCFS */ if (outf!=stderr) close(fileno (outf)); if (ptrace(PTRACE_TRACEME, 0, (char *) 1, 0) < 0) { perror("strace: ptrace(PTRACE_TRACEME, ...)"); return -1; } if (debug) kill(getpid(), SIGSTOP); if (username != NULL || geteuid() == 0) { uid_t run_euid = run_uid; gid_t run_egid = run_gid; if (statbuf.st_mode & S_ISUID) run_euid = statbuf.st_uid; if (statbuf.st_mode & S_ISGID) run_egid = statbuf.st_gid; /* * It is important to set groups before we * lose privileges on setuid. */ if (username != NULL) { if (initgroups(username, run_gid) < 0) { perror("initgroups"); exit(1); } if (setregid(run_gid, run_egid) < 0) { perror("setregid"); exit(1); } if (setreuid(run_uid, run_euid) < 0) { perror("setreuid"); exit(1); } } } else setreuid(run_uid, run_uid);#endif /* !USE_PROCFS */ execv(pathname, &argv[optind]); perror("strace: exec"); _exit(1); break; } default: if ((tcp = alloctcb(pid)) == NULL) { fprintf(stderr, "tcb table full\n"); cleanup(); exit(1); }#ifdef USE_PROCFS if (proc_open(tcp, 0) < 0) { fprintf(stderr, "trouble opening proc file\n"); cleanup(); exit(1); }#endif /* USE_PROCFS */#ifndef USE_PROCFS fake_execve(tcp, pathname, &argv[optind], environ);#endif /* !USE_PROCFS */ break; } } else if (pflag_seen == 0) usage(stderr, 1); sigemptyset(&empty_set); sigemptyset(&blocked_set); sa.sa_handler = SIG_IGN; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sigaction(SIGTTOU, &sa, NULL); sigaction(SIGTTIN, &sa, NULL); if (interactive) { sigaddset(&blocked_set, SIGHUP); sigaddset(&blocked_set, SIGINT); sigaddset(&blocked_set, SIGQUIT); sigaddset(&blocked_set, SIGPIPE); sigaddset(&blocked_set, SIGTERM); sa.sa_handler = interrupt;#ifdef SUNOS4 /* POSIX signals on sunos4.1 are a little broken. */ sa.sa_flags = SA_INTERRUPT;#endif /* SUNOS4 */ } sigaction(SIGHUP, &sa, NULL); sigaction(SIGINT, &sa, NULL); sigaction(SIGQUIT, &sa, NULL); sigaction(SIGPIPE, &sa, NULL); sigaction(SIGTERM, &sa, NULL);#ifdef USE_PROCFS sa.sa_handler = reaper; sigaction(SIGCHLD, &sa, NULL);#endif /* USE_PROCFS */ if (trace() < 0) exit(1); cleanup(); exit(0);}voidnewoutf(tcp)struct tcb *tcp;{ char name[MAXPATHLEN]; FILE *fp; if (outfname && followfork > 1) { sprintf(name, "%s.%u", outfname, tcp->pid);#ifndef SVR4 setreuid(geteuid(), getuid());#endif fp = fopen(name, "w");#ifndef SVR4 setreuid(geteuid(), getuid());#endif if (fp == NULL) { perror("fopen"); return; } tcp->outf = fp; } return;}struct tcb *alloctcb(pid)int pid;{ int i; struct tcb *tcp; for (i = 0, tcp = tcbtab; i < MAX_PROCS; i++, tcp++) { if ((tcp->flags & TCB_INUSE) == 0) { tcp->pid = pid; tcp->parent = NULL; tcp->nchildren = 0; tcp->flags = TCB_INUSE | TCB_STARTUP; tcp->outf = outf; /* Initialise to current out file */ tcp->stime.tv_sec = 0; tcp->stime.tv_usec = 0; tcp->pfd = -1; nprocs++; return tcp; } } return NULL;}#ifdef USE_PROCFSintproc_open(tcp, attaching)struct tcb *tcp;int attaching;{ char proc[32]; long arg;#ifdef SVR4 sysset_t sc_enter, sc_exit; sigset_t signals; fltset_t faults;#endif#ifndef HAVE_POLLABLE_PROCFS static int last_pfd;#endif#ifdef HAVE_MP_PROCFS /* Open the process pseudo-files in /proc. */ sprintf(proc, "/proc/%d/ctl", tcp->pid); if ((tcp->pfd = open(proc, O_WRONLY|O_EXCL)) < 0) { perror("strace: open(\"/proc/...\", ...)"); return -1; } if ((arg = fcntl(tcp->pfd, F_GETFD)) < 0) { perror("F_GETFD"); return -1; } if (fcntl(tcp->pfd, F_SETFD, arg|FD_CLOEXEC) < 0) { perror("F_SETFD"); return -1; } sprintf(proc, "/proc/%d/status", tcp->pid); if ((tcp->pfd_stat = open(proc, O_RDONLY|O_EXCL)) < 0) { perror("strace: open(\"/proc/...\", ...)"); return -1; } if ((arg = fcntl(tcp->pfd_stat, F_GETFD)) < 0) { perror("F_GETFD"); return -1; } if (fcntl(tcp->pfd_stat, F_SETFD, arg|FD_CLOEXEC) < 0) { perror("F_SETFD"); return -1; } sprintf(proc, "/proc/%d/as", tcp->pid); if ((tcp->pfd_as = open(proc, O_RDONLY|O_EXCL)) < 0) { perror("strace: open(\"/proc/...\", ...)"); return -1; } if ((arg = fcntl(tcp->pfd_as, F_GETFD)) < 0) { perror("F_GETFD"); return -1; } if (fcntl(tcp->pfd_as, F_SETFD, arg|FD_CLOEXEC) < 0) { perror("F_SETFD"); return -1; }#else /* Open the process pseudo-file in /proc. */#ifndef FREEBSD sprintf(proc, "/proc/%d", tcp->pid); if ((tcp->pfd = open(proc, O_RDWR|O_EXCL)) < 0) {#else /* FREEBSD */ sprintf(proc, "/proc/%d/mem", tcp->pid); if ((tcp->pfd = open(proc, O_RDWR)) < 0) {#endif /* FREEBSD */ perror("strace: open(\"/proc/...\", ...)"); return -1; } if ((arg = fcntl(tcp->pfd, F_GETFD)) < 0) { perror("F_GETFD");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -