1 /* $OpenBSD: gprof.c,v 1.26 2016/10/08 19:55:39 guenther Exp $ */ 2 /* $NetBSD: gprof.c,v 1.8 1995/04/19 07:15:59 cgd Exp $ */ 3 4 /* 5 * Copyright (c) 1983, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33 #include "gprof.h" 34 35 int valcmp(const void *, const void *); 36 37 static struct gmonhdr gmonhdr; 38 extern char *__progname; 39 40 int 41 main(int argc, char *argv[]) 42 { 43 char **sp; 44 nltype **timesortnlp; 45 char **defaultEs; 46 47 if (pledge("stdio rpath wpath cpath", NULL) == -1) 48 err(1, NULL); 49 50 --argc; 51 argv++; 52 debug = 0; 53 bflag = TRUE; 54 while ( *argv != 0 && **argv == '-' ) { 55 (*argv)++; 56 switch ( **argv ) { 57 case 'a': 58 aflag = TRUE; 59 break; 60 case 'b': 61 bflag = FALSE; 62 break; 63 case 'C': 64 Cflag = TRUE; 65 cyclethreshold = atoi( *++argv ); 66 break; 67 case 'c': 68 #if defined(__i386__) || defined(__mips64__) 69 cflag = TRUE; 70 #else 71 fprintf(stderr, "%s: -c isn't supported on this architecture yet\n", __progname); 72 exit(1); 73 #endif 74 break; 75 case 'd': 76 dflag = TRUE; 77 setvbuf(stdout, NULL, _IOLBF, 0); 78 debug |= atoi( *++argv ); 79 debug |= ANYDEBUG; 80 # ifdef DEBUG 81 printf("[main] debug = %d\n", debug); 82 # else /* not DEBUG */ 83 warnx("-d ignored"); 84 # endif /* DEBUG */ 85 break; 86 case 'E': 87 ++argv; 88 addlist( Elist , *argv ); 89 Eflag = TRUE; 90 addlist( elist , *argv ); 91 eflag = TRUE; 92 break; 93 case 'e': 94 addlist( elist , *++argv ); 95 eflag = TRUE; 96 break; 97 case 'F': 98 ++argv; 99 addlist( Flist , *argv ); 100 Fflag = TRUE; 101 addlist( flist , *argv ); 102 fflag = TRUE; 103 break; 104 case 'f': 105 addlist( flist , *++argv ); 106 fflag = TRUE; 107 break; 108 case 'k': 109 addlist( kfromlist , *++argv ); 110 addlist( ktolist , *++argv ); 111 kflag = TRUE; 112 break; 113 case 's': 114 sflag = TRUE; 115 break; 116 case 'z': 117 zflag = TRUE; 118 break; 119 } 120 argv++; 121 } 122 if ( *argv != 0 ) { 123 a_outname = *argv; 124 argv++; 125 } else { 126 a_outname = A_OUTNAME; 127 } 128 if ( *argv != 0 ) { 129 gmonname = *argv; 130 argv++; 131 } else { 132 gmonname = GMONNAME; 133 } 134 if ( sflag == FALSE ) { 135 if (pledge("stdio rpath", NULL) == -1) 136 err(1, "pledge"); 137 } 138 /* 139 * get information about a.out file. 140 */ 141 if (getnfile(a_outname, &defaultEs) == -1) 142 errx(1, "%s: bad format", a_outname); 143 /* 144 * sort symbol table. 145 */ 146 qsort(nl, nname, sizeof(nltype), valcmp); 147 /* 148 * turn off default functions 149 */ 150 for ( sp = &defaultEs[0] ; *sp ; sp++ ) { 151 Eflag = TRUE; 152 addlist( Elist , *sp ); 153 eflag = TRUE; 154 addlist( elist , *sp ); 155 } 156 /* 157 * get information about mon.out file(s). 158 */ 159 do { 160 getpfile( gmonname ); 161 if ( *argv != 0 ) { 162 gmonname = *argv; 163 } 164 } while ( *argv++ != 0 ); 165 /* 166 * how many ticks per second? 167 * if we can't tell, report time in ticks. 168 */ 169 if (hz == 0) { 170 hz = 1; 171 warnx("time is in ticks, not seconds"); 172 } 173 /* 174 * dump out a gmon.sum file if requested 175 */ 176 if ( sflag ) { 177 dumpsum( GMONSUM ); 178 } 179 /* 180 * assign samples to procedures 181 */ 182 asgnsamples(); 183 /* 184 * assemble the dynamic profile 185 */ 186 timesortnlp = doarcs(); 187 /* 188 * print the dynamic profile 189 */ 190 printgprof( timesortnlp ); 191 /* 192 * print the flat profile 193 */ 194 printprof(); 195 /* 196 * print the index 197 */ 198 printindex(); 199 200 return (0); 201 } 202 203 /* 204 * information from a gmon.out file is in two parts: 205 * an array of sampling hits within pc ranges, 206 * and the arcs. 207 */ 208 void 209 getpfile(const char *filename) 210 { 211 FILE *pfile; 212 struct rawarc arc; 213 214 pfile = openpfile(filename); 215 readsamples(pfile); 216 /* 217 * the rest of the file consists of 218 * a bunch of <from,self,count> tuples. 219 */ 220 while ( fread( &arc , sizeof arc , 1 , pfile ) == 1 ) { 221 # ifdef DEBUG 222 if ( debug & SAMPLEDEBUG ) { 223 printf( "[getpfile] frompc 0x%lx selfpc 0x%lx count %ld\n" , 224 arc.raw_frompc , arc.raw_selfpc , arc.raw_count ); 225 } 226 # endif /* DEBUG */ 227 /* 228 * add this arc 229 */ 230 tally( &arc ); 231 } 232 fclose(pfile); 233 } 234 235 FILE * 236 openpfile(const char *filename) 237 { 238 struct gmonhdr tmp; 239 FILE *pfile; 240 int size; 241 int rate; 242 243 if((pfile = fopen(filename, "r")) == NULL) 244 err(1, "fopen: %s", filename); 245 if (fread(&tmp, sizeof(struct gmonhdr), 1, pfile) != 1) 246 errx(1, "%s: bad gmon header", filename); 247 if ( s_highpc != 0 && ( tmp.lpc != gmonhdr.lpc || 248 tmp.hpc != gmonhdr.hpc || tmp.ncnt != gmonhdr.ncnt)) 249 errx(1, "%s: incompatible with first gmon file", filename); 250 gmonhdr = tmp; 251 if ( gmonhdr.version == GMONVERSION ) { 252 rate = gmonhdr.profrate; 253 size = sizeof(struct gmonhdr); 254 } else { 255 fseek(pfile, sizeof(struct ophdr), SEEK_SET); 256 size = sizeof(struct ophdr); 257 gmonhdr.profrate = rate = hertz(); 258 gmonhdr.version = GMONVERSION; 259 } 260 if (hz == 0) { 261 hz = rate; 262 } else if (hz != rate) 263 errx(1, "%s: profile clock rate (%d) incompatible with clock rate " 264 "(%ld) in first gmon file", filename, rate, hz); 265 s_lowpc = (unsigned long) gmonhdr.lpc; 266 s_highpc = (unsigned long) gmonhdr.hpc; 267 lowpc = (unsigned long)gmonhdr.lpc / sizeof(UNIT); 268 highpc = (unsigned long)gmonhdr.hpc / sizeof(UNIT); 269 sampbytes = gmonhdr.ncnt - size; 270 nsamples = sampbytes / sizeof (UNIT); 271 # ifdef DEBUG 272 if ( debug & SAMPLEDEBUG ) { 273 printf( "[openpfile] hdr.lpc 0x%lx hdr.hpc 0x%lx hdr.ncnt %d\n", 274 gmonhdr.lpc , gmonhdr.hpc , gmonhdr.ncnt ); 275 printf( "[openpfile] s_lowpc 0x%lx s_highpc 0x%lx\n" , 276 s_lowpc , s_highpc ); 277 printf( "[openpfile] lowpc 0x%lx highpc 0x%lx\n" , 278 lowpc , highpc ); 279 printf( "[openpfile] sampbytes %d nsamples %d\n" , 280 sampbytes , nsamples ); 281 printf( "[openpfile] sample rate %ld\n" , hz ); 282 } 283 # endif /* DEBUG */ 284 return(pfile); 285 } 286 287 void 288 tally(struct rawarc *rawp) 289 { 290 nltype *parentp; 291 nltype *childp; 292 293 parentp = nllookup( rawp -> raw_frompc ); 294 childp = nllookup( rawp -> raw_selfpc ); 295 if ( parentp == 0 || childp == 0 ) 296 return; 297 if ( kflag 298 && onlist( kfromlist , parentp -> name ) 299 && onlist( ktolist , childp -> name ) ) { 300 return; 301 } 302 childp -> ncall += rawp -> raw_count; 303 # ifdef DEBUG 304 if ( debug & TALLYDEBUG ) { 305 printf( "[tally] arc from %s to %s traversed %ld times\n" , 306 parentp -> name , childp -> name , rawp -> raw_count ); 307 } 308 # endif /* DEBUG */ 309 addarc( parentp , childp , rawp -> raw_count ); 310 } 311 312 /* 313 * dump out the gmon.sum file 314 */ 315 void 316 dumpsum(const char *sumfile) 317 { 318 nltype *nlp; 319 arctype *arcp; 320 struct rawarc arc; 321 FILE *sfile; 322 323 if ( ( sfile = fopen ( sumfile , "w" ) ) == NULL ) 324 err(1, "fopen: %s", sumfile); 325 /* 326 * dump the header; use the last header read in 327 */ 328 if ( fwrite( &gmonhdr , sizeof gmonhdr , 1 , sfile ) != 1 ) 329 err(1, "fwrite: %s", sumfile); 330 /* 331 * dump the samples 332 */ 333 if (fwrite(samples, sizeof (UNIT), nsamples, sfile) != nsamples) 334 err(1, "fwrite: %s", sumfile); 335 /* 336 * dump the normalized raw arc information 337 */ 338 for ( nlp = nl ; nlp < npe ; nlp++ ) { 339 for ( arcp = nlp -> children ; arcp ; arcp = arcp -> arc_childlist ) { 340 arc.raw_frompc = arcp -> arc_parentp -> value; 341 arc.raw_selfpc = arcp -> arc_childp -> value; 342 arc.raw_count = arcp -> arc_count; 343 if (fwrite ( &arc , sizeof arc , 1 , sfile ) != 1) 344 err(1, "fwrite: %s", sumfile); 345 # ifdef DEBUG 346 if ( debug & SAMPLEDEBUG ) { 347 printf( "[dumpsum] frompc 0x%lx selfpc 0x%lx count %ld\n" , 348 arc.raw_frompc , arc.raw_selfpc , arc.raw_count ); 349 } 350 # endif /* DEBUG */ 351 } 352 } 353 fclose( sfile ); 354 } 355 356 int 357 valcmp(const void *vp1, const void *vp2) 358 { 359 const nltype *p1 = vp1; 360 const nltype *p2 = vp2; 361 362 if ( p1 -> value < p2 -> value ) { 363 return LESSTHAN; 364 } 365 if ( p1 -> value > p2 -> value ) { 366 return GREATERTHAN; 367 } 368 return EQUALTO; 369 } 370 371 void 372 readsamples(FILE *pfile) 373 { 374 UNIT sample; 375 int i; 376 377 if (samples == 0) { 378 samples = calloc(sampbytes, sizeof (UNIT)); 379 if (samples == 0) 380 errx(1, "No room for %ld sample pc's", sampbytes / sizeof (UNIT)); 381 } 382 for (i = 0; i < nsamples; i++) { 383 fread(&sample, sizeof (UNIT), 1, pfile); 384 if (feof(pfile)) 385 break; 386 samples[i] += sample; 387 } 388 if (i != nsamples) 389 errx(1, "unexpected EOF after reading %d/%d samples", i, nsamples ); 390 } 391 392 /* 393 * Assign samples to the procedures to which they belong. 394 * 395 * There are three cases as to where pcl and pch can be 396 * with respect to the routine entry addresses svalue0 and svalue1 397 * as shown in the following diagram. overlap computes the 398 * distance between the arrows, the fraction of the sample 399 * that is to be credited to the routine which starts at svalue0. 400 * 401 * svalue0 svalue1 402 * | | 403 * v v 404 * 405 * +-----------------------------------------------+ 406 * | | 407 * | ->| |<- ->| |<- ->| |<- | 408 * | | | | | | 409 * +---------+ +---------+ +---------+ 410 * 411 * ^ ^ ^ ^ ^ ^ 412 * | | | | | | 413 * pcl pch pcl pch pcl pch 414 * 415 * For the vax we assert that samples will never fall in the first 416 * two bytes of any routine, since that is the entry mask, 417 * thus we give call alignentries() to adjust the entry points if 418 * the entry mask falls in one bucket but the code for the routine 419 * doesn't start until the next bucket. In conjunction with the 420 * alignment of routine addresses, this should allow us to have 421 * only one sample for every four bytes of text space and never 422 * have any overlap (the two end cases, above). 423 */ 424 void 425 asgnsamples(void) 426 { 427 int j; 428 UNIT ccnt; 429 double time; 430 unsigned long pcl, pch; 431 unsigned long i; 432 unsigned long overlap; 433 unsigned long svalue0, svalue1; 434 435 /* read samples and assign to namelist symbols */ 436 scale = highpc - lowpc; 437 scale /= nsamples; 438 alignentries(); 439 for (i = 0, j = 1; i < nsamples; i++) { 440 ccnt = samples[i]; 441 if (ccnt == 0) 442 continue; 443 pcl = lowpc + (unsigned long)(scale * i); 444 pch = lowpc + (unsigned long)(scale * (i + 1)); 445 time = ccnt; 446 # ifdef DEBUG 447 if ( debug & SAMPLEDEBUG ) { 448 printf( "[asgnsamples] pcl 0x%lx pch 0x%lx ccnt %d\n" , 449 pcl , pch , ccnt ); 450 } 451 # endif /* DEBUG */ 452 totime += time; 453 for (j = j - 1; j < nname; j++) { 454 svalue0 = nl[j].svalue; 455 svalue1 = nl[j+1].svalue; 456 /* 457 * if high end of tick is below entry address, 458 * go for next tick. 459 */ 460 if (pch < svalue0) 461 break; 462 /* 463 * if low end of tick into next routine, 464 * go for next routine. 465 */ 466 if (pcl >= svalue1) 467 continue; 468 overlap = min(pch, svalue1) - max(pcl, svalue0); 469 if (overlap > 0) { 470 # ifdef DEBUG 471 if (debug & SAMPLEDEBUG) { 472 printf("[asgnsamples] (0x%lx->0x%lx-0x%lx) %s gets %f ticks %ld overlap\n", 473 nl[j].value/sizeof(UNIT), svalue0, svalue1, 474 nl[j].name, 475 overlap * time / scale, overlap); 476 } 477 # endif /* DEBUG */ 478 nl[j].time += overlap * time / scale; 479 } 480 } 481 } 482 # ifdef DEBUG 483 if (debug & SAMPLEDEBUG) { 484 printf("[asgnsamples] totime %f\n", totime); 485 } 486 # endif /* DEBUG */ 487 } 488 489 490 unsigned long 491 min(unsigned long a, unsigned long b) 492 { 493 if (a<b) 494 return(a); 495 return(b); 496 } 497 498 unsigned long 499 max(unsigned long a, unsigned long b) 500 { 501 if (a>b) 502 return(a); 503 return(b); 504 } 505 506 /* 507 * calculate scaled entry point addresses (to save time in asgnsamples), 508 * and possibly push the scaled entry points over the entry mask, 509 * if it turns out that the entry point is in one bucket and the code 510 * for a routine is in the next bucket. 511 */ 512 void 513 alignentries(void) 514 { 515 struct nl *nlp; 516 unsigned long bucket_of_entry; 517 unsigned long bucket_of_code; 518 519 for (nlp = nl; nlp < npe; nlp++) { 520 nlp -> svalue = nlp -> value / sizeof(UNIT); 521 bucket_of_entry = (nlp->svalue - lowpc) / scale; 522 bucket_of_code = (nlp->svalue + UNITS_TO_CODE - lowpc) / scale; 523 if (bucket_of_entry < bucket_of_code) { 524 # ifdef DEBUG 525 if (debug & SAMPLEDEBUG) { 526 printf("[alignentries] pushing svalue 0x%lx to 0x%lx\n", 527 nlp->svalue, nlp->svalue + UNITS_TO_CODE); 528 } 529 # endif /* DEBUG */ 530 nlp->svalue += UNITS_TO_CODE; 531 } 532 } 533 } 534