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