Commit | Line | Data |
---|---|---|
528b0614 | 1 | /* |
d7c7cf55 KB |
2 | * Copyright (c) 1983, 1992, 1993 |
3 | * The Regents of the University of California. All rights reserved. | |
c82fa61b | 4 | * |
32ce521f | 5 | * %sccs.include.redist.c% |
528b0614 DF |
6 | */ |
7 | ||
8 | #ifndef lint | |
d7c7cf55 KB |
9 | static char copyright[] = |
10 | "@(#) Copyright (c) 1983, 1992, 1993\n\ | |
11 | The Regents of the University of California. All rights reserved.\n"; | |
4d32c205 | 12 | #endif /* not lint */ |
528b0614 | 13 | |
d841a9ba | 14 | #ifndef lint |
d7c7cf55 | 15 | static char sccsid[] = "@(#)kgmon.c 8.1 (Berkeley) %G%"; |
4d32c205 | 16 | #endif /* not lint */ |
d841a9ba | 17 | |
306e368d | 18 | #include <sys/param.h> |
769cbf71 | 19 | #include <sys/file.h> |
fb8a29c5 | 20 | #include <sys/sysctl.h> |
16d65440 | 21 | #include <sys/gmon.h> |
d22f4fb8 CT |
22 | #include <errno.h> |
23 | #include <kvm.h> | |
24 | #include <limits.h> | |
d841a9ba | 25 | #include <stdio.h> |
d22f4fb8 CT |
26 | #include <stdlib.h> |
27 | #include <string.h> | |
d841a9ba KM |
28 | #include <nlist.h> |
29 | #include <ctype.h> | |
7abf8d65 | 30 | #include <paths.h> |
d841a9ba | 31 | |
d841a9ba | 32 | struct nlist nl[] = { |
16d65440 KM |
33 | #define N_GMONPARAM 0 |
34 | { "__gmonparam" }, | |
8b8a3f9e | 35 | #define N_PROFHZ 1 |
16d65440 | 36 | { "_profhz" }, |
d841a9ba KM |
37 | 0, |
38 | }; | |
39 | ||
71a0f9c4 KM |
40 | struct kvmvars { |
41 | kvm_t *kd; | |
42 | struct gmonparam gpm; | |
43 | }; | |
16d65440 | 44 | |
ecc6febd | 45 | int bflag, hflag, kflag, rflag, pflag; |
78e8943b | 46 | int debug = 0; |
71a0f9c4 KM |
47 | void setprof __P((struct kvmvars *kvp, int state)); |
48 | void dumpstate __P((struct kvmvars *kvp)); | |
49 | void reset __P((struct kvmvars *kvp)); | |
d841a9ba | 50 | |
71a0f9c4 KM |
51 | int |
52 | main(int argc, char **argv) | |
16d65440 | 53 | { |
71a0f9c4 KM |
54 | extern char *optarg; |
55 | extern int optind; | |
56 | int ch, mode, disp, accessmode; | |
57 | struct kvmvars kvmvars; | |
58 | char *system, *kmemf; | |
16d65440 | 59 | |
71a0f9c4 KM |
60 | seteuid(getuid()); |
61 | kmemf = NULL; | |
62 | system = NULL; | |
63 | while ((ch = getopt(argc, argv, "M:N:bhpr")) != EOF) { | |
64 | switch((char)ch) { | |
16d65440 | 65 | |
71a0f9c4 KM |
66 | case 'M': |
67 | kmemf = optarg; | |
68 | kflag = 1; | |
69 | break; | |
16d65440 | 70 | |
71a0f9c4 KM |
71 | case 'N': |
72 | system = optarg; | |
73 | break; | |
74 | ||
75 | case 'b': | |
76 | bflag = 1; | |
77 | break; | |
78 | ||
79 | case 'h': | |
80 | hflag = 1; | |
81 | break; | |
82 | ||
83 | case 'p': | |
84 | pflag = 1; | |
85 | break; | |
86 | ||
87 | case 'r': | |
88 | rflag = 1; | |
89 | break; | |
90 | ||
91 | default: | |
92 | (void)fprintf(stderr, | |
93 | "usage: kgmon [-bhrp] [-M core] [-N system]\n"); | |
94 | exit(1); | |
95 | } | |
96 | } | |
97 | argc -= optind; | |
98 | argv += optind; | |
99 | ||
100 | #define BACKWARD_COMPATIBILITY | |
101 | #ifdef BACKWARD_COMPATIBILITY | |
102 | if (*argv) { | |
103 | system = *argv; | |
104 | if (*++argv) { | |
105 | kmemf = *argv; | |
106 | ++kflag; | |
107 | } | |
108 | } | |
109 | #endif | |
110 | if (system == NULL) | |
111 | system = _PATH_UNIX; | |
112 | accessmode = openfiles(system, kmemf, &kvmvars); | |
113 | mode = getprof(&kvmvars); | |
114 | if (hflag) | |
115 | disp = GMON_PROF_OFF; | |
116 | else if (bflag) | |
117 | disp = GMON_PROF_ON; | |
118 | else | |
119 | disp = mode; | |
120 | if (pflag) | |
121 | dumpstate(&kvmvars); | |
122 | if (rflag) | |
123 | reset(&kvmvars); | |
124 | if (accessmode == O_RDWR) | |
125 | setprof(&kvmvars, disp); | |
126 | (void)fprintf(stdout, "kgmon: kernel profiling is %s.\n", | |
127 | disp == GMON_PROF_OFF ? "off" : "running"); | |
128 | return (0); | |
16d65440 KM |
129 | } |
130 | ||
131 | /* | |
71a0f9c4 | 132 | * Check that profiling is enabled and open any ncessary files. |
16d65440 | 133 | */ |
71a0f9c4 KM |
134 | openfiles(system, kmemf, kvp) |
135 | char *system; | |
136 | char *kmemf; | |
137 | struct kvmvars *kvp; | |
16d65440 | 138 | { |
71a0f9c4 KM |
139 | int mib[3], state, size, openmode; |
140 | char errbuf[_POSIX2_LINE_MAX]; | |
16d65440 | 141 | |
71a0f9c4 KM |
142 | if (!kflag) { |
143 | mib[0] = CTL_KERN; | |
144 | mib[1] = KERN_PROF; | |
145 | mib[2] = GPROF_STATE; | |
146 | size = sizeof state; | |
147 | if (sysctl(mib, 3, &state, &size, NULL, 0) < 0) { | |
16d65440 | 148 | (void)fprintf(stderr, |
71a0f9c4 KM |
149 | "kgmon: profiling not defined in kernel.\n"); |
150 | exit(20); | |
16d65440 | 151 | } |
71a0f9c4 KM |
152 | if (!(bflag || hflag || rflag || |
153 | (pflag && state == GMON_PROF_ON))) | |
154 | return (O_RDONLY); | |
155 | (void)seteuid(0); | |
156 | if (sysctl(mib, 3, NULL, NULL, &state, size) >= 0) | |
157 | return (O_RDWR); | |
158 | (void)seteuid(getuid()); | |
159 | kern_readonly(state); | |
160 | return (O_RDONLY); | |
161 | } | |
162 | openmode = (bflag || hflag || pflag || rflag) ? O_RDWR : O_RDONLY; | |
163 | kvp->kd = kvm_openfiles(system, kmemf, NULL, openmode, errbuf); | |
164 | if (kvp->kd == NULL) { | |
165 | if (openmode == O_RDWR) { | |
166 | openmode = O_RDONLY; | |
167 | kvp->kd = kvm_openfiles(system, kmemf, NULL, O_RDONLY, | |
168 | errbuf); | |
16d65440 | 169 | } |
71a0f9c4 KM |
170 | if (kvp->kd == NULL) { |
171 | (void)fprintf(stderr, "kgmon: kvm_openfiles: %s\n", | |
172 | errbuf); | |
173 | exit(2); | |
174 | } | |
175 | kern_readonly(GMON_PROF_ON); | |
176 | } | |
177 | if (kvm_nlist(kvp->kd, nl) < 0) { | |
178 | (void)fprintf(stderr, "kgmon: %s: no namelist\n", system); | |
179 | exit(3); | |
180 | } | |
181 | if (!nl[N_GMONPARAM].n_value) { | |
182 | (void)fprintf(stderr, | |
183 | "kgmon: profiling not defined in kernel.\n"); | |
184 | exit(20); | |
185 | } | |
186 | return (openmode); | |
187 | } | |
188 | ||
189 | /* | |
190 | * Suppress options that require a writable kernel. | |
191 | */ | |
192 | kern_readonly(mode) | |
193 | int mode; | |
194 | { | |
195 | ||
196 | (void)fprintf(stderr, "kgmon: kernel read-only: "); | |
197 | if (pflag && mode == GMON_PROF_ON) | |
198 | (void)fprintf(stderr, "data may be inconsistent\n"); | |
199 | if (rflag) | |
200 | (void)fprintf(stderr, "-r supressed\n"); | |
201 | if (bflag) | |
202 | (void)fprintf(stderr, "-b supressed\n"); | |
203 | if (hflag) | |
204 | (void)fprintf(stderr, "-h supressed\n"); | |
205 | rflag = bflag = hflag = 0; | |
206 | } | |
207 | ||
208 | /* | |
209 | * Get the state of kernel profiling. | |
210 | */ | |
211 | getprof(kvp) | |
212 | struct kvmvars *kvp; | |
213 | { | |
214 | int mib[3], size; | |
215 | ||
216 | if (kflag) { | |
217 | size = kvm_read(kvp->kd, nl[N_GMONPARAM].n_value, &kvp->gpm, | |
218 | sizeof kvp->gpm); | |
219 | } else { | |
220 | mib[0] = CTL_KERN; | |
221 | mib[1] = KERN_PROF; | |
222 | mib[2] = GPROF_GMONPARAM; | |
223 | size = sizeof kvp->gpm; | |
224 | if (sysctl(mib, 3, &kvp->gpm, &size, NULL, 0) < 0) | |
225 | size = 0; | |
226 | } | |
227 | if (size != sizeof kvp->gpm) { | |
228 | (void)fprintf(stderr, "kgmon: cannot get gmonparam: %s\n", | |
229 | kflag ? kvm_geterr(kvp->kd) : strerror(errno)); | |
230 | exit (4); | |
16d65440 | 231 | } |
71a0f9c4 | 232 | return (kvp->gpm.state); |
16d65440 KM |
233 | } |
234 | ||
235 | /* | |
236 | * Enable or disable kernel profiling according to the state variable. | |
237 | */ | |
238 | void | |
71a0f9c4 KM |
239 | setprof(kvp, state) |
240 | struct kvmvars *kvp; | |
241 | int state; | |
16d65440 KM |
242 | { |
243 | struct gmonparam *p = (struct gmonparam *)nl[N_GMONPARAM].n_value; | |
71a0f9c4 | 244 | int mib[3], sz, oldstate; |
fb8a29c5 KM |
245 | |
246 | sz = sizeof(state); | |
247 | if (!kflag) { | |
248 | mib[0] = CTL_KERN; | |
249 | mib[1] = KERN_PROF; | |
250 | mib[2] = GPROF_STATE; | |
71a0f9c4 KM |
251 | if (sysctl(mib, 3, &oldstate, &sz, NULL, 0) < 0) |
252 | goto bad; | |
253 | if (oldstate == state) | |
fb8a29c5 | 254 | return; |
71a0f9c4 KM |
255 | (void)seteuid(0); |
256 | if (sysctl(mib, 3, NULL, NULL, &state, sz) >= 0) { | |
257 | (void)seteuid(getuid()); | |
258 | return; | |
259 | } | |
260 | (void)seteuid(getuid()); | |
261 | } else if (kvm_write(kvp->kd, (u_long)&p->state, (void *)&state, sz) | |
262 | == sz) | |
fb8a29c5 | 263 | return; |
71a0f9c4 | 264 | bad: |
fb8a29c5 KM |
265 | (void)fprintf(stderr, "kgmon: warning: cannot turn profiling %s\n", |
266 | state == GMON_PROF_OFF ? "off" : "on"); | |
16d65440 KM |
267 | } |
268 | ||
269 | /* | |
270 | * Build the gmon.out file. | |
271 | */ | |
272 | void | |
71a0f9c4 KM |
273 | dumpstate(kvp) |
274 | struct kvmvars *kvp; | |
16d65440 KM |
275 | { |
276 | register FILE *fp; | |
277 | struct rawarc rawarc; | |
278 | struct tostruct *tos; | |
279 | u_long frompc, addr; | |
71a0f9c4 KM |
280 | u_short *froms, *tickbuf; |
281 | int mib[3], i; | |
282 | struct gmonhdr h; | |
16d65440 | 283 | int fromindex, endfrom, toindex; |
16d65440 | 284 | |
71a0f9c4 | 285 | setprof(kvp, GMON_PROF_OFF); |
16d65440 KM |
286 | fp = fopen("gmon.out", "w"); |
287 | if (fp == 0) { | |
288 | perror("gmon.out"); | |
289 | return; | |
290 | } | |
71a0f9c4 KM |
291 | |
292 | /* | |
293 | * Build the gmon header and write it to a file. | |
294 | */ | |
295 | bzero(&h, sizeof(h)); | |
296 | h.lpc = kvp->gpm.lowpc; | |
297 | h.hpc = kvp->gpm.highpc; | |
298 | h.ncnt = kvp->gpm.kcountsize + sizeof(h); | |
299 | h.version = GMONVERSION; | |
300 | h.profrate = getprofhz(kvp); | |
301 | fwrite((char *)&h, sizeof(h), 1, fp); | |
302 | ||
303 | /* | |
304 | * Write out the tick buffer. | |
305 | */ | |
306 | mib[0] = CTL_KERN; | |
307 | mib[1] = KERN_PROF; | |
308 | if ((tickbuf = (u_short *)malloc(kvp->gpm.kcountsize)) == NULL) { | |
309 | fprintf(stderr, "kgmon: cannot allocate kcount space\n"); | |
310 | exit (5); | |
16d65440 | 311 | } |
71a0f9c4 KM |
312 | if (kflag) { |
313 | i = kvm_read(kvp->kd, (u_long)kvp->gpm.kcount, (void *)tickbuf, | |
314 | kvp->gpm.kcountsize); | |
315 | } else { | |
316 | mib[2] = GPROF_COUNT; | |
317 | i = kvp->gpm.kcountsize; | |
318 | if (sysctl(mib, 3, tickbuf, &i, NULL, 0) < 0) | |
319 | i = 0; | |
320 | } | |
321 | if (i != kvp->gpm.kcountsize) { | |
322 | (void)fprintf(stderr, "kgmon: read ticks: read %u, got %d: %s", | |
323 | kvp->gpm.kcountsize, i, | |
324 | kflag ? kvm_geterr(kvp->kd) : strerror(errno)); | |
16d65440 KM |
325 | exit(6); |
326 | } | |
71a0f9c4 KM |
327 | if ((fwrite(tickbuf, kvp->gpm.kcountsize, 1, fp)) != 1) { |
328 | perror("kgmon: writing tocks to gmon.out"); | |
329 | exit(7); | |
330 | } | |
331 | free(tickbuf); | |
332 | ||
333 | /* | |
334 | * Write out the arc info. | |
335 | */ | |
336 | if ((froms = (u_short *)malloc(kvp->gpm.fromssize)) == NULL) { | |
337 | fprintf(stderr, "kgmon: cannot allocate froms space\n"); | |
338 | exit (8); | |
339 | } | |
340 | if (kflag) { | |
341 | i = kvm_read(kvp->kd, (u_long)kvp->gpm.froms, (void *)froms, | |
342 | kvp->gpm.fromssize); | |
343 | } else { | |
344 | mib[2] = GPROF_FROMS; | |
345 | i = kvp->gpm.fromssize; | |
346 | if (sysctl(mib, 3, froms, &i, NULL, 0) < 0) | |
347 | i = 0; | |
348 | } | |
349 | if (i != kvp->gpm.fromssize) { | |
350 | (void)fprintf(stderr, "kgmon: read froms: read %u, got %d: %s", | |
351 | kvp->gpm.fromssize, i, | |
352 | kflag ? kvm_geterr(kvp->kd) : strerror(errno)); | |
353 | exit(9); | |
354 | } | |
355 | if ((tos = (struct tostruct *)malloc(kvp->gpm.tossize)) == NULL) { | |
356 | fprintf(stderr, "kgmon: cannot allocate tos space\n"); | |
357 | exit(10); | |
358 | } | |
359 | if (kflag) { | |
360 | i = kvm_read(kvp->kd, (u_long)kvp->gpm.tos, (void *)tos, | |
361 | kvp->gpm.tossize); | |
362 | } else { | |
363 | mib[2] = GPROF_TOS; | |
364 | i = kvp->gpm.tossize; | |
365 | if (sysctl(mib, 3, tos, &i, NULL, 0) < 0) | |
366 | i = 0; | |
367 | } | |
368 | if (i != kvp->gpm.tossize) { | |
369 | (void)fprintf(stderr, "kgmon: read tos: read %u, got %d: %s", | |
370 | kvp->gpm.tossize, i, | |
371 | kflag ? kvm_geterr(kvp->kd) : strerror(errno)); | |
372 | exit(11); | |
373 | } | |
16d65440 | 374 | if (debug) |
71a0f9c4 KM |
375 | (void)fprintf(stderr, "kgmon: lowpc 0x%x, textsize 0x%x\n", |
376 | kvp->gpm.lowpc, kvp->gpm.textsize); | |
377 | endfrom = kvp->gpm.fromssize / sizeof(*froms); | |
16d65440 KM |
378 | for (fromindex = 0; fromindex < endfrom; ++fromindex) { |
379 | if (froms[fromindex] == 0) | |
380 | continue; | |
71a0f9c4 KM |
381 | frompc = (u_long)kvp->gpm.lowpc + |
382 | (fromindex * kvp->gpm.hashfraction * sizeof(*froms)); | |
16d65440 KM |
383 | for (toindex = froms[fromindex]; toindex != 0; |
384 | toindex = tos[toindex].link) { | |
385 | if (debug) | |
386 | (void)fprintf(stderr, | |
71a0f9c4 KM |
387 | "%s: [mcleanup] frompc 0x%x selfpc 0x%x count %d\n", |
388 | "kgmon", frompc, tos[toindex].selfpc, | |
389 | tos[toindex].count); | |
16d65440 KM |
390 | rawarc.raw_frompc = frompc; |
391 | rawarc.raw_selfpc = (u_long)tos[toindex].selfpc; | |
392 | rawarc.raw_count = tos[toindex].count; | |
393 | fwrite((char *)&rawarc, sizeof(rawarc), 1, fp); | |
394 | } | |
395 | } | |
396 | fclose(fp); | |
397 | } | |
398 | ||
399 | /* | |
71a0f9c4 | 400 | * Get the profiling rate. |
16d65440 KM |
401 | */ |
402 | int | |
71a0f9c4 KM |
403 | getprofhz(kvp) |
404 | struct kvmvars *kvp; | |
16d65440 | 405 | { |
71a0f9c4 KM |
406 | int mib[2], size, profrate; |
407 | struct clockinfo clockrate; | |
408 | ||
409 | if (kflag) { | |
410 | profrate = 1; | |
411 | if (kvm_read(kvp->kd, nl[N_PROFHZ].n_value, &profrate, | |
412 | sizeof profrate) != sizeof profrate) | |
413 | (void)fprintf(stderr, "kgmon: get clockrate: %s\n", | |
414 | kvm_geterr(kvp->kd)); | |
415 | return (profrate); | |
16d65440 | 416 | } |
71a0f9c4 KM |
417 | mib[0] = CTL_KERN; |
418 | mib[1] = KERN_CLOCKRATE; | |
419 | clockrate.profhz = 1; | |
420 | size = sizeof clockrate; | |
421 | if (sysctl(mib, 2, &clockrate, &size, NULL, 0) < 0) | |
422 | (void)fprintf(stderr, "kgmon: get clockrate: %s\n", | |
423 | strerror(errno)); | |
424 | return (clockrate.profhz); | |
16d65440 KM |
425 | } |
426 | ||
427 | /* | |
428 | * Reset the kernel profiling date structures. | |
429 | */ | |
430 | void | |
71a0f9c4 KM |
431 | reset(kvp) |
432 | struct kvmvars *kvp; | |
16d65440 | 433 | { |
71a0f9c4 KM |
434 | char *zbuf; |
435 | u_long biggest; | |
436 | int mib[3]; | |
437 | ||
438 | setprof(kvp, GMON_PROF_OFF); | |
439 | ||
440 | biggest = kvp->gpm.kcountsize; | |
441 | if (kvp->gpm.fromssize > biggest) | |
442 | biggest = kvp->gpm.fromssize; | |
443 | if (kvp->gpm.tossize > biggest) | |
444 | biggest = kvp->gpm.tossize; | |
445 | if ((zbuf = (char *)malloc(biggest)) == NULL) { | |
446 | fprintf(stderr, "kgmon: cannot allocate zbuf space\n"); | |
447 | exit(12); | |
16d65440 | 448 | } |
71a0f9c4 KM |
449 | bzero(zbuf, biggest); |
450 | if (kflag) { | |
451 | if (kvm_write(kvp->kd, (u_long)kvp->gpm.kcount, zbuf, | |
452 | kvp->gpm.kcountsize) != kvp->gpm.kcountsize) { | |
453 | (void)fprintf(stderr, "kgmon: tickbuf zero: %s\n", | |
454 | kvm_geterr(kvp->kd)); | |
455 | exit(13); | |
4d32c205 | 456 | } |
71a0f9c4 KM |
457 | if (kvm_write(kvp->kd, (u_long)kvp->gpm.froms, zbuf, |
458 | kvp->gpm.fromssize) != kvp->gpm.fromssize) { | |
459 | (void)fprintf(stderr, "kgmon: froms zero: %s\n", | |
460 | kvm_geterr(kvp->kd)); | |
461 | exit(14); | |
d22f4fb8 | 462 | } |
71a0f9c4 KM |
463 | if (kvm_write(kvp->kd, (u_long)kvp->gpm.tos, zbuf, |
464 | kvp->gpm.tossize) != kvp->gpm.tossize) { | |
465 | (void)fprintf(stderr, "kgmon: tos zero: %s\n", | |
466 | kvm_geterr(kvp->kd)); | |
467 | exit(15); | |
d841a9ba | 468 | } |
71a0f9c4 | 469 | return; |
d841a9ba | 470 | } |
71a0f9c4 KM |
471 | (void)seteuid(0); |
472 | mib[0] = CTL_KERN; | |
473 | mib[1] = KERN_PROF; | |
474 | mib[2] = GPROF_COUNT; | |
475 | if (sysctl(mib, 3, NULL, NULL, zbuf, kvp->gpm.kcountsize) < 0) { | |
476 | (void)fprintf(stderr, "kgmon: tickbuf zero: %s\n", | |
477 | strerror(errno)); | |
478 | exit(13); | |
d22f4fb8 | 479 | } |
71a0f9c4 KM |
480 | mib[2] = GPROF_FROMS; |
481 | if (sysctl(mib, 3, NULL, NULL, zbuf, kvp->gpm.fromssize) < 0) { | |
482 | (void)fprintf(stderr, "kgmon: froms zero: %s\n", | |
483 | strerror(errno)); | |
484 | exit(14); | |
d841a9ba | 485 | } |
71a0f9c4 KM |
486 | mib[2] = GPROF_TOS; |
487 | if (sysctl(mib, 3, NULL, NULL, zbuf, kvp->gpm.tossize) < 0) { | |
488 | (void)fprintf(stderr, "kgmon: tos zero: %s\n", | |
489 | strerror(errno)); | |
490 | exit(15); | |
79ec4952 | 491 | } |
71a0f9c4 KM |
492 | (void)seteuid(getuid()); |
493 | free(zbuf); | |
d22f4fb8 | 494 | } |