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