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

📄 strace.c

📁 linux进程跟踪的工具和源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * 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 + -