1 /* 2 * Copyright (c) 1983, 1992 The Regents of the University of California. 3 * All rights reserved. 4 * 5 * %sccs.include.redist.c% 6 */ 7 8 #ifndef lint 9 char copyright[] = 10 "@(#) Copyright (c) 1983, 1992 The Regents of the University of California.\n\ 11 All rights reserved.\n"; 12 #endif /* not lint */ 13 14 #ifndef lint 15 static char sccsid[] = "@(#)kgmon.c 5.15 (Berkeley) 04/20/93"; 16 #endif /* not lint */ 17 18 #include <sys/param.h> 19 #include <sys/file.h> 20 #include <sys/gmon.h> 21 #include <errno.h> 22 #include <kvm.h> 23 #include <limits.h> 24 #include <stdio.h> 25 #include <stdlib.h> 26 #include <string.h> 27 #include <nlist.h> 28 #include <ctype.h> 29 #include <paths.h> 30 31 struct nlist nl[] = { 32 #define N_GMONPARAM 0 33 { "__gmonparam" }, 34 #define N_PROFHZ 1 35 { "_profhz" }, 36 0, 37 }; 38 39 /* 40 * We should call this _gmonparam for consistency, but that would cause a 41 * problem if we want to profile this program itself. 42 */ 43 struct gmonparam gmonparam; 44 45 int profhz; 46 47 kvm_t *kd; 48 int bflag, hflag, kflag, rflag, pflag; 49 int debug = 0; 50 51 #define KREAD(kd, addr, s)\ 52 kvm_read(kd, addr, (void *)(s), sizeof*(s)) != (sizeof*(s)) 53 54 /* 55 * Build the gmon header and write it to a file. 56 */ 57 void 58 dumphdr(FILE *fp, struct gmonparam *p) 59 { 60 struct gmonhdr h; 61 62 /* zero out the unused fields */ 63 bzero(&h, sizeof(h)); 64 65 h.lpc = p->lowpc; 66 h.hpc = p->highpc; 67 h.ncnt = p->kcountsize + sizeof(h); 68 h.version = GMONVERSION; 69 h.profrate = profhz; 70 71 fwrite((char *)&h, sizeof(h), 1, fp); 72 } 73 74 /* 75 * Dump a range of kernel memory to a file. 76 */ 77 void 78 dumpbuf(FILE *fp, u_long addr, int cc) 79 { 80 int ret, n; 81 char buf[8192]; 82 83 while (cc > 0) { 84 n = MIN(cc, sizeof(buf)); 85 if ((ret = kvm_read(kd, addr, buf, n)) != n) { 86 (void)fprintf(stderr, 87 "kgmon: read kmem: read %d, got %d: %s\n", 88 n, ret, kvm_geterr(kd)); 89 exit(4); 90 } 91 if ((ret = fwrite(buf, n, 1, fp)) != 1) { 92 perror("kgmon: gmon.out"); 93 exit(1); 94 } 95 addr += n; 96 cc -= n; 97 } 98 } 99 100 /* 101 * Enable or disable kernel profiling according to the state variable. 102 */ 103 void 104 setprof(int state) 105 { 106 struct gmonparam *p = (struct gmonparam *)nl[N_GMONPARAM].n_value; 107 108 if (kvm_write(kd, (u_long)&p->state, (void *)&state, sizeof(state)) != 109 sizeof(state)) 110 (void)fprintf(stderr, 111 "kgmon: warning: can't turn profiling %s\n", 112 state == GMON_PROF_OFF ? "off" : "on"); 113 } 114 115 /* 116 * Build the gmon.out file. 117 */ 118 void 119 dumpstate(struct gmonparam *p) 120 { 121 register FILE *fp; 122 struct rawarc rawarc; 123 struct tostruct *tos; 124 u_long frompc, addr; 125 u_short *froms; 126 int i, n; 127 int fromindex, endfrom, toindex; 128 129 setprof(GMON_PROF_OFF); 130 fp = fopen("gmon.out", "w"); 131 if (fp == 0) { 132 perror("gmon.out"); 133 return; 134 } 135 dumphdr(fp, p); 136 dumpbuf(fp, (u_long)p->kcount, p->kcountsize); 137 138 froms = (u_short *)malloc(p->fromssize); 139 i = kvm_read(kd, (u_long)p->froms, (void *)froms, p->fromssize); 140 if (i != p->fromssize) { 141 (void)fprintf(stderr, "kgmon: read kmem: read %u, got %d: %s", 142 p->fromssize, i, strerror(errno)); 143 exit(5); 144 } 145 tos = (struct tostruct *)malloc(p->tossize); 146 i = kvm_read(kd, (u_long)p->tos, (void *)tos, p->tossize); 147 if (i != p->tossize) { 148 (void)fprintf(stderr, "kgmon: read kmem: read %u, got %d: %s", 149 p->tossize, i, kvm_geterr(kd)); 150 exit(6); 151 } 152 if (debug) 153 (void)fprintf(stderr, "lowpc 0x%x, textsize 0x%x\n", 154 p->lowpc, p->textsize); 155 endfrom = p->fromssize / sizeof(*froms); 156 for (fromindex = 0; fromindex < endfrom; ++fromindex) { 157 if (froms[fromindex] == 0) 158 continue; 159 frompc = (u_long)p->lowpc + 160 (fromindex * HASHFRACTION * sizeof(*froms)); 161 for (toindex = froms[fromindex]; toindex != 0; 162 toindex = tos[toindex].link) { 163 if (debug) 164 (void)fprintf(stderr, 165 "[mcleanup] frompc 0x%x selfpc 0x%x count %d\n" , 166 frompc, tos[toindex].selfpc, tos[toindex].count); 167 rawarc.raw_frompc = frompc; 168 rawarc.raw_selfpc = (u_long)tos[toindex].selfpc; 169 rawarc.raw_count = tos[toindex].count; 170 fwrite((char *)&rawarc, sizeof(rawarc), 1, fp); 171 } 172 } 173 fclose(fp); 174 } 175 176 /* 177 * Zero out a region of kernel memory. 178 */ 179 int 180 kzero(u_long addr, int cc) 181 { 182 static char zbuf[MAXBSIZE]; 183 184 while (cc > 0) { 185 register int n = MIN(cc, sizeof(zbuf)); 186 187 if (kvm_write(kd, addr, zbuf, n) != n) 188 return (-1); 189 addr += n; 190 cc -= n; 191 } 192 return (0); 193 } 194 195 /* 196 * Reset the kernel profiling date structures. 197 */ 198 void 199 reset(struct gmonparam *p) 200 { 201 202 setprof(GMON_PROF_OFF); 203 204 if (kzero((u_long)p->kcount, p->kcountsize)) { 205 (void)fprintf(stderr, "kgmon: sbuf write: %s\n", 206 kvm_geterr(kd)); 207 exit(7); 208 } 209 if (kzero((u_long)p->froms, p->fromssize)) { 210 (void)fprintf(stderr, "kgmon: kfroms write: %s\n", 211 kvm_geterr(kd)); 212 exit(8); 213 } 214 if (kzero((u_long)p->tos, p->tossize)) { 215 (void)fprintf(stderr, "kgmon: ktos write: %s\n", 216 kvm_geterr(kd)); 217 exit(9); 218 } 219 } 220 221 int 222 main(int argc, char **argv) 223 { 224 extern char *optarg; 225 extern int optind; 226 int ch, mode, disp, openmode; 227 char *system, *kmemf; 228 char errbuf[_POSIX2_LINE_MAX]; 229 230 kmemf = NULL; 231 system = NULL; 232 while ((ch = getopt(argc, argv, "M:N:bhpr")) != EOF) { 233 switch((char)ch) { 234 235 case 'M': 236 kmemf = optarg; 237 kflag = 1; 238 break; 239 240 case 'N': 241 system = optarg; 242 break; 243 244 case 'b': 245 bflag = 1; 246 break; 247 248 case 'h': 249 hflag = 1; 250 break; 251 252 case 'p': 253 pflag = 1; 254 break; 255 256 case 'r': 257 rflag = 1; 258 break; 259 260 default: 261 (void)fprintf(stderr, 262 "usage: kgmon [-bhrp] [-M core] [-N system]\n"); 263 exit(1); 264 } 265 } 266 argc -= optind; 267 argv += optind; 268 269 #define BACKWARD_COMPATIBILITY 270 #ifdef BACKWARD_COMPATIBILITY 271 if (*argv) { 272 system = *argv; 273 if (*++argv) { 274 kmemf = *argv; 275 ++kflag; 276 } 277 } 278 #endif 279 if (system == NULL) 280 system = _PATH_UNIX; 281 openmode = (bflag || hflag || pflag || rflag) ? O_RDWR : O_RDONLY; 282 kd = kvm_openfiles(system, kmemf, NULL, openmode, errbuf); 283 if (kd == NULL) { 284 if (openmode == O_RDWR) { 285 openmode = O_RDONLY; 286 kd = kvm_openfiles(system, kmemf, NULL, O_RDONLY, 287 errbuf); 288 } 289 if (kd == NULL) { 290 (void)fprintf(stderr, "kgmon: kvm_openfiles: %s\n", 291 errbuf); 292 exit(2); 293 } 294 (void)fprintf(stderr, "kgmon: kernel opened read-only\n"); 295 if (rflag) 296 (void)fprintf(stderr, "-r supressed\n"); 297 if (bflag) 298 (void)fprintf(stderr, "-b supressed\n"); 299 if (hflag) 300 (void)fprintf(stderr, "-h supressed\n"); 301 rflag = bflag = hflag = 0; 302 } 303 if (kvm_nlist(kd, nl) < 0) { 304 (void)fprintf(stderr, "kgmon: %s: no namelist\n", system); 305 exit(2); 306 } 307 if (!nl[N_GMONPARAM].n_value) { 308 (void)fprintf(stderr, 309 "kgmon: profiling not defined in kernel.\n"); 310 exit(10); 311 } 312 if (KREAD(kd, nl[N_GMONPARAM].n_value, &gmonparam)) 313 (void)fprintf(stderr, 314 "kgmon: read kmem: %s\n", kvm_geterr(kd)); 315 if (KREAD(kd, nl[N_PROFHZ].n_value, &profhz)) 316 (void)fprintf(stderr, "kgmon: read kmem: %s\n", 317 kvm_geterr(kd)); 318 319 mode = gmonparam.state; 320 if (hflag) 321 disp = GMON_PROF_OFF; 322 else if (bflag) 323 disp = GMON_PROF_ON; 324 else 325 disp = mode; 326 if (pflag) { 327 if (openmode == O_RDONLY && mode == GMON_PROF_ON) 328 (void)fprintf(stderr, "data may be inconsistent\n"); 329 dumpstate(&gmonparam); 330 } 331 if (rflag) 332 reset(&gmonparam); 333 if (openmode == O_RDWR) 334 setprof(disp); 335 (void)fprintf(stdout, "kernel profiling is %s.\n", 336 disp == GMON_PROF_OFF ? "off" : "running"); 337 338 return (0); 339 } 340