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