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