Home | History | Annotate | Line # | Download | only in ktrace
ktrace.c revision 1.45
      1  1.45     joerg /*	$NetBSD: ktrace.c,v 1.45 2011/09/16 15:39:26 joerg 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.42     lukem __COPYRIGHT("@(#) Copyright (c) 1988, 1993\
     35  1.42     lukem  The Regents of the University of California.  All rights reserved.");
     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.45     joerg __RCSID("$NetBSD: ktrace.c,v 1.45 2011/09/16 15:39:26 joerg 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.35  christos static int rpid(char *);
     70  1.45     joerg __dead static void usage(void);
     71  1.38  christos static int do_ktrace(const char *, int, int, int, int, int);
     72  1.45     joerg __dead static void no_ktrace(int);
     73  1.35  christos static void fset(int fd, int flag);
     74  1.35  christos static void fclear(int fd, int flag);
     75   1.3     glass 
     76   1.7  christos #ifdef KTRUSS
     77   1.7  christos extern int timestamp, decimal, fancy, tail, maxdata;
     78   1.7  christos #endif
     79   1.7  christos 
     80   1.5     mikel int
     81  1.37     enami main(int argc, char *argv[])
     82   1.1       cgd {
     83   1.1       cgd 	enum { NOTSET, CLEAR, CLEARALL } clear;
     84  1.35  christos 	int block, append, ch, fd, trset, ops, pid, pidset, synclog, trpoints;
     85  1.38  christos 	int vers;
     86  1.24     assar 	const char *outfile;
     87  1.15  jdolecek #ifdef KTRUSS
     88  1.24     assar 	const char *infile;
     89  1.15  jdolecek 	const char *emul_name = "netbsd";
     90  1.15  jdolecek #endif
     91   1.1       cgd 
     92   1.1       cgd 	clear = NOTSET;
     93  1.27       dsl 	append = ops = pidset = trset = synclog = 0;
     94  1.27       dsl 	trpoints = 0;
     95  1.35  christos 	block = 1;
     96  1.43  christos 	vers = 2;
     97   1.7  christos 	pid = 0;	/* Appease GCC */
     98   1.7  christos 
     99   1.7  christos #ifdef KTRUSS
    100  1.38  christos # define OPTIONS "aCce:df:g:ilm:no:p:RTt:v:"
    101   1.7  christos 	outfile = infile = NULL;
    102   1.7  christos #else
    103  1.38  christos # define OPTIONS "aCcdf:g:ip:st:v:"
    104  1.24     assar 	outfile = DEF_TRACEFILE;
    105   1.5     mikel #endif
    106   1.7  christos 
    107  1.31     enami 	while ((ch = getopt(argc, argv, OPTIONS)) != -1)
    108  1.32     enami 		switch (ch) {
    109   1.1       cgd 		case 'a':
    110   1.1       cgd 			append = 1;
    111   1.1       cgd 			break;
    112   1.1       cgd 		case 'C':
    113   1.1       cgd 			clear = CLEARALL;
    114   1.1       cgd 			pidset = 1;
    115   1.1       cgd 			break;
    116   1.1       cgd 		case 'c':
    117   1.1       cgd 			clear = CLEAR;
    118   1.6   nathanw 			pidset = 1;
    119   1.1       cgd 			break;
    120   1.1       cgd 		case 'd':
    121   1.1       cgd 			ops |= KTRFLAG_DESCEND;
    122   1.1       cgd 			break;
    123   1.7  christos #ifdef KTRUSS
    124   1.7  christos 		case 'e':
    125  1.15  jdolecek 			emul_name = strdup(optarg); /* it's safer to copy it */
    126   1.7  christos 			break;
    127   1.1       cgd 		case 'f':
    128   1.7  christos 			infile = optarg;
    129   1.1       cgd 			break;
    130   1.9   nathanw #else
    131   1.9   nathanw 		case 'f':
    132  1.24     assar 			outfile = optarg;
    133   1.9   nathanw 			break;
    134   1.9   nathanw #endif
    135   1.1       cgd 		case 'g':
    136   1.1       cgd 			pid = -rpid(optarg);
    137   1.1       cgd 			pidset = 1;
    138   1.1       cgd 			break;
    139   1.1       cgd 		case 'i':
    140  1.27       dsl 			trpoints |= KTRFAC_INHERIT;
    141   1.1       cgd 			break;
    142   1.7  christos #ifdef KTRUSS
    143   1.7  christos 		case 'l':
    144   1.7  christos 			tail = 1;
    145   1.7  christos 			break;
    146   1.7  christos 		case 'm':
    147   1.7  christos 			maxdata = atoi(optarg);
    148   1.7  christos 			break;
    149   1.7  christos 		case 'o':
    150   1.7  christos 			outfile = optarg;
    151   1.7  christos 			break;
    152   1.7  christos #endif
    153  1.35  christos 		case 'n':
    154  1.35  christos 			block = 0;
    155  1.35  christos 			break;
    156   1.1       cgd 		case 'p':
    157   1.1       cgd 			pid = rpid(optarg);
    158   1.1       cgd 			pidset = 1;
    159   1.1       cgd 			break;
    160   1.7  christos #ifdef KTRUSS
    161   1.7  christos 		case 'R':
    162   1.7  christos 			timestamp = 2;	/* relative timestamp */
    163   1.7  christos 			break;
    164  1.23    simonb #else
    165  1.23    simonb 		case 's':
    166  1.23    simonb 			synclog = 1;
    167  1.23    simonb 			break;
    168  1.23    simonb #endif
    169  1.23    simonb #ifdef KTRUSS
    170   1.7  christos 		case 'T':
    171   1.7  christos 			timestamp = 1;
    172   1.7  christos 			break;
    173   1.7  christos #endif
    174   1.1       cgd 		case 't':
    175  1.27       dsl 			trset = 1;
    176  1.27       dsl 			trpoints = getpoints(trpoints, optarg);
    177   1.1       cgd 			if (trpoints < 0) {
    178   1.3     glass 				warnx("unknown facility in %s", optarg);
    179   1.1       cgd 				usage();
    180   1.1       cgd 			}
    181   1.1       cgd 			break;
    182  1.38  christos 		case 'v':
    183  1.38  christos 			vers = atoi(optarg);
    184  1.38  christos 			break;
    185   1.1       cgd 		default:
    186   1.1       cgd 			usage();
    187   1.1       cgd 		}
    188   1.1       cgd 	argv += optind;
    189   1.1       cgd 	argc -= optind;
    190   1.7  christos 
    191  1.27       dsl 	if (!trset)
    192  1.27       dsl 		trpoints |= clear == NOTSET ? DEF_POINTS : ALL_POINTS;
    193  1.27       dsl 
    194  1.24     assar 	if ((pidset && *argv) || (!pidset && !*argv)) {
    195  1.24     assar #ifdef KTRUSS
    196  1.31     enami 		if (!infile)
    197  1.24     assar #endif
    198  1.31     enami 			usage();
    199  1.24     assar 	}
    200   1.7  christos 
    201   1.7  christos #ifdef KTRUSS
    202  1.32     enami 	if (clear == CLEAR && outfile == NULL && pid == 0)
    203  1.32     enami 		usage();
    204  1.32     enami 
    205   1.7  christos 	if (infile) {
    206   1.7  christos 		dumpfile(infile, 0, trpoints);
    207   1.7  christos 		exit(0);
    208   1.7  christos 	}
    209  1.15  jdolecek 
    210  1.15  jdolecek 	setemul(emul_name, 0, 0);
    211   1.7  christos #endif
    212  1.15  jdolecek 
    213  1.17  sommerfe 	/*
    214  1.17  sommerfe 	 * For cleaner traces, initialize malloc now rather
    215  1.17  sommerfe 	 * than in a traced subprocess.
    216  1.17  sommerfe 	 */
    217  1.17  sommerfe 	free(malloc(1));
    218  1.31     enami 
    219   1.3     glass 	(void)signal(SIGSYS, no_ktrace);
    220   1.1       cgd 	if (clear != NOTSET) {
    221   1.1       cgd 		if (clear == CLEARALL) {
    222   1.1       cgd 			ops = KTROP_CLEAR | KTRFLAG_DESCEND;
    223   1.1       cgd 			trpoints = ALL_POINTS;
    224   1.1       cgd 			pid = 1;
    225   1.1       cgd 		} else
    226   1.1       cgd 			ops |= pid ? KTROP_CLEAR : KTROP_CLEARFILE;
    227   1.1       cgd 
    228  1.38  christos 		(void)do_ktrace(outfile, vers, ops, trpoints, pid, block);
    229   1.1       cgd 		exit(0);
    230   1.1       cgd 	}
    231   1.1       cgd 
    232   1.7  christos 	if (outfile && strcmp(outfile, "-")) {
    233   1.7  christos 		if ((fd = open(outfile, O_CREAT | O_WRONLY |
    234  1.23    simonb 		    (append ? 0 : O_TRUNC) | (synclog ? 0 : O_SYNC),
    235  1.23    simonb 		    DEFFILEMODE)) < 0)
    236  1.34     enami 			err(EXIT_FAILURE, "%s", outfile);
    237   1.7  christos 		(void)close(fd);
    238   1.7  christos 	}
    239   1.1       cgd 
    240  1.31     enami 	if (*argv) {
    241  1.13     itohy #ifdef KTRUSS
    242  1.38  christos 		if (do_ktrace(outfile, vers, ops, trpoints, getpid(), block) == 1) {
    243  1.12   darrenr 			execvp(argv[0], &argv[0]);
    244  1.34     enami 			err(EXIT_FAILURE, "exec of '%s' failed", argv[0]);
    245  1.12   darrenr 		}
    246  1.13     itohy #else
    247  1.38  christos 		(void)do_ktrace(outfile, vers, ops, trpoints, getpid(), block);
    248  1.13     itohy 		execvp(argv[0], &argv[0]);
    249  1.34     enami 		err(EXIT_FAILURE, "exec of '%s' failed", argv[0]);
    250  1.13     itohy #endif
    251   1.7  christos 	} else
    252  1.38  christos 		(void)do_ktrace(outfile, vers, ops, trpoints, pid, block);
    253  1.35  christos 	return 0;
    254   1.1       cgd }
    255   1.1       cgd 
    256  1.35  christos static int
    257  1.35  christos rpid(char *p)
    258   1.1       cgd {
    259   1.1       cgd 	static int first;
    260   1.1       cgd 
    261   1.1       cgd 	if (first++) {
    262   1.3     glass 		warnx("only one -g or -p flag is permitted.");
    263   1.1       cgd 		usage();
    264   1.1       cgd 	}
    265   1.1       cgd 	if (!*p) {
    266   1.3     glass 		warnx("illegal process id.");
    267   1.1       cgd 		usage();
    268   1.1       cgd 	}
    269  1.31     enami 	return (atoi(p));
    270   1.1       cgd }
    271   1.1       cgd 
    272  1.35  christos static void
    273  1.35  christos fset(int fd, int flag)
    274  1.35  christos {
    275  1.35  christos 	int oflag = fcntl(fd, F_GETFL, 0);
    276  1.35  christos 
    277  1.35  christos 	if (oflag == -1)
    278  1.35  christos 		err(EXIT_FAILURE, "Cannot get file flags");
    279  1.35  christos 	if (fcntl(fd, F_SETFL, oflag | flag) == -1)
    280  1.35  christos 		err(EXIT_FAILURE, "Cannot set file flags");
    281  1.35  christos }
    282  1.35  christos 
    283  1.35  christos static void
    284  1.35  christos fclear(int fd, int flag)
    285  1.35  christos {
    286  1.35  christos 	int oflag = fcntl(fd, F_GETFL, 0);
    287  1.35  christos 
    288  1.35  christos 	if (oflag == -1)
    289  1.35  christos 		err(EXIT_FAILURE, "Cannot get file flags");
    290  1.35  christos 	if (fcntl(fd, F_SETFL, oflag & ~flag) == -1)
    291  1.35  christos 		err(EXIT_FAILURE, "Cannot set file flags");
    292  1.35  christos }
    293  1.35  christos 
    294  1.35  christos static void
    295  1.35  christos usage(void)
    296   1.1       cgd {
    297  1.31     enami 
    298  1.44      yamt #define	TRPOINTS "[AaceilmnSsuvw+-]"
    299   1.7  christos #ifdef KTRUSS
    300  1.33     enami 	(void)fprintf(stderr, "usage:\t%s "
    301  1.39       wiz 	    "[-aCcdilnRT] [-e emulation] [-f infile] [-g pgrp] "
    302  1.33     enami 	    "[-m maxdata]\n\t    "
    303  1.33     enami 	    "[-o outfile] [-p pid] [-t " TRPOINTS "]\n", getprogname());
    304  1.33     enami 	(void)fprintf(stderr, "\t%s "
    305  1.39       wiz 	    "[-adinRT] [-e emulation] [-m maxdata] [-o outfile]\n\t    "
    306  1.39       wiz 	    "[-t " TRPOINTS "] [-v vers] command\n",
    307  1.33     enami 	    getprogname());
    308  1.33     enami #else
    309  1.33     enami 	(void)fprintf(stderr, "usage:\t%s "
    310  1.39       wiz 	    "[-aCcdins] [-f trfile] [-g pgrp] [-p pid] [-t " TRPOINTS "]\n",
    311  1.33     enami 	    getprogname());
    312  1.33     enami 	(void)fprintf(stderr, "\t%s "
    313  1.33     enami 	    "[-adis] [-f trfile] [-t " TRPOINTS "] command\n",
    314  1.33     enami 	    getprogname());
    315   1.7  christos #endif
    316   1.1       cgd 	exit(1);
    317   1.1       cgd }
    318   1.1       cgd 
    319  1.25  christos static const char *ktracefile = NULL;
    320  1.35  christos static void
    321  1.35  christos /*ARGSUSED*/
    322  1.35  christos no_ktrace(int sig)
    323   1.1       cgd {
    324  1.31     enami 
    325  1.25  christos 	if (ktracefile)
    326  1.25  christos 		(void)unlink(ktracefile);
    327  1.34     enami 	(void)errx(EXIT_FAILURE,
    328  1.34     enami 	    "ktrace(2) system call not supported in the running"
    329  1.25  christos 	    " kernel; re-compile kernel with `options KTRACE'");
    330   1.7  christos }
    331   1.7  christos 
    332  1.35  christos static int
    333  1.38  christos do_ktrace(const char *tracefile, int vers, int ops, int trpoints, int pid,
    334  1.38  christos     int block)
    335   1.7  christos {
    336   1.7  christos 	int ret;
    337  1.38  christos 	ops |= vers << KTRFAC_VER_SHIFT;
    338   1.7  christos 
    339  1.32     enami 	if (KTROP(ops) == KTROP_SET &&
    340  1.32     enami 	    (!tracefile || strcmp(tracefile, "-") == 0)) {
    341  1.37     enami 		int pi[2], dofork;
    342  1.31     enami 
    343   1.7  christos 		if (pipe(pi) < 0)
    344  1.34     enami 			err(EXIT_FAILURE, "pipe(2)");
    345   1.7  christos 
    346  1.35  christos 		fset(pi[0], FD_CLOEXEC);
    347  1.35  christos 		fset(pi[1], FD_CLOEXEC);
    348  1.12   darrenr 		dofork = (pid == getpid());
    349   1.7  christos 
    350  1.37     enami 		if (dofork) {
    351  1.13     itohy #ifdef KTRUSS
    352  1.37     enami 			/*
    353  1.37     enami 			 * Create a child process and trace it.
    354  1.37     enami 			 */
    355  1.37     enami 			pid = fork();
    356  1.37     enami 			if (pid == -1)
    357  1.37     enami 				err(EXIT_FAILURE, "fork");
    358  1.37     enami 			else if (pid == 0) {
    359  1.37     enami 				pid = getpid();
    360  1.37     enami 				goto trace_and_exec;
    361  1.37     enami 			}
    362  1.13     itohy #else
    363  1.37     enami 			int fpid;
    364  1.37     enami 
    365  1.37     enami 			/*
    366  1.37     enami 			 * Create a dumper process and we will be
    367  1.37     enami 			 * traced.
    368  1.37     enami 			 */
    369   1.7  christos 			fpid = fork();
    370  1.37     enami 			if (fpid == -1)
    371  1.37     enami 				err(EXIT_FAILURE, "fork");
    372  1.37     enami 			else if (fpid != 0)
    373  1.37     enami 				goto trace_and_exec;
    374  1.37     enami #endif
    375  1.37     enami 			(void)close(pi[1]);
    376  1.37     enami 		} else {
    377  1.37     enami 			ret = fktrace(pi[1], ops, trpoints, pid);
    378  1.37     enami 			if (ret == -1)
    379  1.37     enami 				err(EXIT_FAILURE, "fd %d, pid %d",
    380  1.37     enami 				    pi[1], pid);
    381  1.37     enami 			if (block)
    382  1.37     enami 				fclear(pi[1], O_NONBLOCK);
    383  1.37     enami 		}
    384  1.13     itohy #ifdef KTRUSS
    385  1.37     enami 		dumpfile(NULL, pi[0], trpoints);
    386  1.37     enami 		waitpid(pid, NULL, 0);
    387  1.13     itohy #else
    388  1.13     itohy 		{
    389  1.37     enami 			char	buf[BUFSIZ];
    390  1.37     enami 			int	n;
    391  1.37     enami 
    392  1.37     enami 			while ((n =
    393  1.37     enami 			    read(pi[0], buf, sizeof(buf))) > 0)
    394  1.37     enami 				if (write(STDOUT_FILENO, buf, (size_t)n) == -1)
    395  1.37     enami 					warn("write failed");
    396  1.37     enami 		}
    397  1.37     enami 		if (dofork)
    398  1.37     enami 			_exit(0);
    399  1.13     itohy #endif
    400  1.37     enami 		return 0;
    401  1.37     enami 
    402  1.37     enami trace_and_exec:
    403  1.35  christos 		(void)close(pi[0]);
    404  1.13     itohy 		ret = fktrace(pi[1], ops, trpoints, pid);
    405  1.37     enami 		if (ret == -1)
    406  1.37     enami 			err(EXIT_FAILURE, "fd %d, pid %d", pi[1], pid);
    407  1.35  christos 		if (block)
    408  1.35  christos 			fclear(pi[1], O_NONBLOCK);
    409  1.32     enami 	} else {
    410  1.25  christos 		ret = ktrace(ktracefile = tracefile, ops, trpoints, pid);
    411  1.32     enami 		if (ret == -1)
    412  1.32     enami 			err(EXIT_FAILURE, "file %s, pid %d",
    413  1.32     enami 			    tracefile != NULL ? tracefile : "NULL", pid);
    414  1.32     enami 	}
    415  1.12   darrenr 	return 1;
    416   1.1       cgd }
    417