]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - usr.sbin/kgmon/kgmon.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.git] / usr.sbin / kgmon / kgmon.c
1 /*
2  * Copyright (c) 1983, 1992, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 4. Neither the name of the University nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29
30 #ifndef lint
31 static const char copyright[] =
32 "@(#) Copyright (c) 1983, 1992, 1993\n\
33         The Regents of the University of California.  All rights reserved.\n";
34 #endif /* not lint */
35
36 #ifndef lint
37 #if 0
38 static char sccsid[] = "@(#)kgmon.c     8.1 (Berkeley) 6/6/93";
39 #endif
40 static const char rcsid[] =
41   "$FreeBSD$";
42 #endif /* not lint */
43
44 #include <sys/param.h>
45 #include <sys/file.h>
46 #include <sys/time.h>
47 #include <sys/sysctl.h>
48 #include <sys/gmon.h>
49 #include <ctype.h>
50 #include <err.h>
51 #include <errno.h>
52 #include <kvm.h>
53 #include <limits.h>
54 #include <nlist.h>
55 #include <paths.h>
56 #include <stddef.h>
57 #include <stdio.h>
58 #include <stdlib.h>
59 #include <string.h>
60 #include <unistd.h>
61
62 struct nlist nl[] = {
63 #define N_GMONPARAM     0
64         { "__gmonparam" },
65 #define N_PROFHZ        1
66         { "_profhz" },
67         { NULL },
68 };
69
70 struct kvmvars {
71         kvm_t   *kd;
72         struct gmonparam gpm;
73 };
74
75 int     Bflag, bflag, hflag, kflag, rflag, pflag;
76 int     debug = 0;
77 int     getprof(struct kvmvars *);
78 int     getprofhz(struct kvmvars *);
79 void    kern_readonly(int);
80 int     openfiles(char *, char *, struct kvmvars *);
81 void    setprof(struct kvmvars *kvp, int state);
82 void    dumpstate(struct kvmvars *kvp);
83 void    reset(struct kvmvars *kvp);
84 static void usage(void);
85
86 int
87 main(int argc, char **argv)
88 {
89         int ch, mode, disp, accessmode;
90         struct kvmvars kvmvars;
91         char *system, *kmemf;
92
93         seteuid(getuid());
94         kmemf = NULL;
95         system = NULL;
96         while ((ch = getopt(argc, argv, "M:N:Bbhpr")) != -1) {
97                 switch((char)ch) {
98
99                 case 'M':
100                         kmemf = optarg;
101                         kflag = 1;
102                         break;
103
104                 case 'N':
105                         system = optarg;
106                         break;
107
108                 case 'B':
109                         Bflag = 1;
110                         break;
111
112                 case 'b':
113                         bflag = 1;
114                         break;
115
116                 case 'h':
117                         hflag = 1;
118                         break;
119
120                 case 'p':
121                         pflag = 1;
122                         break;
123
124                 case 'r':
125                         rflag = 1;
126                         break;
127
128                 default:
129                         usage();
130                 }
131         }
132         argc -= optind;
133         argv += optind;
134
135 #define BACKWARD_COMPATIBILITY
136 #ifdef  BACKWARD_COMPATIBILITY
137         if (*argv) {
138                 system = *argv;
139                 if (*++argv) {
140                         kmemf = *argv;
141                         ++kflag;
142                 }
143         }
144 #endif
145         if (system == NULL)
146                 system = (char *)getbootfile();
147         accessmode = openfiles(system, kmemf, &kvmvars);
148         mode = getprof(&kvmvars);
149         if (hflag)
150                 disp = GMON_PROF_OFF;
151         else if (Bflag)
152                 disp = GMON_PROF_HIRES;
153         else if (bflag)
154                 disp = GMON_PROF_ON;
155         else
156                 disp = mode;
157         if (pflag)
158                 dumpstate(&kvmvars);
159         if (rflag)
160                 reset(&kvmvars);
161         if (accessmode == O_RDWR)
162                 setprof(&kvmvars, disp);
163         (void)fprintf(stdout, "kgmon: kernel profiling is %s.\n",
164                       disp == GMON_PROF_OFF ? "off" :
165                       disp == GMON_PROF_HIRES ? "running (high resolution)" :
166                       disp == GMON_PROF_ON ? "running" :
167                       disp == GMON_PROF_BUSY ? "busy" :
168                       disp == GMON_PROF_ERROR ? "off (error)" :
169                       "in an unknown state");
170         return (0);
171 }
172
173 static void
174 usage()
175 {
176         fprintf(stderr, "usage: kgmon [-Bbhrp] [-M core] [-N system]\n");
177         exit(1);
178 }
179
180 /*
181  * Check that profiling is enabled and open any necessary files.
182  */
183 int
184 openfiles(system, kmemf, kvp)
185         char *system;
186         char *kmemf;
187         struct kvmvars *kvp;
188 {
189         size_t size;
190         int mib[3], state, openmode;
191         char errbuf[_POSIX2_LINE_MAX];
192
193         if (!kflag) {
194                 mib[0] = CTL_KERN;
195                 mib[1] = KERN_PROF;
196                 mib[2] = GPROF_STATE;
197                 size = sizeof state;
198                 if (sysctl(mib, 3, &state, &size, NULL, 0) < 0)
199                         errx(20, "profiling not defined in kernel");
200                 if (!(Bflag || bflag || hflag || rflag ||
201                     (pflag &&
202                      (state == GMON_PROF_HIRES || state == GMON_PROF_ON))))
203                         return (O_RDONLY);
204                 (void)seteuid(0);
205                 if (sysctl(mib, 3, NULL, NULL, &state, size) >= 0)
206                         return (O_RDWR);
207                 (void)seteuid(getuid());
208                 kern_readonly(state);
209                 return (O_RDONLY);
210         }
211         openmode = (Bflag || bflag || hflag || pflag || rflag)
212                    ? O_RDWR : O_RDONLY;
213         kvp->kd = kvm_openfiles(system, kmemf, NULL, openmode, errbuf);
214         if (kvp->kd == NULL) {
215                 if (openmode == O_RDWR) {
216                         openmode = O_RDONLY;
217                         kvp->kd = kvm_openfiles(system, kmemf, NULL, O_RDONLY,
218                             errbuf);
219                 }
220                 if (kvp->kd == NULL)
221                         errx(2, "kvm_openfiles: %s", errbuf);
222                 kern_readonly(GMON_PROF_ON);
223         }
224         if (kvm_nlist(kvp->kd, nl) < 0)
225                 errx(3, "%s: no namelist", system);
226         if (!nl[N_GMONPARAM].n_value)
227                 errx(20, "profiling not defined in kernel");
228         return (openmode);
229 }
230
231 /*
232  * Suppress options that require a writable kernel.
233  */
234 void
235 kern_readonly(mode)
236         int mode;
237 {
238
239         (void)fprintf(stderr, "kgmon: kernel read-only: ");
240         if (pflag && (mode == GMON_PROF_HIRES || mode == GMON_PROF_ON))
241                 (void)fprintf(stderr, "data may be inconsistent\n");
242         if (rflag)
243                 (void)fprintf(stderr, "-r supressed\n");
244         if (Bflag)
245                 (void)fprintf(stderr, "-B supressed\n");
246         if (bflag)
247                 (void)fprintf(stderr, "-b supressed\n");
248         if (hflag)
249                 (void)fprintf(stderr, "-h supressed\n");
250         rflag = Bflag = bflag = hflag = 0;
251 }
252
253 /*
254  * Get the state of kernel profiling.
255  */
256 int
257 getprof(kvp)
258         struct kvmvars *kvp;
259 {
260         size_t size;
261         int mib[3];
262
263         if (kflag) {
264                 size = kvm_read(kvp->kd, nl[N_GMONPARAM].n_value, &kvp->gpm,
265                     sizeof kvp->gpm);
266         } else {
267                 mib[0] = CTL_KERN;
268                 mib[1] = KERN_PROF;
269                 mib[2] = GPROF_GMONPARAM;
270                 size = sizeof kvp->gpm;
271                 if (sysctl(mib, 3, &kvp->gpm, &size, NULL, 0) < 0)
272                         size = 0;
273         }
274
275         /*
276          * Accept certain undersized "structs" from old kernels.  We need
277          * everything up to hashfraction, and want profrate and
278          * histcounter_type.  Assume that the kernel doesn't put garbage
279          * in any padding that is returned instead of profrate and
280          * histcounter_type.  This is a bad assumption for dead kernels,
281          * since kvm_read() will normally return garbage for bytes beyond
282          * the end of the actual kernel struct, if any.
283          */
284         if (size < offsetof(struct gmonparam, hashfraction) +
285             sizeof(kvp->gpm.hashfraction) || size > sizeof(kvp->gpm))
286                 errx(4, "cannot get gmonparam: %s",
287                     kflag ? kvm_geterr(kvp->kd) : strerror(errno));
288         bzero((char *)&kvp->gpm + size, sizeof(kvp->gpm) - size);
289         if (kvp->gpm.profrate == 0)
290                 kvp->gpm.profrate = getprofhz(kvp);
291 #ifdef __i386__
292         if (kvp->gpm.histcounter_type == 0) {
293                 /*
294                  * This fixup only works for not-so-old i386 kernels.  The
295                  * magic 16 is the kernel FUNCTION_ALIGNMENT.  64-bit
296                  * counters are signed; smaller counters are unsigned.
297                  */
298                 kvp->gpm.histcounter_type = 16 /
299                     (kvp->gpm.textsize / kvp->gpm.kcountsize) * CHAR_BIT;
300                 if (kvp->gpm.histcounter_type == 64)
301                         kvp->gpm.histcounter_type = -64;
302         }
303 #endif
304
305         return (kvp->gpm.state);
306 }
307
308 /*
309  * Enable or disable kernel profiling according to the state variable.
310  */
311 void
312 setprof(kvp, state)
313         struct kvmvars *kvp;
314         int state;
315 {
316         struct gmonparam *p = (struct gmonparam *)nl[N_GMONPARAM].n_value;
317         size_t sz;
318         int mib[3], oldstate;
319
320         sz = sizeof(state);
321         if (!kflag) {
322                 mib[0] = CTL_KERN;
323                 mib[1] = KERN_PROF;
324                 mib[2] = GPROF_STATE;
325                 if (sysctl(mib, 3, &oldstate, &sz, NULL, 0) < 0)
326                         goto bad;
327                 if (oldstate == state)
328                         return;
329                 (void)seteuid(0);
330                 if (sysctl(mib, 3, NULL, NULL, &state, sz) >= 0) {
331                         (void)seteuid(getuid());
332                         return;
333                 }
334                 (void)seteuid(getuid());
335         } else if (kvm_write(kvp->kd, (u_long)&p->state, (void *)&state, sz)
336             == sz)
337                 return;
338 bad:
339         warnx("warning: cannot turn profiling %s",
340             state == GMON_PROF_OFF ? "off" : "on");
341 }
342
343 /*
344  * Build the gmon.out file.
345  */
346 void
347 dumpstate(kvp)
348         struct kvmvars *kvp;
349 {
350         register FILE *fp;
351         struct rawarc rawarc;
352         struct tostruct *tos;
353         u_long frompc;
354         u_short *froms, *tickbuf;
355         size_t i;
356         int mib[3];
357         struct gmonhdr h;
358         int fromindex, endfrom, toindex;
359
360         setprof(kvp, GMON_PROF_OFF);
361         fp = fopen("gmon.out", "w");
362         if (fp == 0) {
363                 warn("gmon.out");
364                 return;
365         }
366
367         /*
368          * Build the gmon header and write it to a file.
369          */
370         bzero(&h, sizeof(h));
371         h.lpc = kvp->gpm.lowpc;
372         h.hpc = kvp->gpm.highpc;
373         h.ncnt = kvp->gpm.kcountsize + sizeof(h);
374         h.version = GMONVERSION;
375         h.profrate = kvp->gpm.profrate;
376         h.histcounter_type = kvp->gpm.histcounter_type;
377         fwrite((char *)&h, sizeof(h), 1, fp);
378
379         /*
380          * Write out the tick buffer.
381          */
382         mib[0] = CTL_KERN;
383         mib[1] = KERN_PROF;
384         if ((tickbuf = (u_short *)malloc(kvp->gpm.kcountsize)) == NULL)
385                 errx(5, "cannot allocate kcount space");
386         if (kflag) {
387                 i = kvm_read(kvp->kd, (u_long)kvp->gpm.kcount, (void *)tickbuf,
388                     kvp->gpm.kcountsize);
389         } else {
390                 mib[2] = GPROF_COUNT;
391                 i = kvp->gpm.kcountsize;
392                 if (sysctl(mib, 3, tickbuf, &i, NULL, 0) < 0)
393                         i = 0;
394         }
395         if (i != kvp->gpm.kcountsize)
396                 errx(6, "read ticks: read %lu, got %ld: %s",
397                     kvp->gpm.kcountsize, (long)i,
398                     kflag ? kvm_geterr(kvp->kd) : strerror(errno));
399         if ((fwrite(tickbuf, kvp->gpm.kcountsize, 1, fp)) != 1)
400                 err(7, "writing tocks to gmon.out");
401         free(tickbuf);
402
403         /*
404          * Write out the arc info.
405          */
406         if ((froms = (u_short *)malloc(kvp->gpm.fromssize)) == NULL)
407                 errx(8, "cannot allocate froms space");
408         if (kflag) {
409                 i = kvm_read(kvp->kd, (u_long)kvp->gpm.froms, (void *)froms,
410                     kvp->gpm.fromssize);
411         } else {
412                 mib[2] = GPROF_FROMS;
413                 i = kvp->gpm.fromssize;
414                 if (sysctl(mib, 3, froms, &i, NULL, 0) < 0)
415                         i = 0;
416         }
417         if (i != kvp->gpm.fromssize)
418                 errx(9, "read froms: read %lu, got %ld: %s",
419                     kvp->gpm.fromssize, (long)i,
420                     kflag ? kvm_geterr(kvp->kd) : strerror(errno));
421         if ((tos = (struct tostruct *)malloc(kvp->gpm.tossize)) == NULL)
422                 errx(10, "cannot allocate tos space");
423         if (kflag) {
424                 i = kvm_read(kvp->kd, (u_long)kvp->gpm.tos, (void *)tos,
425                     kvp->gpm.tossize);
426         } else {
427                 mib[2] = GPROF_TOS;
428                 i = kvp->gpm.tossize;
429                 if (sysctl(mib, 3, tos, &i, NULL, 0) < 0)
430                         i = 0;
431         }
432         if (i != kvp->gpm.tossize)
433                 errx(11, "read tos: read %lu, got %ld: %s",
434                     kvp->gpm.tossize, (long)i,
435                     kflag ? kvm_geterr(kvp->kd) : strerror(errno));
436         if (debug)
437                 warnx("lowpc 0x%lx, textsize 0x%lx",
438                     (unsigned long)kvp->gpm.lowpc, kvp->gpm.textsize);
439         endfrom = kvp->gpm.fromssize / sizeof(*froms);
440         for (fromindex = 0; fromindex < endfrom; ++fromindex) {
441                 if (froms[fromindex] == 0)
442                         continue;
443                 frompc = (u_long)kvp->gpm.lowpc +
444                     (fromindex * kvp->gpm.hashfraction * sizeof(*froms));
445                 for (toindex = froms[fromindex]; toindex != 0;
446                      toindex = tos[toindex].link) {
447                         if (debug)
448                                 warnx("[mcleanup] frompc 0x%lx selfpc 0x%lx "
449                                     "count %ld", frompc, tos[toindex].selfpc,
450                                     tos[toindex].count);
451                         rawarc.raw_frompc = frompc;
452                         rawarc.raw_selfpc = (u_long)tos[toindex].selfpc;
453                         rawarc.raw_count = tos[toindex].count;
454                         fwrite((char *)&rawarc, sizeof(rawarc), 1, fp);
455                 }
456         }
457         fclose(fp);
458 }
459
460 /*
461  * Get the profiling rate.
462  */
463 int
464 getprofhz(kvp)
465         struct kvmvars *kvp;
466 {
467         size_t size;
468         int mib[2], profrate;
469         struct clockinfo clockrate;
470
471         if (kflag) {
472                 profrate = 1;
473                 if (kvm_read(kvp->kd, nl[N_PROFHZ].n_value, &profrate,
474                     sizeof profrate) != sizeof profrate)
475                         warnx("get clockrate: %s", kvm_geterr(kvp->kd));
476                 return (profrate);
477         }
478         mib[0] = CTL_KERN;
479         mib[1] = KERN_CLOCKRATE;
480         clockrate.profhz = 1;
481         size = sizeof clockrate;
482         if (sysctl(mib, 2, &clockrate, &size, NULL, 0) < 0)
483                 warn("get clockrate");
484         return (clockrate.profhz);
485 }
486
487 /*
488  * Reset the kernel profiling date structures.
489  */
490 void
491 reset(kvp)
492         struct kvmvars *kvp;
493 {
494         char *zbuf;
495         u_long biggest;
496         int mib[3];
497
498         setprof(kvp, GMON_PROF_OFF);
499
500         biggest = kvp->gpm.kcountsize;
501         if (kvp->gpm.fromssize > biggest)
502                 biggest = kvp->gpm.fromssize;
503         if (kvp->gpm.tossize > biggest)
504                 biggest = kvp->gpm.tossize;
505         if ((zbuf = (char *)malloc(biggest)) == NULL)
506                 errx(12, "cannot allocate zbuf space");
507         bzero(zbuf, biggest);
508         if (kflag) {
509                 if (kvm_write(kvp->kd, (u_long)kvp->gpm.kcount, zbuf,
510                     kvp->gpm.kcountsize) != kvp->gpm.kcountsize)
511                         errx(13, "tickbuf zero: %s", kvm_geterr(kvp->kd));
512                 if (kvm_write(kvp->kd, (u_long)kvp->gpm.froms, zbuf,
513                     kvp->gpm.fromssize) != kvp->gpm.fromssize)
514                         errx(14, "froms zero: %s", kvm_geterr(kvp->kd));
515                 if (kvm_write(kvp->kd, (u_long)kvp->gpm.tos, zbuf,
516                     kvp->gpm.tossize) != kvp->gpm.tossize)
517                         errx(15, "tos zero: %s", kvm_geterr(kvp->kd));
518                 return;
519         }
520         (void)seteuid(0);
521         mib[0] = CTL_KERN;
522         mib[1] = KERN_PROF;
523         mib[2] = GPROF_COUNT;
524         if (sysctl(mib, 3, NULL, NULL, zbuf, kvp->gpm.kcountsize) < 0)
525                 err(13, "tickbuf zero");
526         mib[2] = GPROF_FROMS;
527         if (sysctl(mib, 3, NULL, NULL, zbuf, kvp->gpm.fromssize) < 0)
528                 err(14, "froms zero");
529         mib[2] = GPROF_TOS;
530         if (sysctl(mib, 3, NULL, NULL, zbuf, kvp->gpm.tossize) < 0)
531                 err(15, "tos zero");
532         (void)seteuid(getuid());
533         free(zbuf);
534 }