Home | History | Annotate | Line # | Download | only in ktrace
ktrace.c revision 1.41
      1  1.41  drochner /*	$NetBSD: ktrace.c,v 1.41 2007/02/08 21:40:31 drochner Exp $	*/
      2   1.4       jtc 
      3   1.1       cgd /*-
      4   1.1       cgd  * Copyright (c) 1988, 1993
      5   1.1       cgd  *	The Regents of the University of California.  All rights reserved.
      6   1.1       cgd  *
      7   1.1       cgd  * Redistribution and use in source and binary forms, with or without
      8   1.1       cgd  * modification, are permitted provided that the following conditions
      9   1.1       cgd  * are met:
     10   1.1       cgd  * 1. Redistributions of source code must retain the above copyright
     11   1.1       cgd  *    notice, this list of conditions and the following disclaimer.
     12   1.1       cgd  * 2. Redistributions in binary form must reproduce the above copyright
     13   1.1       cgd  *    notice, this list of conditions and the following disclaimer in the
     14   1.1       cgd  *    documentation and/or other materials provided with the distribution.
     15  1.28       agc  * 3. Neither the name of the University nor the names of its contributors
     16   1.1       cgd  *    may be used to endorse or promote products derived from this software
     17   1.1       cgd  *    without specific prior written permission.
     18   1.1       cgd  *
     19   1.1       cgd  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     20   1.1       cgd  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     21   1.1       cgd  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     22   1.1       cgd  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     23   1.1       cgd  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     24   1.1       cgd  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     25   1.1       cgd  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     26   1.1       cgd  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     27   1.1       cgd  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     28   1.1       cgd  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     29   1.1       cgd  * SUCH DAMAGE.
     30   1.1       cgd  */
     31   1.1       cgd 
     32   1.5     mikel #include <sys/cdefs.h>
     33   1.1       cgd #ifndef lint
     34   1.5     mikel __COPYRIGHT("@(#) Copyright (c) 1988, 1993\n\
     35   1.5     mikel 	The Regents of the University of California.  All rights reserved.\n");
     36   1.1       cgd #endif /* not lint */
     37   1.1       cgd 
     38   1.1       cgd #ifndef lint
     39   1.4       jtc #if 0
     40   1.4       jtc static char sccsid[] = "@(#)ktrace.c	8.2 (Berkeley) 4/28/95";
     41   1.5     mikel #else
     42  1.41  drochner __RCSID("$NetBSD: ktrace.c,v 1.41 2007/02/08 21:40:31 drochner Exp $");
     43   1.4       jtc #endif
     44   1.1       cgd #endif /* not lint */
     45   1.1       cgd 
     46   1.1       cgd #include <sys/param.h>
     47   1.1       cgd #include <sys/stat.h>
     48  1.12   darrenr #include <sys/wait.h>
     49   1.1       cgd #include <sys/file.h>
     50   1.1       cgd #include <sys/time.h>
     51   1.1       cgd #include <sys/uio.h>
     52   1.1       cgd #include <sys/ktrace.h>
     53  1.17  sommerfe #include <sys/socket.h>
     54   1.3     glass 
     55   1.3     glass #include <err.h>
     56  1.35  christos #include <errno.h>
     57   1.1       cgd #include <stdio.h>
     58   1.5     mikel #include <stdlib.h>
     59  1.18      matt #include <string.h>
     60   1.3     glass #include <unistd.h>
     61  1.41  drochner #include <signal.h>
     62   1.3     glass 
     63   1.1       cgd #include "ktrace.h"
     64   1.1       cgd 
     65  1.15  jdolecek #ifdef KTRUSS
     66  1.15  jdolecek #include "setemul.h"
     67  1.15  jdolecek #endif
     68  1.15  jdolecek 
     69  1.37     enami int	main(int, char *[]);
     70  1.35  christos static int rpid(char *);
     71  1.35  christos static void usage(void);
     72  1.38  christos static int do_ktrace(const char *, int, int, int, int, int);
     73  1.35  christos static void no_ktrace(int);
     74  1.35  christos static void fset(int fd, int flag);
     75  1.35  christos static void fclear(int fd, int flag);
     76   1.3     glass 
     77   1.7  christos #ifdef KTRUSS
     78   1.7  christos extern int timestamp, decimal, fancy, tail, maxdata;
     79   1.7  christos #endif
     80   1.7  christos 
     81   1.5     mikel int
     82  1.37     enami main(int argc, char *argv[])
     83   1.1       cgd {
     84   1.1       cgd 	enum { NOTSET, CLEAR, CLEARALL } clear;
     85  1.35  christos 	int block, append, ch, fd, trset, ops, pid, pidset, synclog, trpoints;
     86  1.38  christos 	int vers;
     87  1.24     assar 	const char *outfile;
     88  1.15  jdolecek #ifdef KTRUSS
     89  1.24     assar 	const char *infile;
     90  1.15  jdolecek 	const char *emul_name = "netbsd";
     91  1.15  jdolecek #endif
     92   1.1       cgd 
     93   1.1       cgd 	clear = NOTSET;
     94  1.27       dsl 	append = ops = pidset = trset = synclog = 0;
     95  1.27       dsl 	trpoints = 0;
     96  1.35  christos 	block = 1;
     97  1.38  christos 	vers = 1;
     98   1.7  christos 	pid = 0;	/* Appease GCC */
     99   1.7  christos 
    100   1.7  christos #ifdef KTRUSS
    101  1.38  christos # define OPTIONS "aCce:df:g:ilm:no:p:RTt:v:"
    102   1.7  christos 	outfile = infile = NULL;
    103   1.7  christos #else
    104  1.38  christos # define OPTIONS "aCcdf:g:ip:st:v:"
    105  1.24     assar 	outfile = DEF_TRACEFILE;
    106   1.5     mikel #endif
    107   1.7  christos 
    108  1.31     enami 	while ((ch = getopt(argc, argv, OPTIONS)) != -1)
    109  1.32     enami 		switch (ch) {
    110   1.1       cgd 		case 'a':
    111   1.1       cgd 			append = 1;
    112   1.1       cgd 			break;
    113   1.1       cgd 		case 'C':
    114   1.1       cgd 			clear = CLEARALL;
    115   1.1       cgd 			pidset = 1;
    116   1.1       cgd 			break;
    117   1.1       cgd 		case 'c':
    118   1.1       cgd 			clear = CLEAR;
    119   1.6   nathanw 			pidset = 1;
    120   1.1       cgd 			break;
    121   1.1       cgd 		case 'd':
    122   1.1       cgd 			ops |= KTRFLAG_DESCEND;
    123   1.1       cgd 			break;
    124   1.7  christos #ifdef KTRUSS
    125   1.7  christos 		case 'e':
    126  1.15  jdolecek 			emul_name = strdup(optarg); /* it's safer to copy it */
    127   1.7  christos 			break;
    128   1.1       cgd 		case 'f':
    129   1.7  christos 			infile = optarg;
    130   1.1       cgd 			break;
    131   1.9   nathanw #else
    132   1.9   nathanw 		case 'f':
    133  1.24     assar 			outfile = optarg;
    134   1.9   nathanw 			break;
    135   1.9   nathanw #endif
    136   1.1       cgd 		case 'g':
    137   1.1       cgd 			pid = -rpid(optarg);
    138   1.1       cgd 			pidset = 1;
    139   1.1       cgd 			break;
    140   1.1       cgd 		case 'i':
    141  1.27       dsl 			trpoints |= KTRFAC_INHERIT;
    142   1.1       cgd 			break;
    143   1.7  christos #ifdef KTRUSS
    144   1.7  christos 		case 'l':
    145   1.7  christos 			tail = 1;
    146   1.7  christos 			break;
    147   1.7  christos 		case 'm':
    148   1.7  christos 			maxdata = atoi(optarg);
    149   1.7  christos 			break;
    150   1.7  christos 		case 'o':
    151   1.7  christos 			outfile = optarg;
    152   1.7  christos 			break;
    153   1.7  christos #endif
    154  1.35  christos 		case 'n':
    155  1.35  christos 			block = 0;
    156  1.35  christos 			break;
    157   1.1       cgd 		case 'p':
    158   1.1       cgd 			pid = rpid(optarg);
    159   1.1       cgd 			pidset = 1;
    160   1.1       cgd 			break;
    161   1.7  christos #ifdef KTRUSS
    162   1.7  christos 		case 'R':
    163   1.7  christos 			timestamp = 2;	/* relative timestamp */
    164   1.7  christos 			break;
    165  1.23    simonb #else
    166  1.23    simonb 		case 's':
    167  1.23    simonb 			synclog = 1;
    168  1.23    simonb 			break;
    169  1.23    simonb #endif
    170  1.23    simonb #ifdef KTRUSS
    171   1.7  christos 		case 'T':
    172   1.7  christos 			timestamp = 1;
    173   1.7  christos 			break;
    174   1.7  christos #endif
    175   1.1       cgd 		case 't':
    176  1.27       dsl 			trset = 1;
    177  1.27       dsl 			trpoints = getpoints(trpoints, optarg);
    178   1.1       cgd 			if (trpoints < 0) {
    179   1.3     glass 				warnx("unknown facility in %s", optarg);
    180   1.1       cgd 				usage();
    181   1.1       cgd 			}
    182   1.1       cgd 			break;
    183  1.38  christos 		case 'v':
    184  1.38  christos 			vers = atoi(optarg);
    185  1.38  christos 			break;
    186   1.1       cgd 		default:
    187   1.1       cgd 			usage();
    188   1.1       cgd 		}
    189   1.1       cgd 	argv += optind;
    190   1.1       cgd 	argc -= optind;
    191   1.7  christos 
    192  1.27       dsl 	if (!trset)
    193  1.27       dsl 		trpoints |= clear == NOTSET ? DEF_POINTS : ALL_POINTS;
    194  1.27       dsl 
    195  1.24     assar 	if ((pidset && *argv) || (!pidset && !*argv)) {
    196  1.24     assar #ifdef KTRUSS
    197  1.31     enami 		if (!infile)
    198  1.24     assar #endif
    199  1.31     enami 			usage();
    200  1.24     assar 	}
    201   1.7  christos 
    202   1.7  christos #ifdef KTRUSS
    203  1.32     enami 	if (clear == CLEAR && outfile == NULL && pid == 0)
    204  1.32     enami 		usage();
    205  1.32     enami 
    206   1.7  christos 	if (infile) {
    207   1.7  christos 		dumpfile(infile, 0, trpoints);
    208   1.7  christos 		exit(0);
    209   1.7  christos 	}
    210  1.15  jdolecek 
    211  1.15  jdolecek 	setemul(emul_name, 0, 0);
    212   1.7  christos #endif
    213  1.15  jdolecek 
    214  1.17  sommerfe 	/*
    215  1.17  sommerfe 	 * For cleaner traces, initialize malloc now rather
    216  1.17  sommerfe 	 * than in a traced subprocess.
    217  1.17  sommerfe 	 */
    218  1.17  sommerfe 	free(malloc(1));
    219  1.31     enami 
    220   1.3     glass 	(void)signal(SIGSYS, no_ktrace);
    221   1.1       cgd 	if (clear != NOTSET) {
    222   1.1       cgd 		if (clear == CLEARALL) {
    223   1.1       cgd 			ops = KTROP_CLEAR | KTRFLAG_DESCEND;
    224   1.1       cgd 			trpoints = ALL_POINTS;
    225   1.1       cgd 			pid = 1;
    226   1.1       cgd 		} else
    227   1.1       cgd 			ops |= pid ? KTROP_CLEAR : KTROP_CLEARFILE;
    228   1.1       cgd 
    229  1.38  christos 		(void)do_ktrace(outfile, vers, ops, trpoints, pid, block);
    230   1.1       cgd 		exit(0);
    231   1.1       cgd 	}
    232   1.1       cgd 
    233   1.7  christos 	if (outfile && strcmp(outfile, "-")) {
    234   1.7  christos 		if ((fd = open(outfile, O_CREAT | O_WRONLY |
    235  1.23    simonb 		    (append ? 0 : O_TRUNC) | (synclog ? 0 : O_SYNC),
    236  1.23    simonb 		    DEFFILEMODE)) < 0)
    237  1.34     enami 			err(EXIT_FAILURE, "%s", outfile);
    238   1.7  christos 		(void)close(fd);
    239   1.7  christos 	}
    240   1.1       cgd 
    241  1.31     enami 	if (*argv) {
    242  1.13     itohy #ifdef KTRUSS
    243  1.38  christos 		if (do_ktrace(outfile, vers, ops, trpoints, getpid(), block) == 1) {
    244  1.12   darrenr 			execvp(argv[0], &argv[0]);
    245  1.34     enami 			err(EXIT_FAILURE, "exec of '%s' failed", argv[0]);
    246  1.12   darrenr 		}
    247  1.13     itohy #else
    248  1.38  christos 		(void)do_ktrace(outfile, vers, ops, trpoints, getpid(), block);
    249  1.13     itohy 		execvp(argv[0], &argv[0]);
    250  1.34     enami 		err(EXIT_FAILURE, "exec of '%s' failed", argv[0]);
    251  1.13     itohy #endif
    252   1.7  christos 	} else
    253  1.38  christos 		(void)do_ktrace(outfile, vers, ops, trpoints, pid, block);
    254  1.35  christos 	return 0;
    255   1.1       cgd }
    256   1.1       cgd 
    257  1.35  christos static int
    258  1.35  christos rpid(char *p)
    259   1.1       cgd {
    260   1.1       cgd 	static int first;
    261   1.1       cgd 
    262   1.1       cgd 	if (first++) {
    263   1.3     glass 		warnx("only one -g or -p flag is permitted.");
    264   1.1       cgd 		usage();
    265   1.1       cgd 	}
    266   1.1       cgd 	if (!*p) {
    267   1.3     glass 		warnx("illegal process id.");
    268   1.1       cgd 		usage();
    269   1.1       cgd 	}
    270  1.31     enami 	return (atoi(p));
    271   1.1       cgd }
    272   1.1       cgd 
    273  1.35  christos static void
    274  1.35  christos fset(int fd, int flag)
    275  1.35  christos {
    276  1.35  christos 	int oflag = fcntl(fd, F_GETFL, 0);
    277  1.35  christos 
    278  1.35  christos 	if (oflag == -1)
    279  1.35  christos 		err(EXIT_FAILURE, "Cannot get file flags");
    280  1.35  christos 	if (fcntl(fd, F_SETFL, oflag | flag) == -1)
    281  1.35  christos 		err(EXIT_FAILURE, "Cannot set file flags");
    282  1.35  christos }
    283  1.35  christos 
    284  1.35  christos static void
    285  1.35  christos fclear(int fd, int flag)
    286  1.35  christos {
    287  1.35  christos 	int oflag = fcntl(fd, F_GETFL, 0);
    288  1.35  christos 
    289  1.35  christos 	if (oflag == -1)
    290  1.35  christos 		err(EXIT_FAILURE, "Cannot get file flags");
    291  1.35  christos 	if (fcntl(fd, F_SETFL, oflag & ~flag) == -1)
    292  1.35  christos 		err(EXIT_FAILURE, "Cannot set file flags");
    293  1.35  christos }
    294  1.35  christos 
    295  1.35  christos static void
    296  1.35  christos usage(void)
    297   1.1       cgd {
    298  1.31     enami 
    299  1.39       wiz #define	TRPOINTS "[AaceilmnsUuvw+-]"
    300   1.7  christos #ifdef KTRUSS
    301  1.33     enami 	(void)fprintf(stderr, "usage:\t%s "
    302  1.39       wiz 	    "[-aCcdilnRT] [-e emulation] [-f infile] [-g pgrp] "
    303  1.33     enami 	    "[-m maxdata]\n\t    "
    304  1.33     enami 	    "[-o outfile] [-p pid] [-t " TRPOINTS "]\n", getprogname());
    305  1.33     enami 	(void)fprintf(stderr, "\t%s "
    306  1.39       wiz 	    "[-adinRT] [-e emulation] [-m maxdata] [-o outfile]\n\t    "
    307  1.39       wiz 	    "[-t " TRPOINTS "] [-v vers] command\n",
    308  1.33     enami 	    getprogname());
    309  1.33     enami #else
    310  1.33     enami 	(void)fprintf(stderr, "usage:\t%s "
    311  1.39       wiz 	    "[-aCcdins] [-f trfile] [-g pgrp] [-p pid] [-t " TRPOINTS "]\n",
    312  1.33     enami 	    getprogname());
    313  1.33     enami 	(void)fprintf(stderr, "\t%s "
    314  1.33     enami 	    "[-adis] [-f trfile] [-t " TRPOINTS "] command\n",
    315  1.33     enami 	    getprogname());
    316   1.7  christos #endif
    317   1.1       cgd 	exit(1);
    318   1.1       cgd }
    319   1.1       cgd 
    320  1.25  christos static const char *ktracefile = NULL;
    321  1.35  christos static void
    322  1.35  christos /*ARGSUSED*/
    323  1.35  christos no_ktrace(int sig)
    324   1.1       cgd {
    325  1.31     enami 
    326  1.25  christos 	if (ktracefile)
    327  1.25  christos 		(void)unlink(ktracefile);
    328  1.34     enami 	(void)errx(EXIT_FAILURE,
    329  1.34     enami 	    "ktrace(2) system call not supported in the running"
    330  1.25  christos 	    " kernel; re-compile kernel with `options KTRACE'");
    331   1.7  christos }
    332   1.7  christos 
    333  1.35  christos static int
    334  1.38  christos do_ktrace(const char *tracefile, int vers, int ops, int trpoints, int pid,
    335  1.38  christos     int block)
    336   1.7  christos {
    337   1.7  christos 	int ret;
    338  1.38  christos 	ops |= vers << KTRFAC_VER_SHIFT;
    339   1.7  christos 
    340  1.32     enami 	if (KTROP(ops) == KTROP_SET &&
    341  1.32     enami 	    (!tracefile || strcmp(tracefile, "-") == 0)) {
    342  1.37     enami 		int pi[2], dofork;
    343  1.31     enami 
    344   1.7  christos 		if (pipe(pi) < 0)
    345  1.34     enami 			err(EXIT_FAILURE, "pipe(2)");
    346   1.7  christos 
    347  1.35  christos 		fset(pi[0], FD_CLOEXEC);
    348  1.35  christos 		fset(pi[1], FD_CLOEXEC);
    349  1.12   darrenr 		dofork = (pid == getpid());
    350   1.7  christos 
    351  1.37     enami 		if (dofork) {
    352  1.13     itohy #ifdef KTRUSS
    353  1.37     enami 			/*
    354  1.37     enami 			 * Create a child process and trace it.
    355  1.37     enami 			 */
    356  1.37     enami 			pid = fork();
    357  1.37     enami 			if (pid == -1)
    358  1.37     enami 				err(EXIT_FAILURE, "fork");
    359  1.37     enami 			else if (pid == 0) {
    360  1.37     enami 				pid = getpid();
    361  1.37     enami 				goto trace_and_exec;
    362  1.37     enami 			}
    363  1.13     itohy #else
    364  1.37     enami 			int fpid;
    365  1.37     enami 
    366  1.37     enami 			/*
    367  1.37     enami 			 * Create a dumper process and we will be
    368  1.37     enami 			 * traced.
    369  1.37     enami 			 */
    370   1.7  christos 			fpid = fork();
    371  1.37     enami 			if (fpid == -1)
    372  1.37     enami 				err(EXIT_FAILURE, "fork");
    373  1.37     enami 			else if (fpid != 0)
    374  1.37     enami 				goto trace_and_exec;
    375  1.37     enami #endif
    376  1.37     enami 			(void)close(pi[1]);
    377  1.37     enami 		} else {
    378  1.37     enami 			ret = fktrace(pi[1], ops, trpoints, pid);
    379  1.37     enami 			if (ret == -1)
    380  1.37     enami 				err(EXIT_FAILURE, "fd %d, pid %d",
    381  1.37     enami 				    pi[1], pid);
    382  1.37     enami 			if (block)
    383  1.37     enami 				fclear(pi[1], O_NONBLOCK);
    384  1.37     enami 		}
    385  1.13     itohy #ifdef KTRUSS
    386  1.37     enami 		dumpfile(NULL, pi[0], trpoints);
    387  1.37     enami 		waitpid(pid, NULL, 0);
    388  1.13     itohy #else
    389  1.13     itohy 		{
    390  1.37     enami 			char	buf[BUFSIZ];
    391  1.37     enami 			int	n;
    392  1.37     enami 
    393  1.37     enami 			while ((n =
    394  1.37     enami 			    read(pi[0], buf, sizeof(buf))) > 0)
    395  1.37     enami 				if (write(STDOUT_FILENO, buf, (size_t)n) == -1)
    396  1.37     enami 					warn("write failed");
    397  1.37     enami 		}
    398  1.37     enami 		if (dofork)
    399  1.37     enami 			_exit(0);
    400  1.13     itohy #endif
    401  1.37     enami 		return 0;
    402  1.37     enami 
    403  1.37     enami trace_and_exec:
    404  1.35  christos 		(void)close(pi[0]);
    405  1.13     itohy 		ret = fktrace(pi[1], ops, trpoints, pid);
    406  1.37     enami 		if (ret == -1)
    407  1.37     enami 			err(EXIT_FAILURE, "fd %d, pid %d", pi[1], pid);
    408  1.35  christos 		if (block)
    409  1.35  christos 			fclear(pi[1], O_NONBLOCK);
    410  1.32     enami 	} else {
    411  1.25  christos 		ret = ktrace(ktracefile = tracefile, ops, trpoints, pid);
    412  1.32     enami 		if (ret == -1)
    413  1.32     enami 			err(EXIT_FAILURE, "file %s, pid %d",
    414  1.32     enami 			    tracefile != NULL ? tracefile : "NULL", pid);
    415  1.32     enami 	}
    416  1.12   darrenr 	return 1;
    417   1.1       cgd }
    418