diff options
Diffstat (limited to 'usr.sbin/kgmon')
-rw-r--r-- | usr.sbin/kgmon/Makefile | 14 | ||||
-rw-r--r-- | usr.sbin/kgmon/kgmon.8 | 122 | ||||
-rw-r--r-- | usr.sbin/kgmon/kgmon.c | 520 |
3 files changed, 656 insertions, 0 deletions
diff --git a/usr.sbin/kgmon/Makefile b/usr.sbin/kgmon/Makefile new file mode 100644 index 0000000..e0ef616 --- /dev/null +++ b/usr.sbin/kgmon/Makefile @@ -0,0 +1,14 @@ +# @(#)Makefile 8.1 (Berkeley) 6/6/93 + +PROG= kgmon +MAN8= kgmon.0 +DPADD= ${LIBKVM} +LDADD= -lkvm +# +# This program may safely be run setuid-root to allow non-root +# users to start, stop, and reset profiling buffers. +# +#BINOWN=root +#BINMODE=4555 + +.include <bsd.prog.mk> diff --git a/usr.sbin/kgmon/kgmon.8 b/usr.sbin/kgmon/kgmon.8 new file mode 100644 index 0000000..060ed2e --- /dev/null +++ b/usr.sbin/kgmon/kgmon.8 @@ -0,0 +1,122 @@ +.\" Copyright (c) 1983, 1991, 1993 +.\" The Regents of the University of California. 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. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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. +.\" +.\" @(#)kgmon.8 8.1 (Berkeley) 6/6/93 +.\" +.Dd June 6, 1993 +.Dt KGMON 8 +.Os BSD 4.2 +.Sh NAME +.Nm kgmon +.Nd generate a dump of the operating system's profile buffers +.Sh SYNOPSIS +.Nm kgmon +.Op Fl bhpr +.Op Fl M core +.Op Fl N system +.Sh DESCRIPTION +.Nm Kgmon +is a tool used when profiling the operating system. +When no arguments are supplied, +.Nm kgmon +indicates the state of operating system profiling as running, +off, or not configured. +(see +.Xr config 8 ) +If the +.Fl p +flag is specified, +.Nm kgmon +extracts profile data from the operating system and produces a +.Pa gmon.out +file suitable for later analysis by +.Xr gprof 1 . +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl b +Resume the collection of profile data. +.It Fl h +Stop the collection of profile data. +.It Fl p +Dump the contents of the profile buffers into a +.Pa gmon.out +file. +.It Fl r +Reset all the profile buffers. +If the +.Fl p +flag is also specified, the +.Pa gmon.out +file is generated before the buffers are reset. +.It Fl M +Extract values associated with the name list from the specified core +instead of the default ``/dev/kmem''. +.It Fl N +Extract the name list from the specified system instead of the +default ``/vmunix''. +.El +.Pp +If neither +.Fl b +nor +.Fl h +is specified, the state of profiling collection remains unchanged. +For example, if the +.Fl p +flag is specified and profile data is being collected, +profiling will be momentarily suspended, +the operating system profile buffers will be dumped, +and profiling will be immediately resumed. +.Sh FILES +.Bl -tag -width /dev/kmemx -compact +.It Pa /vmunix +the default system +.It Pa /dev/kmem +the default memory +.El +.Sh SEE ALSO +.Xr gprof 1 , +.Xr config 8 +.Sh DIAGNOSTICS +Users with only read permission on +.Pa /dev/kmem +cannot change the state +of profiling collection. +They can get a +.Pa gmon.out +file with the warning that the data may be +inconsistent if profiling is in progress. +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.2 . diff --git a/usr.sbin/kgmon/kgmon.c b/usr.sbin/kgmon/kgmon.c new file mode 100644 index 0000000..1265ce2 --- /dev/null +++ b/usr.sbin/kgmon/kgmon.c @@ -0,0 +1,520 @@ +/* + * Copyright (c) 1983, 1992, 1993 + * The Regents of the University of California. 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1983, 1992, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)kgmon.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/file.h> +#include <sys/sysctl.h> +#include <sys/gmon.h> +#include <errno.h> +#include <kvm.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <nlist.h> +#include <ctype.h> +#include <paths.h> + +struct nlist nl[] = { +#define N_GMONPARAM 0 + { "__gmonparam" }, +#define N_PROFHZ 1 + { "_profhz" }, + 0, +}; + +struct kvmvars { + kvm_t *kd; + struct gmonparam gpm; +}; + +int bflag, hflag, kflag, rflag, pflag; +int debug = 0; +void setprof __P((struct kvmvars *kvp, int state)); +void dumpstate __P((struct kvmvars *kvp)); +void reset __P((struct kvmvars *kvp)); + +int +main(int argc, char **argv) +{ + extern char *optarg; + extern int optind; + int ch, mode, disp, accessmode; + struct kvmvars kvmvars; + char *system, *kmemf; + + seteuid(getuid()); + kmemf = NULL; + system = NULL; + while ((ch = getopt(argc, argv, "M:N:bhpr")) != EOF) { + switch((char)ch) { + + case 'M': + kmemf = optarg; + kflag = 1; + break; + + case 'N': + system = optarg; + break; + + case 'b': + bflag = 1; + break; + + case 'h': + hflag = 1; + break; + + case 'p': + pflag = 1; + break; + + case 'r': + rflag = 1; + break; + + default: + (void)fprintf(stderr, + "usage: kgmon [-bhrp] [-M core] [-N system]\n"); + exit(1); + } + } + argc -= optind; + argv += optind; + +#define BACKWARD_COMPATIBILITY +#ifdef BACKWARD_COMPATIBILITY + if (*argv) { + system = *argv; + if (*++argv) { + kmemf = *argv; + ++kflag; + } + } +#endif + if (system == NULL) + system = _PATH_UNIX; + accessmode = openfiles(system, kmemf, &kvmvars); + mode = getprof(&kvmvars); + if (hflag) + disp = GMON_PROF_OFF; + else if (bflag) + disp = GMON_PROF_ON; + else + disp = mode; + if (pflag) + dumpstate(&kvmvars); + if (rflag) + reset(&kvmvars); + if (accessmode == O_RDWR) + setprof(&kvmvars, disp); + (void)fprintf(stdout, "kgmon: kernel profiling is %s.\n", + disp == GMON_PROF_OFF ? "off" : "running"); + return (0); +} + +/* + * Check that profiling is enabled and open any ncessary files. + */ +openfiles(system, kmemf, kvp) + char *system; + char *kmemf; + struct kvmvars *kvp; +{ + int mib[3], state, size, openmode; + char errbuf[_POSIX2_LINE_MAX]; + + if (!kflag) { + mib[0] = CTL_KERN; + mib[1] = KERN_PROF; + mib[2] = GPROF_STATE; + size = sizeof state; + if (sysctl(mib, 3, &state, &size, NULL, 0) < 0) { + (void)fprintf(stderr, + "kgmon: profiling not defined in kernel.\n"); + exit(20); + } + if (!(bflag || hflag || rflag || + (pflag && state == GMON_PROF_ON))) + return (O_RDONLY); + (void)seteuid(0); + if (sysctl(mib, 3, NULL, NULL, &state, size) >= 0) + return (O_RDWR); + (void)seteuid(getuid()); + kern_readonly(state); + return (O_RDONLY); + } + openmode = (bflag || hflag || pflag || rflag) ? O_RDWR : O_RDONLY; + kvp->kd = kvm_openfiles(system, kmemf, NULL, openmode, errbuf); + if (kvp->kd == NULL) { + if (openmode == O_RDWR) { + openmode = O_RDONLY; + kvp->kd = kvm_openfiles(system, kmemf, NULL, O_RDONLY, + errbuf); + } + if (kvp->kd == NULL) { + (void)fprintf(stderr, "kgmon: kvm_openfiles: %s\n", + errbuf); + exit(2); + } + kern_readonly(GMON_PROF_ON); + } + if (kvm_nlist(kvp->kd, nl) < 0) { + (void)fprintf(stderr, "kgmon: %s: no namelist\n", system); + exit(3); + } + if (!nl[N_GMONPARAM].n_value) { + (void)fprintf(stderr, + "kgmon: profiling not defined in kernel.\n"); + exit(20); + } + return (openmode); +} + +/* + * Suppress options that require a writable kernel. + */ +kern_readonly(mode) + int mode; +{ + + (void)fprintf(stderr, "kgmon: kernel read-only: "); + if (pflag && mode == GMON_PROF_ON) + (void)fprintf(stderr, "data may be inconsistent\n"); + if (rflag) + (void)fprintf(stderr, "-r supressed\n"); + if (bflag) + (void)fprintf(stderr, "-b supressed\n"); + if (hflag) + (void)fprintf(stderr, "-h supressed\n"); + rflag = bflag = hflag = 0; +} + +/* + * Get the state of kernel profiling. + */ +getprof(kvp) + struct kvmvars *kvp; +{ + int mib[3], size; + + if (kflag) { + size = kvm_read(kvp->kd, nl[N_GMONPARAM].n_value, &kvp->gpm, + sizeof kvp->gpm); + } else { + mib[0] = CTL_KERN; + mib[1] = KERN_PROF; + mib[2] = GPROF_GMONPARAM; + size = sizeof kvp->gpm; + if (sysctl(mib, 3, &kvp->gpm, &size, NULL, 0) < 0) + size = 0; + } + if (size != sizeof kvp->gpm) { + (void)fprintf(stderr, "kgmon: cannot get gmonparam: %s\n", + kflag ? kvm_geterr(kvp->kd) : strerror(errno)); + exit (4); + } + return (kvp->gpm.state); +} + +/* + * Enable or disable kernel profiling according to the state variable. + */ +void +setprof(kvp, state) + struct kvmvars *kvp; + int state; +{ + struct gmonparam *p = (struct gmonparam *)nl[N_GMONPARAM].n_value; + int mib[3], sz, oldstate; + + sz = sizeof(state); + if (!kflag) { + mib[0] = CTL_KERN; + mib[1] = KERN_PROF; + mib[2] = GPROF_STATE; + if (sysctl(mib, 3, &oldstate, &sz, NULL, 0) < 0) + goto bad; + if (oldstate == state) + return; + (void)seteuid(0); + if (sysctl(mib, 3, NULL, NULL, &state, sz) >= 0) { + (void)seteuid(getuid()); + return; + } + (void)seteuid(getuid()); + } else if (kvm_write(kvp->kd, (u_long)&p->state, (void *)&state, sz) + == sz) + return; +bad: + (void)fprintf(stderr, "kgmon: warning: cannot turn profiling %s\n", + state == GMON_PROF_OFF ? "off" : "on"); +} + +/* + * Build the gmon.out file. + */ +void +dumpstate(kvp) + struct kvmvars *kvp; +{ + register FILE *fp; + struct rawarc rawarc; + struct tostruct *tos; + u_long frompc, addr; + u_short *froms, *tickbuf; + int mib[3], i; + struct gmonhdr h; + int fromindex, endfrom, toindex; + + setprof(kvp, GMON_PROF_OFF); + fp = fopen("gmon.out", "w"); + if (fp == 0) { + perror("gmon.out"); + return; + } + + /* + * Build the gmon header and write it to a file. + */ + bzero(&h, sizeof(h)); + h.lpc = kvp->gpm.lowpc; + h.hpc = kvp->gpm.highpc; + h.ncnt = kvp->gpm.kcountsize + sizeof(h); + h.version = GMONVERSION; + h.profrate = getprofhz(kvp); + fwrite((char *)&h, sizeof(h), 1, fp); + + /* + * Write out the tick buffer. + */ + mib[0] = CTL_KERN; + mib[1] = KERN_PROF; + if ((tickbuf = (u_short *)malloc(kvp->gpm.kcountsize)) == NULL) { + fprintf(stderr, "kgmon: cannot allocate kcount space\n"); + exit (5); + } + if (kflag) { + i = kvm_read(kvp->kd, (u_long)kvp->gpm.kcount, (void *)tickbuf, + kvp->gpm.kcountsize); + } else { + mib[2] = GPROF_COUNT; + i = kvp->gpm.kcountsize; + if (sysctl(mib, 3, tickbuf, &i, NULL, 0) < 0) + i = 0; + } + if (i != kvp->gpm.kcountsize) { + (void)fprintf(stderr, "kgmon: read ticks: read %u, got %d: %s", + kvp->gpm.kcountsize, i, + kflag ? kvm_geterr(kvp->kd) : strerror(errno)); + exit(6); + } + if ((fwrite(tickbuf, kvp->gpm.kcountsize, 1, fp)) != 1) { + perror("kgmon: writing tocks to gmon.out"); + exit(7); + } + free(tickbuf); + + /* + * Write out the arc info. + */ + if ((froms = (u_short *)malloc(kvp->gpm.fromssize)) == NULL) { + fprintf(stderr, "kgmon: cannot allocate froms space\n"); + exit (8); + } + if (kflag) { + i = kvm_read(kvp->kd, (u_long)kvp->gpm.froms, (void *)froms, + kvp->gpm.fromssize); + } else { + mib[2] = GPROF_FROMS; + i = kvp->gpm.fromssize; + if (sysctl(mib, 3, froms, &i, NULL, 0) < 0) + i = 0; + } + if (i != kvp->gpm.fromssize) { + (void)fprintf(stderr, "kgmon: read froms: read %u, got %d: %s", + kvp->gpm.fromssize, i, + kflag ? kvm_geterr(kvp->kd) : strerror(errno)); + exit(9); + } + if ((tos = (struct tostruct *)malloc(kvp->gpm.tossize)) == NULL) { + fprintf(stderr, "kgmon: cannot allocate tos space\n"); + exit(10); + } + if (kflag) { + i = kvm_read(kvp->kd, (u_long)kvp->gpm.tos, (void *)tos, + kvp->gpm.tossize); + } else { + mib[2] = GPROF_TOS; + i = kvp->gpm.tossize; + if (sysctl(mib, 3, tos, &i, NULL, 0) < 0) + i = 0; + } + if (i != kvp->gpm.tossize) { + (void)fprintf(stderr, "kgmon: read tos: read %u, got %d: %s", + kvp->gpm.tossize, i, + kflag ? kvm_geterr(kvp->kd) : strerror(errno)); + exit(11); + } + if (debug) + (void)fprintf(stderr, "kgmon: lowpc 0x%x, textsize 0x%x\n", + kvp->gpm.lowpc, kvp->gpm.textsize); + endfrom = kvp->gpm.fromssize / sizeof(*froms); + for (fromindex = 0; fromindex < endfrom; ++fromindex) { + if (froms[fromindex] == 0) + continue; + frompc = (u_long)kvp->gpm.lowpc + + (fromindex * kvp->gpm.hashfraction * sizeof(*froms)); + for (toindex = froms[fromindex]; toindex != 0; + toindex = tos[toindex].link) { + if (debug) + (void)fprintf(stderr, + "%s: [mcleanup] frompc 0x%x selfpc 0x%x count %d\n", + "kgmon", frompc, tos[toindex].selfpc, + tos[toindex].count); + rawarc.raw_frompc = frompc; + rawarc.raw_selfpc = (u_long)tos[toindex].selfpc; + rawarc.raw_count = tos[toindex].count; + fwrite((char *)&rawarc, sizeof(rawarc), 1, fp); + } + } + fclose(fp); +} + +/* + * Get the profiling rate. + */ +int +getprofhz(kvp) + struct kvmvars *kvp; +{ + int mib[2], size, profrate; + struct clockinfo clockrate; + + if (kflag) { + profrate = 1; + if (kvm_read(kvp->kd, nl[N_PROFHZ].n_value, &profrate, + sizeof profrate) != sizeof profrate) + (void)fprintf(stderr, "kgmon: get clockrate: %s\n", + kvm_geterr(kvp->kd)); + return (profrate); + } + mib[0] = CTL_KERN; + mib[1] = KERN_CLOCKRATE; + clockrate.profhz = 1; + size = sizeof clockrate; + if (sysctl(mib, 2, &clockrate, &size, NULL, 0) < 0) + (void)fprintf(stderr, "kgmon: get clockrate: %s\n", + strerror(errno)); + return (clockrate.profhz); +} + +/* + * Reset the kernel profiling date structures. + */ +void +reset(kvp) + struct kvmvars *kvp; +{ + char *zbuf; + u_long biggest; + int mib[3]; + + setprof(kvp, GMON_PROF_OFF); + + biggest = kvp->gpm.kcountsize; + if (kvp->gpm.fromssize > biggest) + biggest = kvp->gpm.fromssize; + if (kvp->gpm.tossize > biggest) + biggest = kvp->gpm.tossize; + if ((zbuf = (char *)malloc(biggest)) == NULL) { + fprintf(stderr, "kgmon: cannot allocate zbuf space\n"); + exit(12); + } + bzero(zbuf, biggest); + if (kflag) { + if (kvm_write(kvp->kd, (u_long)kvp->gpm.kcount, zbuf, + kvp->gpm.kcountsize) != kvp->gpm.kcountsize) { + (void)fprintf(stderr, "kgmon: tickbuf zero: %s\n", + kvm_geterr(kvp->kd)); + exit(13); + } + if (kvm_write(kvp->kd, (u_long)kvp->gpm.froms, zbuf, + kvp->gpm.fromssize) != kvp->gpm.fromssize) { + (void)fprintf(stderr, "kgmon: froms zero: %s\n", + kvm_geterr(kvp->kd)); + exit(14); + } + if (kvm_write(kvp->kd, (u_long)kvp->gpm.tos, zbuf, + kvp->gpm.tossize) != kvp->gpm.tossize) { + (void)fprintf(stderr, "kgmon: tos zero: %s\n", + kvm_geterr(kvp->kd)); + exit(15); + } + return; + } + (void)seteuid(0); + mib[0] = CTL_KERN; + mib[1] = KERN_PROF; + mib[2] = GPROF_COUNT; + if (sysctl(mib, 3, NULL, NULL, zbuf, kvp->gpm.kcountsize) < 0) { + (void)fprintf(stderr, "kgmon: tickbuf zero: %s\n", + strerror(errno)); + exit(13); + } + mib[2] = GPROF_FROMS; + if (sysctl(mib, 3, NULL, NULL, zbuf, kvp->gpm.fromssize) < 0) { + (void)fprintf(stderr, "kgmon: froms zero: %s\n", + strerror(errno)); + exit(14); + } + mib[2] = GPROF_TOS; + if (sysctl(mib, 3, NULL, NULL, zbuf, kvp->gpm.tossize) < 0) { + (void)fprintf(stderr, "kgmon: tos zero: %s\n", + strerror(errno)); + exit(15); + } + (void)seteuid(getuid()); + free(zbuf); +} |