1*0Sstevel@tonic-gate /* 2*0Sstevel@tonic-gate * CDDL HEADER START 3*0Sstevel@tonic-gate * 4*0Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*0Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 6*0Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 7*0Sstevel@tonic-gate * with the License. 8*0Sstevel@tonic-gate * 9*0Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*0Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 11*0Sstevel@tonic-gate * See the License for the specific language governing permissions 12*0Sstevel@tonic-gate * and limitations under the License. 13*0Sstevel@tonic-gate * 14*0Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 15*0Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*0Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 17*0Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 18*0Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 19*0Sstevel@tonic-gate * 20*0Sstevel@tonic-gate * CDDL HEADER END 21*0Sstevel@tonic-gate */ 22*0Sstevel@tonic-gate /* 23*0Sstevel@tonic-gate * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24*0Sstevel@tonic-gate * Use is subject to license terms. 25*0Sstevel@tonic-gate */ 26*0Sstevel@tonic-gate 27*0Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 28*0Sstevel@tonic-gate 29*0Sstevel@tonic-gate #include <sysexits.h> 30*0Sstevel@tonic-gate #include <stdlib.h> 31*0Sstevel@tonic-gate #include <unistd.h> 32*0Sstevel@tonic-gate #include "gprof.h" 33*0Sstevel@tonic-gate #include "profile.h" 34*0Sstevel@tonic-gate 35*0Sstevel@tonic-gate char *whoami = "gprof"; 36*0Sstevel@tonic-gate static pctype lowpc, highpc; /* range profiled, in UNIT's */ 37*0Sstevel@tonic-gate 38*0Sstevel@tonic-gate /* 39*0Sstevel@tonic-gate * things which get -E excluded by default. 40*0Sstevel@tonic-gate */ 41*0Sstevel@tonic-gate static char *defaultEs[] = { 42*0Sstevel@tonic-gate "mcount", 43*0Sstevel@tonic-gate "__mcleanup", 44*0Sstevel@tonic-gate 0 45*0Sstevel@tonic-gate }; 46*0Sstevel@tonic-gate 47*0Sstevel@tonic-gate #ifdef DEBUG 48*0Sstevel@tonic-gate 49*0Sstevel@tonic-gate static char *objname[] = { 50*0Sstevel@tonic-gate "<invalid object>", 51*0Sstevel@tonic-gate "PROF_BUFFER_T", 52*0Sstevel@tonic-gate "PROF_CALLGRAPH_T", 53*0Sstevel@tonic-gate "PROF_MODULES_T", 54*0Sstevel@tonic-gate 0 55*0Sstevel@tonic-gate }; 56*0Sstevel@tonic-gate #define MAX_OBJTYPES 3 57*0Sstevel@tonic-gate 58*0Sstevel@tonic-gate #endif DEBUG 59*0Sstevel@tonic-gate 60*0Sstevel@tonic-gate void 61*0Sstevel@tonic-gate done() 62*0Sstevel@tonic-gate { 63*0Sstevel@tonic-gate 64*0Sstevel@tonic-gate exit(EX_OK); 65*0Sstevel@tonic-gate } 66*0Sstevel@tonic-gate 67*0Sstevel@tonic-gate static pctype 68*0Sstevel@tonic-gate max(pctype a, pctype b) 69*0Sstevel@tonic-gate { 70*0Sstevel@tonic-gate if (a > b) 71*0Sstevel@tonic-gate return (a); 72*0Sstevel@tonic-gate return (b); 73*0Sstevel@tonic-gate } 74*0Sstevel@tonic-gate 75*0Sstevel@tonic-gate static pctype 76*0Sstevel@tonic-gate min(pctype a, pctype b) 77*0Sstevel@tonic-gate { 78*0Sstevel@tonic-gate if (a < b) 79*0Sstevel@tonic-gate return (a); 80*0Sstevel@tonic-gate return (b); 81*0Sstevel@tonic-gate } 82*0Sstevel@tonic-gate 83*0Sstevel@tonic-gate /* 84*0Sstevel@tonic-gate * calculate scaled entry point addresses (to save time in asgnsamples), 85*0Sstevel@tonic-gate * and possibly push the scaled entry points over the entry mask, 86*0Sstevel@tonic-gate * if it turns out that the entry point is in one bucket and the code 87*0Sstevel@tonic-gate * for a routine is in the next bucket. 88*0Sstevel@tonic-gate * 89*0Sstevel@tonic-gate */ 90*0Sstevel@tonic-gate static void 91*0Sstevel@tonic-gate alignentries() 92*0Sstevel@tonic-gate { 93*0Sstevel@tonic-gate register struct nl * nlp; 94*0Sstevel@tonic-gate #ifdef DEBUG 95*0Sstevel@tonic-gate pctype bucket_of_entry; 96*0Sstevel@tonic-gate pctype bucket_of_code; 97*0Sstevel@tonic-gate #endif DEBUG 98*0Sstevel@tonic-gate 99*0Sstevel@tonic-gate /* for old-style gmon.out, nameslist is only in modules.nl */ 100*0Sstevel@tonic-gate 101*0Sstevel@tonic-gate for (nlp = modules.nl; nlp < modules.npe; nlp++) { 102*0Sstevel@tonic-gate nlp->svalue = nlp->value / sizeof (UNIT); 103*0Sstevel@tonic-gate #ifdef DEBUG 104*0Sstevel@tonic-gate bucket_of_entry = (nlp->svalue - lowpc) / scale; 105*0Sstevel@tonic-gate bucket_of_code = (nlp->svalue + UNITS_TO_CODE - lowpc) / scale; 106*0Sstevel@tonic-gate if (bucket_of_entry < bucket_of_code) { 107*0Sstevel@tonic-gate if (debug & SAMPLEDEBUG) { 108*0Sstevel@tonic-gate printf("[alignentries] pushing svalue 0x%llx " 109*0Sstevel@tonic-gate "to 0x%llx\n", nlp->svalue, 110*0Sstevel@tonic-gate nlp->svalue + UNITS_TO_CODE); 111*0Sstevel@tonic-gate } 112*0Sstevel@tonic-gate } 113*0Sstevel@tonic-gate #endif DEBUG 114*0Sstevel@tonic-gate } 115*0Sstevel@tonic-gate } 116*0Sstevel@tonic-gate 117*0Sstevel@tonic-gate /* 118*0Sstevel@tonic-gate * old-style gmon.out 119*0Sstevel@tonic-gate * ------------------ 120*0Sstevel@tonic-gate * 121*0Sstevel@tonic-gate * Assign samples to the procedures to which they belong. 122*0Sstevel@tonic-gate * 123*0Sstevel@tonic-gate * There are three cases as to where pcl and pch can be 124*0Sstevel@tonic-gate * with respect to the routine entry addresses svalue0 and svalue1 125*0Sstevel@tonic-gate * as shown in the following diagram. overlap computes the 126*0Sstevel@tonic-gate * distance between the arrows, the fraction of the sample 127*0Sstevel@tonic-gate * that is to be credited to the routine which starts at svalue0. 128*0Sstevel@tonic-gate * 129*0Sstevel@tonic-gate * svalue0 svalue1 130*0Sstevel@tonic-gate * | | 131*0Sstevel@tonic-gate * v v 132*0Sstevel@tonic-gate * 133*0Sstevel@tonic-gate * +-----------------------------------------------+ 134*0Sstevel@tonic-gate * | | 135*0Sstevel@tonic-gate * | ->| |<- ->| |<- ->| |<- | 136*0Sstevel@tonic-gate * | | | | | | 137*0Sstevel@tonic-gate * +---------+ +---------+ +---------+ 138*0Sstevel@tonic-gate * 139*0Sstevel@tonic-gate * ^ ^ ^ ^ ^ ^ 140*0Sstevel@tonic-gate * | | | | | | 141*0Sstevel@tonic-gate * pcl pch pcl pch pcl pch 142*0Sstevel@tonic-gate * 143*0Sstevel@tonic-gate * For the vax we assert that samples will never fall in the first 144*0Sstevel@tonic-gate * two bytes of any routine, since that is the entry mask, 145*0Sstevel@tonic-gate * thus we give call alignentries() to adjust the entry points if 146*0Sstevel@tonic-gate * the entry mask falls in one bucket but the code for the routine 147*0Sstevel@tonic-gate * doesn't start until the next bucket. In conjunction with the 148*0Sstevel@tonic-gate * alignment of routine addresses, this should allow us to have 149*0Sstevel@tonic-gate * only one sample for every four bytes of text space and never 150*0Sstevel@tonic-gate * have any overlap (the two end cases, above). 151*0Sstevel@tonic-gate */ 152*0Sstevel@tonic-gate static void 153*0Sstevel@tonic-gate asgnsamples() 154*0Sstevel@tonic-gate { 155*0Sstevel@tonic-gate sztype i, j; 156*0Sstevel@tonic-gate unsigned_UNIT ccnt; 157*0Sstevel@tonic-gate double time; 158*0Sstevel@tonic-gate pctype pcl, pch; 159*0Sstevel@tonic-gate pctype overlap; 160*0Sstevel@tonic-gate pctype svalue0, svalue1; 161*0Sstevel@tonic-gate 162*0Sstevel@tonic-gate extern mod_info_t modules; 163*0Sstevel@tonic-gate nltype *nl = modules.nl; 164*0Sstevel@tonic-gate sztype nname = modules.nname; 165*0Sstevel@tonic-gate 166*0Sstevel@tonic-gate /* read samples and assign to namelist symbols */ 167*0Sstevel@tonic-gate scale = highpc - lowpc; 168*0Sstevel@tonic-gate scale /= nsamples; 169*0Sstevel@tonic-gate alignentries(); 170*0Sstevel@tonic-gate for (i = 0, j = 1; i < nsamples; i++) { 171*0Sstevel@tonic-gate ccnt = samples[i]; 172*0Sstevel@tonic-gate if (ccnt == 0) 173*0Sstevel@tonic-gate continue; 174*0Sstevel@tonic-gate pcl = lowpc + scale * i; 175*0Sstevel@tonic-gate pch = lowpc + scale * (i + 1); 176*0Sstevel@tonic-gate time = ccnt; 177*0Sstevel@tonic-gate #ifdef DEBUG 178*0Sstevel@tonic-gate if (debug & SAMPLEDEBUG) { 179*0Sstevel@tonic-gate printf("[asgnsamples] pcl 0x%llx pch 0x%llx ccnt %d\n", 180*0Sstevel@tonic-gate pcl, pch, ccnt); 181*0Sstevel@tonic-gate } 182*0Sstevel@tonic-gate #endif DEBUG 183*0Sstevel@tonic-gate totime += time; 184*0Sstevel@tonic-gate for (j = (j ? j - 1 : 0); j < nname; j++) { 185*0Sstevel@tonic-gate svalue0 = nl[j].svalue; 186*0Sstevel@tonic-gate svalue1 = nl[j+1].svalue; 187*0Sstevel@tonic-gate /* 188*0Sstevel@tonic-gate * if high end of tick is below entry address, 189*0Sstevel@tonic-gate * go for next tick. 190*0Sstevel@tonic-gate */ 191*0Sstevel@tonic-gate if (pch < svalue0) 192*0Sstevel@tonic-gate break; 193*0Sstevel@tonic-gate /* 194*0Sstevel@tonic-gate * if low end of tick into next routine, 195*0Sstevel@tonic-gate * go for next routine. 196*0Sstevel@tonic-gate */ 197*0Sstevel@tonic-gate if (pcl >= svalue1) 198*0Sstevel@tonic-gate continue; 199*0Sstevel@tonic-gate overlap = min(pch, svalue1) - max(pcl, svalue0); 200*0Sstevel@tonic-gate if (overlap != 0) { 201*0Sstevel@tonic-gate #ifdef DEBUG 202*0Sstevel@tonic-gate if (debug & SAMPLEDEBUG) { 203*0Sstevel@tonic-gate printf("[asgnsamples] " 204*0Sstevel@tonic-gate "(0x%llx->0x%llx-0x%llx) %s gets " 205*0Sstevel@tonic-gate "%f ticks %lld overlap\n", 206*0Sstevel@tonic-gate nl[j].value/sizeof (UNIT), svalue0, 207*0Sstevel@tonic-gate svalue1, nl[j].name, 208*0Sstevel@tonic-gate overlap * time / scale, overlap); 209*0Sstevel@tonic-gate } 210*0Sstevel@tonic-gate #endif DEBUG 211*0Sstevel@tonic-gate nl[j].time += overlap * time / scale; 212*0Sstevel@tonic-gate } 213*0Sstevel@tonic-gate } 214*0Sstevel@tonic-gate } 215*0Sstevel@tonic-gate #ifdef DEBUG 216*0Sstevel@tonic-gate if (debug & SAMPLEDEBUG) { 217*0Sstevel@tonic-gate printf("[asgnsamples] totime %f\n", totime); 218*0Sstevel@tonic-gate } 219*0Sstevel@tonic-gate #endif DEBUG 220*0Sstevel@tonic-gate } 221*0Sstevel@tonic-gate 222*0Sstevel@tonic-gate 223*0Sstevel@tonic-gate static void 224*0Sstevel@tonic-gate dump_callgraph(FILE *fp, char *filename, 225*0Sstevel@tonic-gate unsigned long tarcs, unsigned long ncallees) 226*0Sstevel@tonic-gate { 227*0Sstevel@tonic-gate ProfCallGraph prof_cgraph; 228*0Sstevel@tonic-gate ProfFunction prof_func; 229*0Sstevel@tonic-gate register arctype *arcp; 230*0Sstevel@tonic-gate mod_info_t *mi; 231*0Sstevel@tonic-gate nltype *nlp; 232*0Sstevel@tonic-gate size_t cur_offset; 233*0Sstevel@tonic-gate unsigned long caller_id = 0, callee_id = 0; 234*0Sstevel@tonic-gate 235*0Sstevel@tonic-gate /* 236*0Sstevel@tonic-gate * Write the callgraph header 237*0Sstevel@tonic-gate */ 238*0Sstevel@tonic-gate prof_cgraph.type = PROF_CALLGRAPH_T; 239*0Sstevel@tonic-gate prof_cgraph.version = PROF_CALLGRAPH_VER; 240*0Sstevel@tonic-gate prof_cgraph.functions = PROFCGRAPH_SZ; 241*0Sstevel@tonic-gate prof_cgraph.size = PROFCGRAPH_SZ + tarcs * PROFFUNC_SZ; 242*0Sstevel@tonic-gate if (fwrite(&prof_cgraph, sizeof (ProfCallGraph), 1, fp) != 1) { 243*0Sstevel@tonic-gate perror(filename); 244*0Sstevel@tonic-gate exit(EX_IOERR); 245*0Sstevel@tonic-gate } 246*0Sstevel@tonic-gate if (CGRAPH_FILLER) 247*0Sstevel@tonic-gate fseek(fp, CGRAPH_FILLER, SEEK_CUR); 248*0Sstevel@tonic-gate 249*0Sstevel@tonic-gate /* Current offset inside the callgraph object */ 250*0Sstevel@tonic-gate cur_offset = prof_cgraph.functions; 251*0Sstevel@tonic-gate 252*0Sstevel@tonic-gate for (mi = &modules; mi; mi = mi->next) { 253*0Sstevel@tonic-gate for (nlp = mi->nl; nlp < mi->npe; nlp++) { 254*0Sstevel@tonic-gate if (nlp->ncallers == 0) 255*0Sstevel@tonic-gate continue; 256*0Sstevel@tonic-gate 257*0Sstevel@tonic-gate /* If this is the last callee, set next_to to 0 */ 258*0Sstevel@tonic-gate callee_id++; 259*0Sstevel@tonic-gate if (callee_id == ncallees) 260*0Sstevel@tonic-gate prof_func.next_to = 0; 261*0Sstevel@tonic-gate else { 262*0Sstevel@tonic-gate prof_func.next_to = cur_offset + 263*0Sstevel@tonic-gate nlp->ncallers * PROFFUNC_SZ; 264*0Sstevel@tonic-gate } 265*0Sstevel@tonic-gate 266*0Sstevel@tonic-gate /* 267*0Sstevel@tonic-gate * Dump this callee's raw arc information with all 268*0Sstevel@tonic-gate * its callers 269*0Sstevel@tonic-gate */ 270*0Sstevel@tonic-gate caller_id = 1; 271*0Sstevel@tonic-gate for (arcp = nlp->parents; arcp; 272*0Sstevel@tonic-gate arcp = arcp->arc_parentlist) { 273*0Sstevel@tonic-gate /* 274*0Sstevel@tonic-gate * If no more callers for this callee, set 275*0Sstevel@tonic-gate * next_from to 0 276*0Sstevel@tonic-gate */ 277*0Sstevel@tonic-gate if (caller_id == nlp->ncallers) 278*0Sstevel@tonic-gate prof_func.next_from = 0; 279*0Sstevel@tonic-gate else { 280*0Sstevel@tonic-gate prof_func.next_from = cur_offset + 281*0Sstevel@tonic-gate PROFFUNC_SZ; 282*0Sstevel@tonic-gate } 283*0Sstevel@tonic-gate 284*0Sstevel@tonic-gate prof_func.frompc = 285*0Sstevel@tonic-gate arcp->arc_parentp->module->load_base + 286*0Sstevel@tonic-gate (arcp->arc_parentp->value - 287*0Sstevel@tonic-gate arcp->arc_parentp->module->txt_origin); 288*0Sstevel@tonic-gate prof_func.topc = 289*0Sstevel@tonic-gate mi->load_base + 290*0Sstevel@tonic-gate (nlp->value - mi->txt_origin); 291*0Sstevel@tonic-gate prof_func.count = arcp->arc_count; 292*0Sstevel@tonic-gate 293*0Sstevel@tonic-gate 294*0Sstevel@tonic-gate if (fwrite(&prof_func, sizeof (ProfFunction), 295*0Sstevel@tonic-gate 1, fp) != 1) { 296*0Sstevel@tonic-gate perror(filename); 297*0Sstevel@tonic-gate exit(EX_IOERR); 298*0Sstevel@tonic-gate } 299*0Sstevel@tonic-gate if (FUNC_FILLER) 300*0Sstevel@tonic-gate fseek(fp, FUNC_FILLER, SEEK_CUR); 301*0Sstevel@tonic-gate 302*0Sstevel@tonic-gate cur_offset += PROFFUNC_SZ; 303*0Sstevel@tonic-gate caller_id++; 304*0Sstevel@tonic-gate } 305*0Sstevel@tonic-gate } /* for nlp... */ 306*0Sstevel@tonic-gate } /* for mi... */ 307*0Sstevel@tonic-gate } 308*0Sstevel@tonic-gate 309*0Sstevel@tonic-gate /* 310*0Sstevel@tonic-gate * To save all pc-hits in all the gmon.out's is infeasible, as this 311*0Sstevel@tonic-gate * may become quite huge even with a small number of files to sum. 312*0Sstevel@tonic-gate * Instead, we'll dump *fictitious hits* to correct functions 313*0Sstevel@tonic-gate * by scanning module namelists. Again, since this is summing 314*0Sstevel@tonic-gate * pc-hits, we may have to dump the pcsamples out in chunks if the 315*0Sstevel@tonic-gate * number of pc-hits is high. 316*0Sstevel@tonic-gate */ 317*0Sstevel@tonic-gate static void 318*0Sstevel@tonic-gate dump_hits(FILE *fp, char *filename, nltype *nlp) 319*0Sstevel@tonic-gate { 320*0Sstevel@tonic-gate Address *p, hitpc; 321*0Sstevel@tonic-gate size_t i, nelem, ntowrite; 322*0Sstevel@tonic-gate 323*0Sstevel@tonic-gate if ((nelem = nlp->nticks) > PROF_BUFFER_SIZE) 324*0Sstevel@tonic-gate nelem = PROF_BUFFER_SIZE; 325*0Sstevel@tonic-gate 326*0Sstevel@tonic-gate if ((p = (Address *) calloc(nelem, sizeof (Address))) == NULL) { 327*0Sstevel@tonic-gate fprintf(stderr, "%s: no room for %ld pcsamples\n", 328*0Sstevel@tonic-gate whoami, nelem); 329*0Sstevel@tonic-gate exit(EX_OSERR); 330*0Sstevel@tonic-gate } 331*0Sstevel@tonic-gate 332*0Sstevel@tonic-gate /* 333*0Sstevel@tonic-gate * Set up *fictitious* hits (to function entry) buffer 334*0Sstevel@tonic-gate */ 335*0Sstevel@tonic-gate hitpc = nlp->module->load_base + (nlp->value - nlp->module->txt_origin); 336*0Sstevel@tonic-gate for (i = 0; i < nelem; i++) 337*0Sstevel@tonic-gate p[i] = hitpc; 338*0Sstevel@tonic-gate 339*0Sstevel@tonic-gate for (ntowrite = nlp->nticks; ntowrite >= nelem; ntowrite -= nelem) { 340*0Sstevel@tonic-gate if (fwrite(p, nelem * sizeof (Address), 1, fp) != 1) { 341*0Sstevel@tonic-gate perror(filename); 342*0Sstevel@tonic-gate exit(EX_IOERR); 343*0Sstevel@tonic-gate } 344*0Sstevel@tonic-gate } 345*0Sstevel@tonic-gate 346*0Sstevel@tonic-gate if (ntowrite) { 347*0Sstevel@tonic-gate if (fwrite(p, ntowrite * sizeof (Address), 1, fp) != 1) { 348*0Sstevel@tonic-gate perror(filename); 349*0Sstevel@tonic-gate exit(EX_IOERR); 350*0Sstevel@tonic-gate } 351*0Sstevel@tonic-gate } 352*0Sstevel@tonic-gate 353*0Sstevel@tonic-gate free(p); 354*0Sstevel@tonic-gate } 355*0Sstevel@tonic-gate 356*0Sstevel@tonic-gate static void 357*0Sstevel@tonic-gate dump_pcsamples(FILE *fp, char *filename, 358*0Sstevel@tonic-gate unsigned long *tarcs, unsigned long *ncallees) 359*0Sstevel@tonic-gate { 360*0Sstevel@tonic-gate ProfBuffer prof_buffer; 361*0Sstevel@tonic-gate register arctype *arcp; 362*0Sstevel@tonic-gate mod_info_t *mi; 363*0Sstevel@tonic-gate nltype *nlp; 364*0Sstevel@tonic-gate 365*0Sstevel@tonic-gate prof_buffer.type = PROF_BUFFER_T; 366*0Sstevel@tonic-gate prof_buffer.version = PROF_BUFFER_VER; 367*0Sstevel@tonic-gate prof_buffer.buffer = PROFBUF_SZ; 368*0Sstevel@tonic-gate prof_buffer.bufsize = n_pcsamples; 369*0Sstevel@tonic-gate prof_buffer.size = PROFBUF_SZ + n_pcsamples * sizeof (Address); 370*0Sstevel@tonic-gate if (fwrite(&prof_buffer, sizeof (ProfBuffer), 1, fp) != 1) { 371*0Sstevel@tonic-gate perror(filename); 372*0Sstevel@tonic-gate exit(EX_IOERR); 373*0Sstevel@tonic-gate } 374*0Sstevel@tonic-gate if (BUF_FILLER) 375*0Sstevel@tonic-gate fseek(fp, BUF_FILLER, SEEK_CUR); 376*0Sstevel@tonic-gate 377*0Sstevel@tonic-gate *tarcs = 0; 378*0Sstevel@tonic-gate *ncallees = 0; 379*0Sstevel@tonic-gate for (mi = &modules; mi; mi = mi->next) { 380*0Sstevel@tonic-gate for (nlp = mi->nl; nlp < mi->npe; nlp++) { 381*0Sstevel@tonic-gate if (nlp->nticks) 382*0Sstevel@tonic-gate dump_hits(fp, filename, nlp); 383*0Sstevel@tonic-gate 384*0Sstevel@tonic-gate nlp->ncallers = 0; 385*0Sstevel@tonic-gate for (arcp = nlp->parents; arcp; 386*0Sstevel@tonic-gate arcp = arcp->arc_parentlist) { 387*0Sstevel@tonic-gate (nlp->ncallers)++; 388*0Sstevel@tonic-gate } 389*0Sstevel@tonic-gate 390*0Sstevel@tonic-gate if (nlp->ncallers) { 391*0Sstevel@tonic-gate (*tarcs) += nlp->ncallers; 392*0Sstevel@tonic-gate (*ncallees)++; 393*0Sstevel@tonic-gate } 394*0Sstevel@tonic-gate } 395*0Sstevel@tonic-gate } 396*0Sstevel@tonic-gate } 397*0Sstevel@tonic-gate 398*0Sstevel@tonic-gate static void 399*0Sstevel@tonic-gate dump_modules(FILE *fp, char *filename, size_t pbuf_sz) 400*0Sstevel@tonic-gate { 401*0Sstevel@tonic-gate char *pbuf, *p; 402*0Sstevel@tonic-gate size_t namelen; 403*0Sstevel@tonic-gate Index off_nxt, off_path; 404*0Sstevel@tonic-gate mod_info_t *mi; 405*0Sstevel@tonic-gate 406*0Sstevel@tonic-gate ProfModuleList prof_modlist; 407*0Sstevel@tonic-gate ProfModule prof_mod; 408*0Sstevel@tonic-gate 409*0Sstevel@tonic-gate /* Allocate for path strings buffer */ 410*0Sstevel@tonic-gate pbuf_sz = CEIL(pbuf_sz, STRUCT_ALIGN); 411*0Sstevel@tonic-gate if ((p = pbuf = (char *) calloc(pbuf_sz, sizeof (char))) == NULL) { 412*0Sstevel@tonic-gate fprintf(stderr, "%s: no room for %ld bytes\n", 413*0Sstevel@tonic-gate whoami, pbuf_sz * sizeof (char)); 414*0Sstevel@tonic-gate exit(EX_OSERR); 415*0Sstevel@tonic-gate } 416*0Sstevel@tonic-gate 417*0Sstevel@tonic-gate /* Dump out PROF_MODULE_T info for all non-aout modules */ 418*0Sstevel@tonic-gate prof_modlist.type = PROF_MODULES_T; 419*0Sstevel@tonic-gate prof_modlist.version = PROF_MODULES_VER; 420*0Sstevel@tonic-gate prof_modlist.modules = PROFMODLIST_SZ; 421*0Sstevel@tonic-gate prof_modlist.size = PROFMODLIST_SZ + (n_modules - 1) * PROFMOD_SZ + 422*0Sstevel@tonic-gate pbuf_sz; 423*0Sstevel@tonic-gate if (fwrite(&prof_modlist, sizeof (ProfModuleList), 1, fp) != 1) { 424*0Sstevel@tonic-gate perror(filename); 425*0Sstevel@tonic-gate exit(EX_IOERR); 426*0Sstevel@tonic-gate } 427*0Sstevel@tonic-gate if (MODLIST_FILLER) 428*0Sstevel@tonic-gate fseek(fp, MODLIST_FILLER, SEEK_CUR); 429*0Sstevel@tonic-gate 430*0Sstevel@tonic-gate /* 431*0Sstevel@tonic-gate * Initialize offsets for ProfModule elements. 432*0Sstevel@tonic-gate */ 433*0Sstevel@tonic-gate off_nxt = PROFMODLIST_SZ + PROFMOD_SZ; 434*0Sstevel@tonic-gate off_path = PROFMODLIST_SZ + (n_modules - 1) * PROFMOD_SZ; 435*0Sstevel@tonic-gate 436*0Sstevel@tonic-gate for (mi = modules.next; mi; mi = mi->next) { 437*0Sstevel@tonic-gate if (mi->next) 438*0Sstevel@tonic-gate prof_mod.next = off_nxt; 439*0Sstevel@tonic-gate else 440*0Sstevel@tonic-gate prof_mod.next = 0; 441*0Sstevel@tonic-gate prof_mod.path = off_path; 442*0Sstevel@tonic-gate prof_mod.startaddr = mi->load_base; 443*0Sstevel@tonic-gate prof_mod.endaddr = mi->load_end; 444*0Sstevel@tonic-gate 445*0Sstevel@tonic-gate if (fwrite(&prof_mod, sizeof (ProfModule), 1, fp) != 1) { 446*0Sstevel@tonic-gate perror(filename); 447*0Sstevel@tonic-gate exit(EX_IOERR); 448*0Sstevel@tonic-gate } 449*0Sstevel@tonic-gate 450*0Sstevel@tonic-gate if (MOD_FILLER) 451*0Sstevel@tonic-gate fseek(fp, MOD_FILLER, SEEK_CUR); 452*0Sstevel@tonic-gate 453*0Sstevel@tonic-gate strcpy(p, mi->name); 454*0Sstevel@tonic-gate namelen = strlen(mi->name); 455*0Sstevel@tonic-gate p += namelen + 1; 456*0Sstevel@tonic-gate 457*0Sstevel@tonic-gate /* Note that offset to every path str need not be aligned */ 458*0Sstevel@tonic-gate off_nxt += PROFMOD_SZ; 459*0Sstevel@tonic-gate off_path += namelen + 1; 460*0Sstevel@tonic-gate } 461*0Sstevel@tonic-gate 462*0Sstevel@tonic-gate /* Write out the module path strings */ 463*0Sstevel@tonic-gate if (pbuf_sz) { 464*0Sstevel@tonic-gate if (fwrite(pbuf, pbuf_sz, 1, fp) != 1) { 465*0Sstevel@tonic-gate perror(filename); 466*0Sstevel@tonic-gate exit(EX_IOERR); 467*0Sstevel@tonic-gate } 468*0Sstevel@tonic-gate 469*0Sstevel@tonic-gate free(pbuf); 470*0Sstevel@tonic-gate } 471*0Sstevel@tonic-gate } 472*0Sstevel@tonic-gate 473*0Sstevel@tonic-gate /* 474*0Sstevel@tonic-gate * If we have inactive modules, their current load addresses may overlap with 475*0Sstevel@tonic-gate * active ones, and so we've to assign fictitious, non-overlapping addresses 476*0Sstevel@tonic-gate * to all modules before we dump them. 477*0Sstevel@tonic-gate */ 478*0Sstevel@tonic-gate static void 479*0Sstevel@tonic-gate fixup_maps(size_t *pathsz) 480*0Sstevel@tonic-gate { 481*0Sstevel@tonic-gate unsigned int n_inactive = 0; 482*0Sstevel@tonic-gate Address lbase, lend; 483*0Sstevel@tonic-gate mod_info_t *mi; 484*0Sstevel@tonic-gate 485*0Sstevel@tonic-gate /* Pick the lowest load address among modules */ 486*0Sstevel@tonic-gate *pathsz = 0; 487*0Sstevel@tonic-gate for (mi = &modules; mi; mi = mi->next) { 488*0Sstevel@tonic-gate 489*0Sstevel@tonic-gate if (mi->active == FALSE) 490*0Sstevel@tonic-gate n_inactive++; 491*0Sstevel@tonic-gate 492*0Sstevel@tonic-gate if (mi == &modules || mi->load_base < lbase) 493*0Sstevel@tonic-gate lbase = mi->load_base; 494*0Sstevel@tonic-gate 495*0Sstevel@tonic-gate /* 496*0Sstevel@tonic-gate * Return total path size of non-aout modules only 497*0Sstevel@tonic-gate */ 498*0Sstevel@tonic-gate if (mi != &modules) 499*0Sstevel@tonic-gate *pathsz = (*pathsz) + strlen(mi->name) + 1; 500*0Sstevel@tonic-gate } 501*0Sstevel@tonic-gate 502*0Sstevel@tonic-gate /* 503*0Sstevel@tonic-gate * All module info is in fine shape already if there are no 504*0Sstevel@tonic-gate * inactive modules 505*0Sstevel@tonic-gate */ 506*0Sstevel@tonic-gate if (n_inactive == 0) 507*0Sstevel@tonic-gate return; 508*0Sstevel@tonic-gate 509*0Sstevel@tonic-gate /* 510*0Sstevel@tonic-gate * Assign fictitious load addresses to all (non-aout) modules so 511*0Sstevel@tonic-gate * that sum info can be dumped out. 512*0Sstevel@tonic-gate */ 513*0Sstevel@tonic-gate for (mi = modules.next; mi; mi = mi->next) { 514*0Sstevel@tonic-gate lend = lbase + (mi->data_end - mi->txt_origin); 515*0Sstevel@tonic-gate if ((lbase < modules.load_base && lend < modules.load_base) || 516*0Sstevel@tonic-gate (lbase > modules.load_end && lend > modules.load_end)) { 517*0Sstevel@tonic-gate 518*0Sstevel@tonic-gate mi->load_base = lbase; 519*0Sstevel@tonic-gate mi->load_end = lend; 520*0Sstevel@tonic-gate 521*0Sstevel@tonic-gate /* just to give an appearance of reality */ 522*0Sstevel@tonic-gate lbase = CEIL(lend + PGSZ, PGSZ); 523*0Sstevel@tonic-gate } else { 524*0Sstevel@tonic-gate /* 525*0Sstevel@tonic-gate * can't use this lbase & lend pair, as it 526*0Sstevel@tonic-gate * overlaps with aout's addresses 527*0Sstevel@tonic-gate */ 528*0Sstevel@tonic-gate mi->load_base = CEIL(modules.load_end + PGSZ, PGSZ); 529*0Sstevel@tonic-gate mi->load_end = mi->load_base + (lend - lbase); 530*0Sstevel@tonic-gate 531*0Sstevel@tonic-gate lbase = CEIL(mi->load_end + PGSZ, PGSZ); 532*0Sstevel@tonic-gate } 533*0Sstevel@tonic-gate } 534*0Sstevel@tonic-gate } 535*0Sstevel@tonic-gate 536*0Sstevel@tonic-gate static void 537*0Sstevel@tonic-gate dump_gprofhdr(FILE *fp, char *filename) 538*0Sstevel@tonic-gate { 539*0Sstevel@tonic-gate ProfHeader prof_hdr; 540*0Sstevel@tonic-gate 541*0Sstevel@tonic-gate prof_hdr.h_magic = PROF_MAGIC; 542*0Sstevel@tonic-gate prof_hdr.h_major_ver = PROF_MAJOR_VERSION; 543*0Sstevel@tonic-gate prof_hdr.h_minor_ver = PROF_MINOR_VERSION; 544*0Sstevel@tonic-gate prof_hdr.size = PROFHDR_SZ; 545*0Sstevel@tonic-gate if (fwrite(&prof_hdr, sizeof (prof_hdr), 1, fp) != 1) { 546*0Sstevel@tonic-gate perror(filename); 547*0Sstevel@tonic-gate exit(EX_IOERR); 548*0Sstevel@tonic-gate } 549*0Sstevel@tonic-gate 550*0Sstevel@tonic-gate if (HDR_FILLER) 551*0Sstevel@tonic-gate fseek(fp, HDR_FILLER, SEEK_CUR); 552*0Sstevel@tonic-gate } 553*0Sstevel@tonic-gate 554*0Sstevel@tonic-gate static void 555*0Sstevel@tonic-gate dumpsum_ostyle(char *sumfile) 556*0Sstevel@tonic-gate { 557*0Sstevel@tonic-gate register nltype *nlp; 558*0Sstevel@tonic-gate register arctype *arcp; 559*0Sstevel@tonic-gate struct rawarc arc; 560*0Sstevel@tonic-gate struct rawarc32 arc32; 561*0Sstevel@tonic-gate FILE *sfile; 562*0Sstevel@tonic-gate 563*0Sstevel@tonic-gate if ((sfile = fopen(sumfile, "w")) == NULL) { 564*0Sstevel@tonic-gate perror(sumfile); 565*0Sstevel@tonic-gate exit(EX_IOERR); 566*0Sstevel@tonic-gate } 567*0Sstevel@tonic-gate /* 568*0Sstevel@tonic-gate * dump the header; use the last header read in 569*0Sstevel@tonic-gate */ 570*0Sstevel@tonic-gate if (Bflag) { 571*0Sstevel@tonic-gate if (fwrite(&h, sizeof (h), 1, sfile) != 1) { 572*0Sstevel@tonic-gate perror(sumfile); 573*0Sstevel@tonic-gate exit(EX_IOERR); 574*0Sstevel@tonic-gate } 575*0Sstevel@tonic-gate } else { 576*0Sstevel@tonic-gate struct hdr32 hdr; 577*0Sstevel@tonic-gate hdr.lowpc = (pctype32)h.lowpc; 578*0Sstevel@tonic-gate hdr.highpc = (pctype32)h.highpc; 579*0Sstevel@tonic-gate hdr.ncnt = (pctype32)h.ncnt; 580*0Sstevel@tonic-gate if (fwrite(&hdr, sizeof (hdr), 1, sfile) != 1) { 581*0Sstevel@tonic-gate perror(sumfile); 582*0Sstevel@tonic-gate exit(EX_IOERR); 583*0Sstevel@tonic-gate } 584*0Sstevel@tonic-gate } 585*0Sstevel@tonic-gate /* 586*0Sstevel@tonic-gate * dump the samples 587*0Sstevel@tonic-gate */ 588*0Sstevel@tonic-gate if (fwrite(samples, sizeof (unsigned_UNIT), nsamples, sfile) != 589*0Sstevel@tonic-gate nsamples) { 590*0Sstevel@tonic-gate perror(sumfile); 591*0Sstevel@tonic-gate exit(EX_IOERR); 592*0Sstevel@tonic-gate } 593*0Sstevel@tonic-gate /* 594*0Sstevel@tonic-gate * dump the normalized raw arc information. For old-style dumping, 595*0Sstevel@tonic-gate * the only namelist is in modules.nl 596*0Sstevel@tonic-gate */ 597*0Sstevel@tonic-gate for (nlp = modules.nl; nlp < modules.npe; nlp++) { 598*0Sstevel@tonic-gate for (arcp = nlp->children; arcp; 599*0Sstevel@tonic-gate arcp = arcp->arc_childlist) { 600*0Sstevel@tonic-gate if (Bflag) { 601*0Sstevel@tonic-gate arc.raw_frompc = arcp->arc_parentp->value; 602*0Sstevel@tonic-gate arc.raw_selfpc = arcp->arc_childp->value; 603*0Sstevel@tonic-gate arc.raw_count = arcp->arc_count; 604*0Sstevel@tonic-gate if (fwrite(&arc, sizeof (arc), 1, sfile) != 1) { 605*0Sstevel@tonic-gate perror(sumfile); 606*0Sstevel@tonic-gate exit(EX_IOERR); 607*0Sstevel@tonic-gate } 608*0Sstevel@tonic-gate } else { 609*0Sstevel@tonic-gate arc32.raw_frompc = 610*0Sstevel@tonic-gate (pctype32)arcp->arc_parentp->value; 611*0Sstevel@tonic-gate arc32.raw_selfpc = 612*0Sstevel@tonic-gate (pctype32)arcp->arc_childp->value; 613*0Sstevel@tonic-gate arc32.raw_count = (actype32)arcp->arc_count; 614*0Sstevel@tonic-gate if (fwrite(&arc32, sizeof (arc32), 1, sfile) != 1) { 615*0Sstevel@tonic-gate perror(sumfile); 616*0Sstevel@tonic-gate exit(EX_IOERR); 617*0Sstevel@tonic-gate } 618*0Sstevel@tonic-gate } 619*0Sstevel@tonic-gate #ifdef DEBUG 620*0Sstevel@tonic-gate if (debug & SAMPLEDEBUG) { 621*0Sstevel@tonic-gate printf("[dumpsum_ostyle] frompc 0x%llx selfpc " 622*0Sstevel@tonic-gate "0x%llx count %lld\n", arc.raw_frompc, 623*0Sstevel@tonic-gate arc.raw_selfpc, arc.raw_count); 624*0Sstevel@tonic-gate } 625*0Sstevel@tonic-gate #endif DEBUG 626*0Sstevel@tonic-gate } 627*0Sstevel@tonic-gate } 628*0Sstevel@tonic-gate fclose(sfile); 629*0Sstevel@tonic-gate } 630*0Sstevel@tonic-gate 631*0Sstevel@tonic-gate /* 632*0Sstevel@tonic-gate * dump out the gmon.sum file 633*0Sstevel@tonic-gate */ 634*0Sstevel@tonic-gate static void 635*0Sstevel@tonic-gate dumpsum(char *sumfile) 636*0Sstevel@tonic-gate { 637*0Sstevel@tonic-gate FILE *sfile; 638*0Sstevel@tonic-gate size_t pathbuf_sz; 639*0Sstevel@tonic-gate unsigned long total_arcs; /* total number of arcs in all */ 640*0Sstevel@tonic-gate unsigned long ncallees; /* no. of callees with parents */ 641*0Sstevel@tonic-gate 642*0Sstevel@tonic-gate if (old_style) { 643*0Sstevel@tonic-gate dumpsum_ostyle(sumfile); 644*0Sstevel@tonic-gate return; 645*0Sstevel@tonic-gate } 646*0Sstevel@tonic-gate 647*0Sstevel@tonic-gate if ((sfile = fopen(sumfile, "w")) == NULL) { 648*0Sstevel@tonic-gate perror(sumfile); 649*0Sstevel@tonic-gate exit(EX_IOERR); 650*0Sstevel@tonic-gate } 651*0Sstevel@tonic-gate 652*0Sstevel@tonic-gate /* 653*0Sstevel@tonic-gate * Dump the new-style gprof header. Even if one of the original 654*0Sstevel@tonic-gate * profiled-files was of a older version, the summed file is of 655*0Sstevel@tonic-gate * current version only. 656*0Sstevel@tonic-gate */ 657*0Sstevel@tonic-gate dump_gprofhdr(sfile, sumfile); 658*0Sstevel@tonic-gate 659*0Sstevel@tonic-gate /* 660*0Sstevel@tonic-gate * Fix up load-maps and dump out modules info 661*0Sstevel@tonic-gate * 662*0Sstevel@tonic-gate * Fix up module load maps so inactive modules get *some* address 663*0Sstevel@tonic-gate * (and btw, could you get the total size of non-aout module path 664*0Sstevel@tonic-gate * strings please ?) 665*0Sstevel@tonic-gate */ 666*0Sstevel@tonic-gate fixup_maps(&pathbuf_sz); 667*0Sstevel@tonic-gate dump_modules(sfile, sumfile, pathbuf_sz); 668*0Sstevel@tonic-gate 669*0Sstevel@tonic-gate 670*0Sstevel@tonic-gate /* 671*0Sstevel@tonic-gate * Dump out the summ'd pcsamples 672*0Sstevel@tonic-gate * 673*0Sstevel@tonic-gate * For dumping call graph information later, we need certain 674*0Sstevel@tonic-gate * statistics (like total arcs, number of callers for each node); 675*0Sstevel@tonic-gate * collect these also while we are at it. 676*0Sstevel@tonic-gate */ 677*0Sstevel@tonic-gate dump_pcsamples(sfile, sumfile, &total_arcs, &ncallees); 678*0Sstevel@tonic-gate 679*0Sstevel@tonic-gate /* 680*0Sstevel@tonic-gate * Dump out the summ'd call graph information 681*0Sstevel@tonic-gate */ 682*0Sstevel@tonic-gate dump_callgraph(sfile, sumfile, total_arcs, ncallees); 683*0Sstevel@tonic-gate 684*0Sstevel@tonic-gate 685*0Sstevel@tonic-gate fclose(sfile); 686*0Sstevel@tonic-gate } 687*0Sstevel@tonic-gate 688*0Sstevel@tonic-gate static void 689*0Sstevel@tonic-gate tally(mod_info_t *caller_mod, mod_info_t *callee_mod, struct rawarc *rawp) 690*0Sstevel@tonic-gate { 691*0Sstevel@tonic-gate nltype *parentp; 692*0Sstevel@tonic-gate nltype *childp; 693*0Sstevel@tonic-gate 694*0Sstevel@tonic-gate /* 695*0Sstevel@tonic-gate * if count == 0 this is a null arc and 696*0Sstevel@tonic-gate * we don't need to tally it. 697*0Sstevel@tonic-gate */ 698*0Sstevel@tonic-gate if (rawp->raw_count == 0) 699*0Sstevel@tonic-gate return; 700*0Sstevel@tonic-gate 701*0Sstevel@tonic-gate /* 702*0Sstevel@tonic-gate * Lookup the caller and callee pcs in namelists of 703*0Sstevel@tonic-gate * appropriate modules 704*0Sstevel@tonic-gate */ 705*0Sstevel@tonic-gate parentp = nllookup(caller_mod, rawp->raw_frompc, NULL); 706*0Sstevel@tonic-gate childp = nllookup(callee_mod, rawp->raw_selfpc, NULL); 707*0Sstevel@tonic-gate if (childp && parentp) { 708*0Sstevel@tonic-gate if (!Dflag) 709*0Sstevel@tonic-gate childp->ncall += rawp->raw_count; 710*0Sstevel@tonic-gate else { 711*0Sstevel@tonic-gate if (first_file) 712*0Sstevel@tonic-gate childp->ncall += rawp->raw_count; 713*0Sstevel@tonic-gate else { 714*0Sstevel@tonic-gate childp->ncall -= rawp->raw_count; 715*0Sstevel@tonic-gate if (childp->ncall < 0) 716*0Sstevel@tonic-gate childp->ncall = 0; 717*0Sstevel@tonic-gate } 718*0Sstevel@tonic-gate } 719*0Sstevel@tonic-gate 720*0Sstevel@tonic-gate #ifdef DEBUG 721*0Sstevel@tonic-gate if (debug & TALLYDEBUG) { 722*0Sstevel@tonic-gate printf("[tally] arc from %s to %s traversed " 723*0Sstevel@tonic-gate "%lld times\n", parentp->name, 724*0Sstevel@tonic-gate childp->name, rawp->raw_count); 725*0Sstevel@tonic-gate } 726*0Sstevel@tonic-gate #endif DEBUG 727*0Sstevel@tonic-gate addarc(parentp, childp, rawp->raw_count); 728*0Sstevel@tonic-gate } 729*0Sstevel@tonic-gate } 730*0Sstevel@tonic-gate 731*0Sstevel@tonic-gate /* 732*0Sstevel@tonic-gate * Look up a module's base address in a sorted list of pc-hits. Unlike 733*0Sstevel@tonic-gate * nllookup(), this deals with misses by mapping them to the next *higher* 734*0Sstevel@tonic-gate * pc-hit. This is so that we get into the module's first pc-hit rightaway, 735*0Sstevel@tonic-gate * even if the module's entry-point (load_base) itself is not a hit. 736*0Sstevel@tonic-gate */ 737*0Sstevel@tonic-gate static Address * 738*0Sstevel@tonic-gate locate(Address *pclist, size_t nelem, Address keypc) 739*0Sstevel@tonic-gate { 740*0Sstevel@tonic-gate size_t low = 0, middle, high = nelem - 1; 741*0Sstevel@tonic-gate 742*0Sstevel@tonic-gate if (keypc <= pclist[low]) 743*0Sstevel@tonic-gate return (pclist); 744*0Sstevel@tonic-gate 745*0Sstevel@tonic-gate if (keypc > pclist[high]) 746*0Sstevel@tonic-gate return (NULL); 747*0Sstevel@tonic-gate 748*0Sstevel@tonic-gate while (low != high) { 749*0Sstevel@tonic-gate middle = (high + low) >> 1; 750*0Sstevel@tonic-gate 751*0Sstevel@tonic-gate if ((pclist[middle] < keypc) && (pclist[middle + 1] >= keypc)) 752*0Sstevel@tonic-gate return (&pclist[middle + 1]); 753*0Sstevel@tonic-gate 754*0Sstevel@tonic-gate if (pclist[middle] >= keypc) 755*0Sstevel@tonic-gate high = middle; 756*0Sstevel@tonic-gate else 757*0Sstevel@tonic-gate low = middle + 1; 758*0Sstevel@tonic-gate } 759*0Sstevel@tonic-gate 760*0Sstevel@tonic-gate /* must never reach here! */ 761*0Sstevel@tonic-gate return (NULL); 762*0Sstevel@tonic-gate } 763*0Sstevel@tonic-gate 764*0Sstevel@tonic-gate static void 765*0Sstevel@tonic-gate assign_pcsamples(module, pcsmpl, n_samples) 766*0Sstevel@tonic-gate mod_info_t *module; 767*0Sstevel@tonic-gate Address *pcsmpl; 768*0Sstevel@tonic-gate size_t n_samples; 769*0Sstevel@tonic-gate { 770*0Sstevel@tonic-gate Address *pcptr, *pcse = pcsmpl + n_samples; 771*0Sstevel@tonic-gate pctype nxt_func; 772*0Sstevel@tonic-gate nltype *fnl; 773*0Sstevel@tonic-gate size_t func_nticks; 774*0Sstevel@tonic-gate #ifdef DEBUG 775*0Sstevel@tonic-gate size_t n_hits_in_module = 0; 776*0Sstevel@tonic-gate #endif DEBUG 777*0Sstevel@tonic-gate 778*0Sstevel@tonic-gate /* Locate the first pc-hit for this module */ 779*0Sstevel@tonic-gate if ((pcptr = locate(pcsmpl, n_samples, module->load_base)) == NULL) { 780*0Sstevel@tonic-gate #ifdef DEBUG 781*0Sstevel@tonic-gate if (debug & PCSMPLDEBUG) { 782*0Sstevel@tonic-gate printf("[assign_pcsamples] no pc-hits in\n"); 783*0Sstevel@tonic-gate printf(" `%s'\n", module->name); 784*0Sstevel@tonic-gate } 785*0Sstevel@tonic-gate #endif DEBUG 786*0Sstevel@tonic-gate return; /* no pc-hits in this module */ 787*0Sstevel@tonic-gate } 788*0Sstevel@tonic-gate 789*0Sstevel@tonic-gate /* Assign all pc-hits in this module to appropriate functions */ 790*0Sstevel@tonic-gate while ((pcptr < pcse) && (*pcptr < module->load_end)) { 791*0Sstevel@tonic-gate 792*0Sstevel@tonic-gate /* Update the corresponding function's time */ 793*0Sstevel@tonic-gate if (fnl = nllookup(module, (pctype) *pcptr, &nxt_func)) { 794*0Sstevel@tonic-gate /* 795*0Sstevel@tonic-gate * Collect all pc-hits in this function. Each 796*0Sstevel@tonic-gate * pc-hit counts as 1 tick. 797*0Sstevel@tonic-gate */ 798*0Sstevel@tonic-gate func_nticks = 0; 799*0Sstevel@tonic-gate while ((pcptr < pcse) && (*pcptr < nxt_func)) { 800*0Sstevel@tonic-gate func_nticks++; 801*0Sstevel@tonic-gate pcptr++; 802*0Sstevel@tonic-gate } 803*0Sstevel@tonic-gate 804*0Sstevel@tonic-gate if (func_nticks == 0) 805*0Sstevel@tonic-gate pcptr++; 806*0Sstevel@tonic-gate else { 807*0Sstevel@tonic-gate fnl->nticks += func_nticks; 808*0Sstevel@tonic-gate fnl->time += func_nticks; 809*0Sstevel@tonic-gate totime += func_nticks; 810*0Sstevel@tonic-gate } 811*0Sstevel@tonic-gate 812*0Sstevel@tonic-gate #ifdef DEBUG 813*0Sstevel@tonic-gate n_hits_in_module += func_nticks; 814*0Sstevel@tonic-gate #endif DEBUG 815*0Sstevel@tonic-gate } else { 816*0Sstevel@tonic-gate /* 817*0Sstevel@tonic-gate * pc sample could not be assigned to function; 818*0Sstevel@tonic-gate * probably in a PLT 819*0Sstevel@tonic-gate */ 820*0Sstevel@tonic-gate pcptr++; 821*0Sstevel@tonic-gate } 822*0Sstevel@tonic-gate } 823*0Sstevel@tonic-gate 824*0Sstevel@tonic-gate #ifdef DEBUG 825*0Sstevel@tonic-gate if (debug & PCSMPLDEBUG) { 826*0Sstevel@tonic-gate printf("[assign_pcsamples] %ld hits in\n", n_hits_in_module); 827*0Sstevel@tonic-gate printf(" `%s'\n", module->name); 828*0Sstevel@tonic-gate } 829*0Sstevel@tonic-gate #endif DEBUG 830*0Sstevel@tonic-gate } 831*0Sstevel@tonic-gate 832*0Sstevel@tonic-gate int 833*0Sstevel@tonic-gate pc_cmp(Address *pc1, Address *pc2) 834*0Sstevel@tonic-gate { 835*0Sstevel@tonic-gate if (*pc1 > *pc2) 836*0Sstevel@tonic-gate return (1); 837*0Sstevel@tonic-gate 838*0Sstevel@tonic-gate if (*pc1 < *pc2) 839*0Sstevel@tonic-gate return (-1); 840*0Sstevel@tonic-gate 841*0Sstevel@tonic-gate return (0); 842*0Sstevel@tonic-gate } 843*0Sstevel@tonic-gate 844*0Sstevel@tonic-gate static void 845*0Sstevel@tonic-gate process_pcsamples(bufp) 846*0Sstevel@tonic-gate ProfBuffer *bufp; 847*0Sstevel@tonic-gate { 848*0Sstevel@tonic-gate Address *pc_samples; 849*0Sstevel@tonic-gate mod_info_t *mi; 850*0Sstevel@tonic-gate caddr_t p; 851*0Sstevel@tonic-gate size_t chunk_size, nelem_read, nelem_to_read; 852*0Sstevel@tonic-gate 853*0Sstevel@tonic-gate #ifdef DEBUG 854*0Sstevel@tonic-gate if (debug & PCSMPLDEBUG) { 855*0Sstevel@tonic-gate printf("[process_pcsamples] number of pcsamples = %lld\n", 856*0Sstevel@tonic-gate bufp->bufsize); 857*0Sstevel@tonic-gate } 858*0Sstevel@tonic-gate #endif DEBUG 859*0Sstevel@tonic-gate 860*0Sstevel@tonic-gate /* buffer with no pc samples ? */ 861*0Sstevel@tonic-gate if (bufp->bufsize == 0) 862*0Sstevel@tonic-gate return; 863*0Sstevel@tonic-gate 864*0Sstevel@tonic-gate /* 865*0Sstevel@tonic-gate * If we're processing pcsamples of a profile sum, we could have 866*0Sstevel@tonic-gate * more than PROF_BUFFER_SIZE number of samples. In such a case, 867*0Sstevel@tonic-gate * we must read the pcsamples in chunks. 868*0Sstevel@tonic-gate */ 869*0Sstevel@tonic-gate if ((chunk_size = bufp->bufsize) > PROF_BUFFER_SIZE) 870*0Sstevel@tonic-gate chunk_size = PROF_BUFFER_SIZE; 871*0Sstevel@tonic-gate 872*0Sstevel@tonic-gate /* Allocate for the pcsample chunk */ 873*0Sstevel@tonic-gate pc_samples = (Address *) calloc(chunk_size, sizeof (Address)); 874*0Sstevel@tonic-gate if (pc_samples == NULL) { 875*0Sstevel@tonic-gate fprintf(stderr, "%s: no room for %ld sample pc's\n", 876*0Sstevel@tonic-gate whoami, chunk_size); 877*0Sstevel@tonic-gate exit(EX_OSERR); 878*0Sstevel@tonic-gate } 879*0Sstevel@tonic-gate 880*0Sstevel@tonic-gate /* Copy the current set of pcsamples */ 881*0Sstevel@tonic-gate nelem_read = 0; 882*0Sstevel@tonic-gate nelem_to_read = bufp->bufsize; 883*0Sstevel@tonic-gate p = (char *) bufp + bufp->buffer; 884*0Sstevel@tonic-gate 885*0Sstevel@tonic-gate while (nelem_read < nelem_to_read) { 886*0Sstevel@tonic-gate memcpy((void *) pc_samples, p, chunk_size * sizeof (Address)); 887*0Sstevel@tonic-gate 888*0Sstevel@tonic-gate /* Sort the pc samples */ 889*0Sstevel@tonic-gate qsort(pc_samples, chunk_size, sizeof (Address), 890*0Sstevel@tonic-gate (int (*)(const void *, const void *)) pc_cmp); 891*0Sstevel@tonic-gate 892*0Sstevel@tonic-gate /* 893*0Sstevel@tonic-gate * Assign pcsamples to functions in the currently active 894*0Sstevel@tonic-gate * module list 895*0Sstevel@tonic-gate */ 896*0Sstevel@tonic-gate for (mi = &modules; mi; mi = mi->next) { 897*0Sstevel@tonic-gate if (mi->active == FALSE) 898*0Sstevel@tonic-gate continue; 899*0Sstevel@tonic-gate assign_pcsamples(mi, pc_samples, chunk_size); 900*0Sstevel@tonic-gate } 901*0Sstevel@tonic-gate 902*0Sstevel@tonic-gate p += (chunk_size * sizeof (Address)); 903*0Sstevel@tonic-gate nelem_read += chunk_size; 904*0Sstevel@tonic-gate 905*0Sstevel@tonic-gate if ((nelem_to_read - nelem_read) < chunk_size) 906*0Sstevel@tonic-gate chunk_size = nelem_to_read - nelem_read; 907*0Sstevel@tonic-gate } 908*0Sstevel@tonic-gate 909*0Sstevel@tonic-gate free(pc_samples); 910*0Sstevel@tonic-gate 911*0Sstevel@tonic-gate /* Update total number of pcsamples read so far */ 912*0Sstevel@tonic-gate n_pcsamples += bufp->bufsize; 913*0Sstevel@tonic-gate } 914*0Sstevel@tonic-gate 915*0Sstevel@tonic-gate static mod_info_t * 916*0Sstevel@tonic-gate find_module(Address addr) 917*0Sstevel@tonic-gate { 918*0Sstevel@tonic-gate mod_info_t *mi; 919*0Sstevel@tonic-gate 920*0Sstevel@tonic-gate for (mi = &modules; mi; mi = mi->next) { 921*0Sstevel@tonic-gate if (mi->active == FALSE) 922*0Sstevel@tonic-gate continue; 923*0Sstevel@tonic-gate 924*0Sstevel@tonic-gate if (addr >= mi->load_base && addr < mi->load_end) 925*0Sstevel@tonic-gate return (mi); 926*0Sstevel@tonic-gate } 927*0Sstevel@tonic-gate 928*0Sstevel@tonic-gate return (NULL); 929*0Sstevel@tonic-gate } 930*0Sstevel@tonic-gate 931*0Sstevel@tonic-gate static void 932*0Sstevel@tonic-gate process_cgraph(cgp) 933*0Sstevel@tonic-gate ProfCallGraph *cgp; 934*0Sstevel@tonic-gate { 935*0Sstevel@tonic-gate struct rawarc arc; 936*0Sstevel@tonic-gate mod_info_t *callee_mi, *caller_mi; 937*0Sstevel@tonic-gate ProfFunction *calleep, *callerp; 938*0Sstevel@tonic-gate Index caller_off, callee_off; 939*0Sstevel@tonic-gate 940*0Sstevel@tonic-gate /* 941*0Sstevel@tonic-gate * Note that *callee_off* increment in the for loop below 942*0Sstevel@tonic-gate * uses *calleep* and *calleep* doesn't get set until the for loop 943*0Sstevel@tonic-gate * is entered. We don't expect the increment to be executed before 944*0Sstevel@tonic-gate * the loop body is executed atleast once, so this should be ok. 945*0Sstevel@tonic-gate */ 946*0Sstevel@tonic-gate for (callee_off = cgp->functions; callee_off; 947*0Sstevel@tonic-gate callee_off = calleep->next_to) { 948*0Sstevel@tonic-gate 949*0Sstevel@tonic-gate calleep = (ProfFunction *) ((char *) cgp + callee_off); 950*0Sstevel@tonic-gate 951*0Sstevel@tonic-gate /* 952*0Sstevel@tonic-gate * We could choose either to sort the {caller, callee} 953*0Sstevel@tonic-gate * list twice and assign callee/caller to modules or inspect 954*0Sstevel@tonic-gate * each callee/caller in the active modules list. Since 955*0Sstevel@tonic-gate * the modules list is usually very small, we'l choose the 956*0Sstevel@tonic-gate * latter. 957*0Sstevel@tonic-gate */ 958*0Sstevel@tonic-gate 959*0Sstevel@tonic-gate /* 960*0Sstevel@tonic-gate * If we cannot identify a callee with a module, there's 961*0Sstevel@tonic-gate * no use worrying about who called it. 962*0Sstevel@tonic-gate */ 963*0Sstevel@tonic-gate if ((callee_mi = find_module(calleep->topc)) == NULL) { 964*0Sstevel@tonic-gate #ifdef DEBUG 965*0Sstevel@tonic-gate if (debug & CGRAPHDEBUG) { 966*0Sstevel@tonic-gate printf("[process_cgraph] callee %#llx missed\n", 967*0Sstevel@tonic-gate calleep->topc); 968*0Sstevel@tonic-gate } 969*0Sstevel@tonic-gate #endif DEBUG 970*0Sstevel@tonic-gate continue; 971*0Sstevel@tonic-gate } else 972*0Sstevel@tonic-gate arc.raw_selfpc = calleep->topc; 973*0Sstevel@tonic-gate 974*0Sstevel@tonic-gate for (caller_off = callee_off; caller_off; 975*0Sstevel@tonic-gate caller_off = callerp->next_from) { 976*0Sstevel@tonic-gate 977*0Sstevel@tonic-gate callerp = (ProfFunction *) ((char *) cgp + caller_off); 978*0Sstevel@tonic-gate if ((caller_mi = find_module(callerp->frompc)) == 979*0Sstevel@tonic-gate NULL) { 980*0Sstevel@tonic-gate #ifdef DEBUG 981*0Sstevel@tonic-gate if (debug & CGRAPHDEBUG) { 982*0Sstevel@tonic-gate printf("[process_cgraph] caller %#llx " 983*0Sstevel@tonic-gate "missed\n", callerp->frompc); 984*0Sstevel@tonic-gate } 985*0Sstevel@tonic-gate #endif DEBUG 986*0Sstevel@tonic-gate continue; 987*0Sstevel@tonic-gate } 988*0Sstevel@tonic-gate 989*0Sstevel@tonic-gate arc.raw_frompc = callerp->frompc; 990*0Sstevel@tonic-gate arc.raw_count = callerp->count; 991*0Sstevel@tonic-gate 992*0Sstevel@tonic-gate #ifdef DEBUG 993*0Sstevel@tonic-gate if (debug & CGRAPHDEBUG) { 994*0Sstevel@tonic-gate printf("[process_cgraph] arc <%#llx, %#llx, " 995*0Sstevel@tonic-gate "%lld>\n", arc.raw_frompc, 996*0Sstevel@tonic-gate arc.raw_selfpc, arc.raw_count); 997*0Sstevel@tonic-gate } 998*0Sstevel@tonic-gate #endif DEBUG 999*0Sstevel@tonic-gate tally(caller_mi, callee_mi, &arc); 1000*0Sstevel@tonic-gate } 1001*0Sstevel@tonic-gate } 1002*0Sstevel@tonic-gate 1003*0Sstevel@tonic-gate #ifdef DEBUG 1004*0Sstevel@tonic-gate puts("\n"); 1005*0Sstevel@tonic-gate #endif DEBUG 1006*0Sstevel@tonic-gate } 1007*0Sstevel@tonic-gate 1008*0Sstevel@tonic-gate /* 1009*0Sstevel@tonic-gate * Two modules overlap each other if they don't lie completely *outside* 1010*0Sstevel@tonic-gate * each other. 1011*0Sstevel@tonic-gate */ 1012*0Sstevel@tonic-gate static bool 1013*0Sstevel@tonic-gate does_overlap(ProfModule *new, mod_info_t *old) 1014*0Sstevel@tonic-gate { 1015*0Sstevel@tonic-gate /* case 1: new module lies completely *before* the old one */ 1016*0Sstevel@tonic-gate if (new->startaddr < old->load_base && new->endaddr <= old->load_base) 1017*0Sstevel@tonic-gate return (FALSE); 1018*0Sstevel@tonic-gate 1019*0Sstevel@tonic-gate /* case 2: new module lies completely *after* the old one */ 1020*0Sstevel@tonic-gate if (new->startaddr >= old->load_end && new->endaddr >= old->load_end) 1021*0Sstevel@tonic-gate return (FALSE); 1022*0Sstevel@tonic-gate 1023*0Sstevel@tonic-gate /* probably a dlopen: the modules overlap each other */ 1024*0Sstevel@tonic-gate return (TRUE); 1025*0Sstevel@tonic-gate } 1026*0Sstevel@tonic-gate 1027*0Sstevel@tonic-gate static bool 1028*0Sstevel@tonic-gate is_same_as_aout(char *modpath, struct stat *buf) 1029*0Sstevel@tonic-gate { 1030*0Sstevel@tonic-gate if (stat(modpath, buf) == -1) { 1031*0Sstevel@tonic-gate fprintf(stderr, "%s: can't get info on `%s'\n", 1032*0Sstevel@tonic-gate whoami, modpath); 1033*0Sstevel@tonic-gate exit(EX_NOINPUT); 1034*0Sstevel@tonic-gate } 1035*0Sstevel@tonic-gate 1036*0Sstevel@tonic-gate if ((buf->st_dev == aout_info.dev) && (buf->st_ino == aout_info.ino)) 1037*0Sstevel@tonic-gate return (TRUE); 1038*0Sstevel@tonic-gate else 1039*0Sstevel@tonic-gate return (FALSE); 1040*0Sstevel@tonic-gate } 1041*0Sstevel@tonic-gate 1042*0Sstevel@tonic-gate static void 1043*0Sstevel@tonic-gate process_modules(modlp) 1044*0Sstevel@tonic-gate ProfModuleList *modlp; 1045*0Sstevel@tonic-gate { 1046*0Sstevel@tonic-gate ProfModule *newmodp; 1047*0Sstevel@tonic-gate mod_info_t *mi, *last, *new_module; 1048*0Sstevel@tonic-gate char *so_path, *name; 1049*0Sstevel@tonic-gate bool more_modules = TRUE; 1050*0Sstevel@tonic-gate struct stat so_statbuf; 1051*0Sstevel@tonic-gate 1052*0Sstevel@tonic-gate #ifdef DEBUG 1053*0Sstevel@tonic-gate if (debug & MODULEDEBUG) { 1054*0Sstevel@tonic-gate printf("[process_modules] module obj version %u\n", 1055*0Sstevel@tonic-gate modlp->version); 1056*0Sstevel@tonic-gate } 1057*0Sstevel@tonic-gate #endif DEBUG 1058*0Sstevel@tonic-gate 1059*0Sstevel@tonic-gate /* Check version of module type object */ 1060*0Sstevel@tonic-gate if (modlp->version > PROF_MODULES_VER) { 1061*0Sstevel@tonic-gate fprintf(stderr, "%s: version %d for module type objects" 1062*0Sstevel@tonic-gate "is not supported\n", whoami, modlp->version); 1063*0Sstevel@tonic-gate exit(EX_SOFTWARE); 1064*0Sstevel@tonic-gate } 1065*0Sstevel@tonic-gate 1066*0Sstevel@tonic-gate 1067*0Sstevel@tonic-gate /* 1068*0Sstevel@tonic-gate * Scan the PROF_MODULES_T list and add modules to current list 1069*0Sstevel@tonic-gate * of modules, if they're not present already 1070*0Sstevel@tonic-gate */ 1071*0Sstevel@tonic-gate newmodp = (ProfModule *) ((char *) modlp + modlp->modules); 1072*0Sstevel@tonic-gate do { 1073*0Sstevel@tonic-gate /* 1074*0Sstevel@tonic-gate * Since the prog could've been renamed after its run, we 1075*0Sstevel@tonic-gate * should see if this overlaps a.out. If it does, it is 1076*0Sstevel@tonic-gate * probably the renamed aout. We should also skip any other 1077*0Sstevel@tonic-gate * non-sharedobj's that we see (or should we report an error ?) 1078*0Sstevel@tonic-gate */ 1079*0Sstevel@tonic-gate so_path = (caddr_t) modlp + newmodp->path; 1080*0Sstevel@tonic-gate if (does_overlap(newmodp, &modules) || 1081*0Sstevel@tonic-gate is_same_as_aout(so_path, &so_statbuf) || 1082*0Sstevel@tonic-gate (!is_shared_obj(so_path))) { 1083*0Sstevel@tonic-gate 1084*0Sstevel@tonic-gate if (!newmodp->next) 1085*0Sstevel@tonic-gate more_modules = FALSE; 1086*0Sstevel@tonic-gate 1087*0Sstevel@tonic-gate newmodp = (ProfModule *) 1088*0Sstevel@tonic-gate ((caddr_t) modlp + newmodp->next); 1089*0Sstevel@tonic-gate #ifdef DEBUG 1090*0Sstevel@tonic-gate if (debug & MODULEDEBUG) { 1091*0Sstevel@tonic-gate printf("[process_modules] `%s'\n", so_path); 1092*0Sstevel@tonic-gate printf(" skipped\n"); 1093*0Sstevel@tonic-gate } 1094*0Sstevel@tonic-gate #endif DEBUG 1095*0Sstevel@tonic-gate continue; 1096*0Sstevel@tonic-gate } 1097*0Sstevel@tonic-gate #ifdef DEBUG 1098*0Sstevel@tonic-gate if (debug & MODULEDEBUG) 1099*0Sstevel@tonic-gate printf("[process_modules] `%s'...\n", so_path); 1100*0Sstevel@tonic-gate #endif DEBUG 1101*0Sstevel@tonic-gate 1102*0Sstevel@tonic-gate /* 1103*0Sstevel@tonic-gate * Check all modules (leave the first one, 'cos that 1104*0Sstevel@tonic-gate * is the program executable info). If this module is already 1105*0Sstevel@tonic-gate * there in the list, update the load addresses and proceed. 1106*0Sstevel@tonic-gate */ 1107*0Sstevel@tonic-gate last = &modules; 1108*0Sstevel@tonic-gate while (mi = last->next) { 1109*0Sstevel@tonic-gate /* 1110*0Sstevel@tonic-gate * We expect the full pathname for all shared objects 1111*0Sstevel@tonic-gate * needed by the program executable. In this case, we 1112*0Sstevel@tonic-gate * simply need to compare the paths to see if they are 1113*0Sstevel@tonic-gate * the same file. 1114*0Sstevel@tonic-gate */ 1115*0Sstevel@tonic-gate if (strcmp(mi->name, so_path) == 0) 1116*0Sstevel@tonic-gate break; 1117*0Sstevel@tonic-gate 1118*0Sstevel@tonic-gate /* 1119*0Sstevel@tonic-gate * Check if this new shared object will overlap 1120*0Sstevel@tonic-gate * any existing module. If yes, remove the old one 1121*0Sstevel@tonic-gate * from the linked list (but don't free it, 'cos 1122*0Sstevel@tonic-gate * there may be symbols referring to this module 1123*0Sstevel@tonic-gate * still) 1124*0Sstevel@tonic-gate */ 1125*0Sstevel@tonic-gate if (does_overlap(newmodp, mi)) { 1126*0Sstevel@tonic-gate #ifdef DEBUG 1127*0Sstevel@tonic-gate if (debug & MODULEDEBUG) { 1128*0Sstevel@tonic-gate printf("[process_modules] `%s'\n", 1129*0Sstevel@tonic-gate so_path); 1130*0Sstevel@tonic-gate printf(" overlaps\n"); 1131*0Sstevel@tonic-gate printf(" `%s'\n", 1132*0Sstevel@tonic-gate mi->name); 1133*0Sstevel@tonic-gate } 1134*0Sstevel@tonic-gate #endif DEBUG 1135*0Sstevel@tonic-gate mi->active = FALSE; 1136*0Sstevel@tonic-gate } 1137*0Sstevel@tonic-gate 1138*0Sstevel@tonic-gate last = mi; 1139*0Sstevel@tonic-gate } 1140*0Sstevel@tonic-gate 1141*0Sstevel@tonic-gate /* Module already there, skip it */ 1142*0Sstevel@tonic-gate if (mi != NULL) { 1143*0Sstevel@tonic-gate mi->load_base = newmodp->startaddr; 1144*0Sstevel@tonic-gate mi->load_end = newmodp->endaddr; 1145*0Sstevel@tonic-gate mi->active = TRUE; 1146*0Sstevel@tonic-gate if (!newmodp->next) 1147*0Sstevel@tonic-gate more_modules = FALSE; 1148*0Sstevel@tonic-gate 1149*0Sstevel@tonic-gate newmodp = (ProfModule *) 1150*0Sstevel@tonic-gate ((caddr_t) modlp + newmodp->next); 1151*0Sstevel@tonic-gate 1152*0Sstevel@tonic-gate #ifdef DEBUG 1153*0Sstevel@tonic-gate if (debug & MODULEDEBUG) { 1154*0Sstevel@tonic-gate printf("[process_modules] base=%#llx, " 1155*0Sstevel@tonic-gate "end=%#llx\n", mi->load_base, 1156*0Sstevel@tonic-gate mi->load_end); 1157*0Sstevel@tonic-gate } 1158*0Sstevel@tonic-gate #endif DEBUG 1159*0Sstevel@tonic-gate continue; 1160*0Sstevel@tonic-gate } 1161*0Sstevel@tonic-gate 1162*0Sstevel@tonic-gate /* 1163*0Sstevel@tonic-gate * Check if gmon.out is outdated with respect to the new 1164*0Sstevel@tonic-gate * module we want to add 1165*0Sstevel@tonic-gate */ 1166*0Sstevel@tonic-gate if (gmonout_info.mtime < so_statbuf.st_mtime) { 1167*0Sstevel@tonic-gate fprintf(stderr, "%s: shared obj outdates prof info\n", 1168*0Sstevel@tonic-gate whoami); 1169*0Sstevel@tonic-gate fprintf(stderr, "\t(newer %s)\n", so_path); 1170*0Sstevel@tonic-gate exit(EX_NOINPUT); 1171*0Sstevel@tonic-gate } 1172*0Sstevel@tonic-gate 1173*0Sstevel@tonic-gate /* Create a new module element */ 1174*0Sstevel@tonic-gate new_module = (mod_info_t *) malloc(sizeof (mod_info_t)); 1175*0Sstevel@tonic-gate if (new_module == NULL) { 1176*0Sstevel@tonic-gate fprintf(stderr, "%s: no room for %ld bytes\n", 1177*0Sstevel@tonic-gate whoami, sizeof (mod_info_t)); 1178*0Sstevel@tonic-gate exit(EX_OSERR); 1179*0Sstevel@tonic-gate } 1180*0Sstevel@tonic-gate 1181*0Sstevel@tonic-gate /* and fill in info... */ 1182*0Sstevel@tonic-gate new_module->id = n_modules + 1; 1183*0Sstevel@tonic-gate new_module->load_base = newmodp->startaddr; 1184*0Sstevel@tonic-gate new_module->load_end = newmodp->endaddr; 1185*0Sstevel@tonic-gate new_module->name = (char *) malloc(strlen(so_path) + 1); 1186*0Sstevel@tonic-gate if (new_module->name == NULL) { 1187*0Sstevel@tonic-gate fprintf(stderr, "%s: no room for %ld bytes\n", 1188*0Sstevel@tonic-gate whoami, strlen(so_path) + 1); 1189*0Sstevel@tonic-gate exit(EX_OSERR); 1190*0Sstevel@tonic-gate } 1191*0Sstevel@tonic-gate strcpy(new_module->name, so_path); 1192*0Sstevel@tonic-gate #ifdef DEBUG 1193*0Sstevel@tonic-gate if (debug & MODULEDEBUG) { 1194*0Sstevel@tonic-gate printf("[process_modules] base=%#llx, end=%#llx\n", 1195*0Sstevel@tonic-gate new_module->load_base, new_module->load_end); 1196*0Sstevel@tonic-gate } 1197*0Sstevel@tonic-gate #endif DEBUG 1198*0Sstevel@tonic-gate 1199*0Sstevel@tonic-gate /* Create this module's nameslist */ 1200*0Sstevel@tonic-gate process_namelist(new_module); 1201*0Sstevel@tonic-gate 1202*0Sstevel@tonic-gate /* Add it to the tail of active module list */ 1203*0Sstevel@tonic-gate last->next = new_module; 1204*0Sstevel@tonic-gate n_modules++; 1205*0Sstevel@tonic-gate 1206*0Sstevel@tonic-gate #ifdef DEBUG 1207*0Sstevel@tonic-gate if (debug & MODULEDEBUG) { 1208*0Sstevel@tonic-gate printf("[process_modules] total shared objects = %ld\n", 1209*0Sstevel@tonic-gate n_modules - 1); 1210*0Sstevel@tonic-gate } 1211*0Sstevel@tonic-gate #endif DEBUG 1212*0Sstevel@tonic-gate /* 1213*0Sstevel@tonic-gate * Move to the next module in the PROF_MODULES_T list 1214*0Sstevel@tonic-gate * (if present) 1215*0Sstevel@tonic-gate */ 1216*0Sstevel@tonic-gate if (!newmodp->next) 1217*0Sstevel@tonic-gate more_modules = FALSE; 1218*0Sstevel@tonic-gate 1219*0Sstevel@tonic-gate newmodp = (ProfModule *) ((caddr_t) modlp + newmodp->next); 1220*0Sstevel@tonic-gate 1221*0Sstevel@tonic-gate } while (more_modules); 1222*0Sstevel@tonic-gate } 1223*0Sstevel@tonic-gate 1224*0Sstevel@tonic-gate static void 1225*0Sstevel@tonic-gate reset_active_modules() 1226*0Sstevel@tonic-gate { 1227*0Sstevel@tonic-gate mod_info_t *mi; 1228*0Sstevel@tonic-gate 1229*0Sstevel@tonic-gate /* Except the executable, no other module should remain active */ 1230*0Sstevel@tonic-gate for (mi = modules.next; mi; mi = mi->next) 1231*0Sstevel@tonic-gate mi->active = FALSE; 1232*0Sstevel@tonic-gate } 1233*0Sstevel@tonic-gate 1234*0Sstevel@tonic-gate static void 1235*0Sstevel@tonic-gate getpfiledata(memp, fsz) 1236*0Sstevel@tonic-gate caddr_t memp; 1237*0Sstevel@tonic-gate size_t fsz; 1238*0Sstevel@tonic-gate { 1239*0Sstevel@tonic-gate ProfObject *objp; 1240*0Sstevel@tonic-gate caddr_t file_end; 1241*0Sstevel@tonic-gate bool found_pcsamples = FALSE, found_cgraph = FALSE; 1242*0Sstevel@tonic-gate 1243*0Sstevel@tonic-gate /* 1244*0Sstevel@tonic-gate * Before processing a new gmon.out, all modules except the 1245*0Sstevel@tonic-gate * program executable must be made inactive, so that symbols 1246*0Sstevel@tonic-gate * are searched only in the program executable, if we don't 1247*0Sstevel@tonic-gate * find a MODULES_T object. Don't do it *after* we read a gmon.out, 1248*0Sstevel@tonic-gate * because we need the active module data after we're done with 1249*0Sstevel@tonic-gate * the last gmon.out, if we're doing summing. 1250*0Sstevel@tonic-gate */ 1251*0Sstevel@tonic-gate reset_active_modules(); 1252*0Sstevel@tonic-gate 1253*0Sstevel@tonic-gate file_end = memp + fsz; 1254*0Sstevel@tonic-gate objp = (ProfObject *) (memp + ((ProfHeader *) memp)->size); 1255*0Sstevel@tonic-gate while ((caddr_t) objp < file_end) { 1256*0Sstevel@tonic-gate #ifdef DEBUG 1257*0Sstevel@tonic-gate { 1258*0Sstevel@tonic-gate unsigned int type = 0; 1259*0Sstevel@tonic-gate 1260*0Sstevel@tonic-gate if (debug & MONOUTDEBUG) { 1261*0Sstevel@tonic-gate if (objp->type <= MAX_OBJTYPES) 1262*0Sstevel@tonic-gate type = objp->type; 1263*0Sstevel@tonic-gate 1264*0Sstevel@tonic-gate printf("\n[getpfiledata] object %s [%#lx]\n", 1265*0Sstevel@tonic-gate objname[type], objp->type); 1266*0Sstevel@tonic-gate } 1267*0Sstevel@tonic-gate } 1268*0Sstevel@tonic-gate #endif DEBUG 1269*0Sstevel@tonic-gate switch (objp->type) { 1270*0Sstevel@tonic-gate case PROF_MODULES_T : 1271*0Sstevel@tonic-gate process_modules((ProfModuleList *) objp); 1272*0Sstevel@tonic-gate break; 1273*0Sstevel@tonic-gate 1274*0Sstevel@tonic-gate case PROF_CALLGRAPH_T : 1275*0Sstevel@tonic-gate process_cgraph((ProfCallGraph *) objp); 1276*0Sstevel@tonic-gate found_cgraph = TRUE; 1277*0Sstevel@tonic-gate break; 1278*0Sstevel@tonic-gate 1279*0Sstevel@tonic-gate case PROF_BUFFER_T : 1280*0Sstevel@tonic-gate process_pcsamples((ProfBuffer *) objp); 1281*0Sstevel@tonic-gate found_pcsamples = TRUE; 1282*0Sstevel@tonic-gate break; 1283*0Sstevel@tonic-gate 1284*0Sstevel@tonic-gate default : 1285*0Sstevel@tonic-gate fprintf(stderr, 1286*0Sstevel@tonic-gate "%s: unknown prof object type=%d\n", 1287*0Sstevel@tonic-gate whoami, objp->type); 1288*0Sstevel@tonic-gate exit(EX_SOFTWARE); 1289*0Sstevel@tonic-gate } 1290*0Sstevel@tonic-gate objp = (ProfObject *) ((caddr_t) objp + objp->size); 1291*0Sstevel@tonic-gate } 1292*0Sstevel@tonic-gate 1293*0Sstevel@tonic-gate if (!found_cgraph || !found_pcsamples) { 1294*0Sstevel@tonic-gate fprintf(stderr, 1295*0Sstevel@tonic-gate "%s: missing callgraph/pcsamples object\n", whoami); 1296*0Sstevel@tonic-gate exit(EX_SOFTWARE); 1297*0Sstevel@tonic-gate } 1298*0Sstevel@tonic-gate 1299*0Sstevel@tonic-gate if ((caddr_t) objp > file_end) { 1300*0Sstevel@tonic-gate fprintf(stderr, "%s: malformed profile file.\n", whoami); 1301*0Sstevel@tonic-gate exit(EX_SOFTWARE); 1302*0Sstevel@tonic-gate } 1303*0Sstevel@tonic-gate 1304*0Sstevel@tonic-gate if (first_file) 1305*0Sstevel@tonic-gate first_file = FALSE; 1306*0Sstevel@tonic-gate } 1307*0Sstevel@tonic-gate 1308*0Sstevel@tonic-gate static void 1309*0Sstevel@tonic-gate readarcs(pfile) 1310*0Sstevel@tonic-gate FILE *pfile; 1311*0Sstevel@tonic-gate { 1312*0Sstevel@tonic-gate /* 1313*0Sstevel@tonic-gate * the rest of the file consists of 1314*0Sstevel@tonic-gate * a bunch of <from,self,count> tuples. 1315*0Sstevel@tonic-gate */ 1316*0Sstevel@tonic-gate /* CONSTCOND */ 1317*0Sstevel@tonic-gate while (1) { 1318*0Sstevel@tonic-gate struct rawarc arc; 1319*0Sstevel@tonic-gate 1320*0Sstevel@tonic-gate if (rflag) { 1321*0Sstevel@tonic-gate if (Bflag) { 1322*0Sstevel@tonic-gate L_cgarc64 rtld_arc64; 1323*0Sstevel@tonic-gate 1324*0Sstevel@tonic-gate /* 1325*0Sstevel@tonic-gate * If rflag is set then this is an profiled 1326*0Sstevel@tonic-gate * image generated by rtld. It needs to be 1327*0Sstevel@tonic-gate * 'converted' to the standard data format. 1328*0Sstevel@tonic-gate */ 1329*0Sstevel@tonic-gate if (fread(&rtld_arc64, 1330*0Sstevel@tonic-gate sizeof (L_cgarc64), 1, pfile) != 1) 1331*0Sstevel@tonic-gate break; 1332*0Sstevel@tonic-gate 1333*0Sstevel@tonic-gate if (rtld_arc64.cg_from == PRF_OUTADDR64) 1334*0Sstevel@tonic-gate arc.raw_frompc = s_highpc + 0x10; 1335*0Sstevel@tonic-gate else 1336*0Sstevel@tonic-gate arc.raw_frompc = 1337*0Sstevel@tonic-gate (pctype)rtld_arc64.cg_from; 1338*0Sstevel@tonic-gate arc.raw_selfpc = (pctype)rtld_arc64.cg_to; 1339*0Sstevel@tonic-gate arc.raw_count = (actype)rtld_arc64.cg_count; 1340*0Sstevel@tonic-gate } else { 1341*0Sstevel@tonic-gate L_cgarc rtld_arc; 1342*0Sstevel@tonic-gate 1343*0Sstevel@tonic-gate /* 1344*0Sstevel@tonic-gate * If rflag is set then this is an profiled 1345*0Sstevel@tonic-gate * image generated by rtld. It needs to be 1346*0Sstevel@tonic-gate * 'converted' to the standard data format. 1347*0Sstevel@tonic-gate */ 1348*0Sstevel@tonic-gate if (fread(&rtld_arc, 1349*0Sstevel@tonic-gate sizeof (L_cgarc), 1, pfile) != 1) 1350*0Sstevel@tonic-gate break; 1351*0Sstevel@tonic-gate 1352*0Sstevel@tonic-gate if (rtld_arc.cg_from == PRF_OUTADDR) 1353*0Sstevel@tonic-gate arc.raw_frompc = s_highpc + 0x10; 1354*0Sstevel@tonic-gate else 1355*0Sstevel@tonic-gate arc.raw_frompc = (pctype) 1356*0Sstevel@tonic-gate (uintptr_t)rtld_arc.cg_from; 1357*0Sstevel@tonic-gate arc.raw_selfpc = (pctype) 1358*0Sstevel@tonic-gate (uintptr_t)rtld_arc.cg_to; 1359*0Sstevel@tonic-gate arc.raw_count = (actype)rtld_arc.cg_count; 1360*0Sstevel@tonic-gate } 1361*0Sstevel@tonic-gate } else { 1362*0Sstevel@tonic-gate if (Bflag) { 1363*0Sstevel@tonic-gate if (fread(&arc, sizeof (struct rawarc), 1, 1364*0Sstevel@tonic-gate pfile) != 1) { 1365*0Sstevel@tonic-gate break; 1366*0Sstevel@tonic-gate } 1367*0Sstevel@tonic-gate } else { 1368*0Sstevel@tonic-gate /* 1369*0Sstevel@tonic-gate * If these aren't big %pc's, we need to read 1370*0Sstevel@tonic-gate * into the 32-bit raw arc structure, and 1371*0Sstevel@tonic-gate * assign the members into the actual arc. 1372*0Sstevel@tonic-gate */ 1373*0Sstevel@tonic-gate struct rawarc32 arc32; 1374*0Sstevel@tonic-gate if (fread(&arc32, sizeof (struct rawarc32), 1375*0Sstevel@tonic-gate 1, pfile) != 1) 1376*0Sstevel@tonic-gate break; 1377*0Sstevel@tonic-gate arc.raw_frompc = (pctype)arc32.raw_frompc; 1378*0Sstevel@tonic-gate arc.raw_selfpc = (pctype)arc32.raw_selfpc; 1379*0Sstevel@tonic-gate arc.raw_count = (actype)arc32.raw_count; 1380*0Sstevel@tonic-gate } 1381*0Sstevel@tonic-gate } 1382*0Sstevel@tonic-gate 1383*0Sstevel@tonic-gate #ifdef DEBUG 1384*0Sstevel@tonic-gate if (debug & SAMPLEDEBUG) { 1385*0Sstevel@tonic-gate printf("[getpfile] frompc 0x%llx selfpc " 1386*0Sstevel@tonic-gate "0x%llx count %lld\n", arc.raw_frompc, 1387*0Sstevel@tonic-gate arc.raw_selfpc, arc.raw_count); 1388*0Sstevel@tonic-gate } 1389*0Sstevel@tonic-gate #endif DEBUG 1390*0Sstevel@tonic-gate /* 1391*0Sstevel@tonic-gate * add this arc 1392*0Sstevel@tonic-gate */ 1393*0Sstevel@tonic-gate tally(&modules, &modules, &arc); 1394*0Sstevel@tonic-gate } 1395*0Sstevel@tonic-gate if (first_file) 1396*0Sstevel@tonic-gate first_file = FALSE; 1397*0Sstevel@tonic-gate } 1398*0Sstevel@tonic-gate 1399*0Sstevel@tonic-gate static void 1400*0Sstevel@tonic-gate readsamples(FILE *pfile) 1401*0Sstevel@tonic-gate { 1402*0Sstevel@tonic-gate sztype i; 1403*0Sstevel@tonic-gate unsigned_UNIT sample; 1404*0Sstevel@tonic-gate 1405*0Sstevel@tonic-gate if (samples == 0) { 1406*0Sstevel@tonic-gate samples = (unsigned_UNIT *) calloc(nsamples, 1407*0Sstevel@tonic-gate sizeof (unsigned_UNIT)); 1408*0Sstevel@tonic-gate if (samples == 0) { 1409*0Sstevel@tonic-gate fprintf(stderr, "%s: No room for %ld sample pc's\n", 1410*0Sstevel@tonic-gate whoami, sampbytes / sizeof (unsigned_UNIT)); 1411*0Sstevel@tonic-gate exit(EX_OSERR); 1412*0Sstevel@tonic-gate } 1413*0Sstevel@tonic-gate } 1414*0Sstevel@tonic-gate 1415*0Sstevel@tonic-gate for (i = 0; i < nsamples; i++) { 1416*0Sstevel@tonic-gate fread(&sample, sizeof (unsigned_UNIT), 1, pfile); 1417*0Sstevel@tonic-gate if (feof(pfile)) 1418*0Sstevel@tonic-gate break; 1419*0Sstevel@tonic-gate samples[i] += sample; 1420*0Sstevel@tonic-gate } 1421*0Sstevel@tonic-gate if (i != nsamples) { 1422*0Sstevel@tonic-gate fprintf(stderr, 1423*0Sstevel@tonic-gate "%s: unexpected EOF after reading %ld/%ld samples\n", 1424*0Sstevel@tonic-gate whoami, --i, nsamples); 1425*0Sstevel@tonic-gate exit(EX_IOERR); 1426*0Sstevel@tonic-gate } 1427*0Sstevel@tonic-gate } 1428*0Sstevel@tonic-gate 1429*0Sstevel@tonic-gate static void * 1430*0Sstevel@tonic-gate handle_versioned(FILE *pfile, char *filename, size_t *fsz) 1431*0Sstevel@tonic-gate { 1432*0Sstevel@tonic-gate int fd; 1433*0Sstevel@tonic-gate bool invalid_version; 1434*0Sstevel@tonic-gate caddr_t fmem; 1435*0Sstevel@tonic-gate struct stat buf; 1436*0Sstevel@tonic-gate ProfHeader prof_hdr; 1437*0Sstevel@tonic-gate 1438*0Sstevel@tonic-gate /* 1439*0Sstevel@tonic-gate * Check versioning info. For now, let's say we provide 1440*0Sstevel@tonic-gate * backward compatibility, so we accept all older versions. 1441*0Sstevel@tonic-gate */ 1442*0Sstevel@tonic-gate if (fread(&prof_hdr, sizeof (ProfHeader), 1, pfile) == 0) { 1443*0Sstevel@tonic-gate perror("fread()"); 1444*0Sstevel@tonic-gate exit(EX_IOERR); 1445*0Sstevel@tonic-gate } 1446*0Sstevel@tonic-gate 1447*0Sstevel@tonic-gate invalid_version = FALSE; 1448*0Sstevel@tonic-gate if (prof_hdr.h_major_ver > PROF_MAJOR_VERSION) 1449*0Sstevel@tonic-gate invalid_version = TRUE; 1450*0Sstevel@tonic-gate else if (prof_hdr.h_major_ver == PROF_MAJOR_VERSION) { 1451*0Sstevel@tonic-gate if (prof_hdr.h_minor_ver > PROF_MINOR_VERSION) 1452*0Sstevel@tonic-gate invalid_version = FALSE; 1453*0Sstevel@tonic-gate } 1454*0Sstevel@tonic-gate 1455*0Sstevel@tonic-gate if (invalid_version) { 1456*0Sstevel@tonic-gate fprintf(stderr, "%s: version %d.%d not supported\n", 1457*0Sstevel@tonic-gate whoami, prof_hdr.h_major_ver, prof_hdr.h_minor_ver); 1458*0Sstevel@tonic-gate exit(EX_SOFTWARE); 1459*0Sstevel@tonic-gate } 1460*0Sstevel@tonic-gate 1461*0Sstevel@tonic-gate /* 1462*0Sstevel@tonic-gate * Map gmon.out onto memory. 1463*0Sstevel@tonic-gate */ 1464*0Sstevel@tonic-gate fclose(pfile); 1465*0Sstevel@tonic-gate if ((fd = open(filename, O_RDONLY)) == -1) { 1466*0Sstevel@tonic-gate perror(filename); 1467*0Sstevel@tonic-gate exit(EX_IOERR); 1468*0Sstevel@tonic-gate } 1469*0Sstevel@tonic-gate 1470*0Sstevel@tonic-gate if ((*fsz = lseek(fd, 0, SEEK_END)) == -1) { 1471*0Sstevel@tonic-gate perror(filename); 1472*0Sstevel@tonic-gate exit(EX_IOERR); 1473*0Sstevel@tonic-gate } 1474*0Sstevel@tonic-gate 1475*0Sstevel@tonic-gate fmem = mmap(0, *fsz, PROT_READ, MAP_PRIVATE, fd, 0); 1476*0Sstevel@tonic-gate if (fmem == MAP_FAILED) { 1477*0Sstevel@tonic-gate fprintf(stderr, "%s: can't map %s\n", whoami, filename); 1478*0Sstevel@tonic-gate exit(EX_IOERR); 1479*0Sstevel@tonic-gate } 1480*0Sstevel@tonic-gate 1481*0Sstevel@tonic-gate /* 1482*0Sstevel@tonic-gate * Before we close this fd, save this gmon.out's info to later verify 1483*0Sstevel@tonic-gate * if the shared objects it references have changed since the time 1484*0Sstevel@tonic-gate * they were used to generate this gmon.out 1485*0Sstevel@tonic-gate */ 1486*0Sstevel@tonic-gate if (fstat(fd, &buf) == -1) { 1487*0Sstevel@tonic-gate fprintf(stderr, "%s: can't get info on `%s'\n", 1488*0Sstevel@tonic-gate whoami, filename); 1489*0Sstevel@tonic-gate exit(EX_NOINPUT); 1490*0Sstevel@tonic-gate } 1491*0Sstevel@tonic-gate gmonout_info.dev = buf.st_dev; 1492*0Sstevel@tonic-gate gmonout_info.ino = buf.st_ino; 1493*0Sstevel@tonic-gate gmonout_info.mtime = buf.st_mtime; 1494*0Sstevel@tonic-gate gmonout_info.size = buf.st_size; 1495*0Sstevel@tonic-gate 1496*0Sstevel@tonic-gate close(fd); 1497*0Sstevel@tonic-gate 1498*0Sstevel@tonic-gate return ((void *) fmem); 1499*0Sstevel@tonic-gate } 1500*0Sstevel@tonic-gate 1501*0Sstevel@tonic-gate static void * 1502*0Sstevel@tonic-gate openpfile(filename, fsz) 1503*0Sstevel@tonic-gate char *filename; 1504*0Sstevel@tonic-gate size_t *fsz; 1505*0Sstevel@tonic-gate { 1506*0Sstevel@tonic-gate struct hdr tmp; 1507*0Sstevel@tonic-gate FILE * pfile; 1508*0Sstevel@tonic-gate unsigned long magic_num; 1509*0Sstevel@tonic-gate size_t hdrsize = sizeof (struct hdr); 1510*0Sstevel@tonic-gate static bool first_time = TRUE; 1511*0Sstevel@tonic-gate extern bool old_style; 1512*0Sstevel@tonic-gate 1513*0Sstevel@tonic-gate if ((pfile = fopen(filename, "r")) == NULL) { 1514*0Sstevel@tonic-gate perror(filename); 1515*0Sstevel@tonic-gate exit(EX_IOERR); 1516*0Sstevel@tonic-gate } 1517*0Sstevel@tonic-gate 1518*0Sstevel@tonic-gate /* 1519*0Sstevel@tonic-gate * Read in the magic. Note that we changed the cast "unsigned long" 1520*0Sstevel@tonic-gate * to "unsigned int" because that's how h_magic is defined in the 1521*0Sstevel@tonic-gate * new format ProfHeader. 1522*0Sstevel@tonic-gate */ 1523*0Sstevel@tonic-gate if (fread(&magic_num, sizeof (unsigned int), 1, pfile) == 0) { 1524*0Sstevel@tonic-gate perror("fread()"); 1525*0Sstevel@tonic-gate exit(EX_IOERR); 1526*0Sstevel@tonic-gate } 1527*0Sstevel@tonic-gate 1528*0Sstevel@tonic-gate rewind(pfile); 1529*0Sstevel@tonic-gate 1530*0Sstevel@tonic-gate /* 1531*0Sstevel@tonic-gate * First check if this is versioned or *old-style* gmon.out 1532*0Sstevel@tonic-gate */ 1533*0Sstevel@tonic-gate if (magic_num == (unsigned int)PROF_MAGIC) { 1534*0Sstevel@tonic-gate if ((!first_time) && (old_style == TRUE)) { 1535*0Sstevel@tonic-gate fprintf(stderr, "%s: can't mix old & new format " 1536*0Sstevel@tonic-gate "profiled files\n", whoami); 1537*0Sstevel@tonic-gate exit(EX_SOFTWARE); 1538*0Sstevel@tonic-gate } 1539*0Sstevel@tonic-gate first_time = FALSE; 1540*0Sstevel@tonic-gate old_style = FALSE; 1541*0Sstevel@tonic-gate return (handle_versioned(pfile, filename, fsz)); 1542*0Sstevel@tonic-gate } 1543*0Sstevel@tonic-gate 1544*0Sstevel@tonic-gate if ((!first_time) && (old_style == FALSE)) { 1545*0Sstevel@tonic-gate fprintf(stderr, "%s: can't mix old & new format " 1546*0Sstevel@tonic-gate "profiled files\n", whoami); 1547*0Sstevel@tonic-gate exit(EX_SOFTWARE); 1548*0Sstevel@tonic-gate } 1549*0Sstevel@tonic-gate 1550*0Sstevel@tonic-gate first_time = FALSE; 1551*0Sstevel@tonic-gate old_style = TRUE; 1552*0Sstevel@tonic-gate fsz = 0; 1553*0Sstevel@tonic-gate 1554*0Sstevel@tonic-gate /* 1555*0Sstevel@tonic-gate * Now, we need to determine if this is a run-time linker 1556*0Sstevel@tonic-gate * profiled file or if it is a standard gmon.out. 1557*0Sstevel@tonic-gate * 1558*0Sstevel@tonic-gate * We do this by checking if magic matches PRF_MAGIC. If it 1559*0Sstevel@tonic-gate * does, then this is a run-time linker profiled file, if it 1560*0Sstevel@tonic-gate * doesn't, it must be a gmon.out file. 1561*0Sstevel@tonic-gate */ 1562*0Sstevel@tonic-gate if (magic_num == (unsigned long)PRF_MAGIC) 1563*0Sstevel@tonic-gate rflag = TRUE; 1564*0Sstevel@tonic-gate else 1565*0Sstevel@tonic-gate rflag = FALSE; 1566*0Sstevel@tonic-gate 1567*0Sstevel@tonic-gate if (rflag) { 1568*0Sstevel@tonic-gate if (Bflag) { 1569*0Sstevel@tonic-gate L_hdr64 l_hdr64; 1570*0Sstevel@tonic-gate 1571*0Sstevel@tonic-gate /* 1572*0Sstevel@tonic-gate * If the rflag is set then the input file is 1573*0Sstevel@tonic-gate * rtld profiled data, we'll read it in and convert 1574*0Sstevel@tonic-gate * it to the standard format (ie: make it look like 1575*0Sstevel@tonic-gate * a gmon.out file). 1576*0Sstevel@tonic-gate */ 1577*0Sstevel@tonic-gate if (fread(&l_hdr64, sizeof (L_hdr64), 1, pfile) == 0) { 1578*0Sstevel@tonic-gate perror("fread()"); 1579*0Sstevel@tonic-gate exit(EX_IOERR); 1580*0Sstevel@tonic-gate } 1581*0Sstevel@tonic-gate if (l_hdr64.hd_version != PRF_VERSION_64) { 1582*0Sstevel@tonic-gate fprintf(stderr, "%s: expected version %d, " 1583*0Sstevel@tonic-gate "got version %d when processing 64-bit " 1584*0Sstevel@tonic-gate "run-time linker profiled file.\n", 1585*0Sstevel@tonic-gate whoami, PRF_VERSION_64, l_hdr64.hd_version); 1586*0Sstevel@tonic-gate exit(EX_SOFTWARE); 1587*0Sstevel@tonic-gate } 1588*0Sstevel@tonic-gate tmp.lowpc = 0; 1589*0Sstevel@tonic-gate tmp.highpc = (pctype)l_hdr64.hd_hpc; 1590*0Sstevel@tonic-gate tmp.ncnt = sizeof (M_hdr64) + l_hdr64.hd_psize; 1591*0Sstevel@tonic-gate } else { 1592*0Sstevel@tonic-gate L_hdr l_hdr; 1593*0Sstevel@tonic-gate 1594*0Sstevel@tonic-gate /* 1595*0Sstevel@tonic-gate * If the rflag is set then the input file is 1596*0Sstevel@tonic-gate * rtld profiled data, we'll read it in and convert 1597*0Sstevel@tonic-gate * it to the standard format (ie: make it look like 1598*0Sstevel@tonic-gate * a gmon.out file). 1599*0Sstevel@tonic-gate */ 1600*0Sstevel@tonic-gate if (fread(&l_hdr, sizeof (L_hdr), 1, pfile) == 0) { 1601*0Sstevel@tonic-gate perror("fread()"); 1602*0Sstevel@tonic-gate exit(EX_IOERR); 1603*0Sstevel@tonic-gate } 1604*0Sstevel@tonic-gate if (l_hdr.hd_version != PRF_VERSION) { 1605*0Sstevel@tonic-gate fprintf(stderr, "%s: expected version %d, " 1606*0Sstevel@tonic-gate "got version %d when processing " 1607*0Sstevel@tonic-gate "run-time linker profiled file.\n", 1608*0Sstevel@tonic-gate whoami, PRF_VERSION, l_hdr.hd_version); 1609*0Sstevel@tonic-gate exit(EX_SOFTWARE); 1610*0Sstevel@tonic-gate } 1611*0Sstevel@tonic-gate tmp.lowpc = 0; 1612*0Sstevel@tonic-gate tmp.highpc = (pctype)(uintptr_t)l_hdr.hd_hpc; 1613*0Sstevel@tonic-gate tmp.ncnt = sizeof (M_hdr) + l_hdr.hd_psize; 1614*0Sstevel@tonic-gate hdrsize = sizeof (M_hdr); 1615*0Sstevel@tonic-gate } 1616*0Sstevel@tonic-gate } else { 1617*0Sstevel@tonic-gate if (Bflag) { 1618*0Sstevel@tonic-gate if (fread(&tmp, sizeof (struct hdr), 1, pfile) == 0) { 1619*0Sstevel@tonic-gate perror("fread()"); 1620*0Sstevel@tonic-gate exit(EX_IOERR); 1621*0Sstevel@tonic-gate } 1622*0Sstevel@tonic-gate } else { 1623*0Sstevel@tonic-gate /* 1624*0Sstevel@tonic-gate * If we're not reading big %pc's, we need to read 1625*0Sstevel@tonic-gate * the 32-bit header, and assign the members to 1626*0Sstevel@tonic-gate * the actual header. 1627*0Sstevel@tonic-gate */ 1628*0Sstevel@tonic-gate struct hdr32 hdr32; 1629*0Sstevel@tonic-gate if (fread(&hdr32, sizeof (hdr32), 1, pfile) == 0) { 1630*0Sstevel@tonic-gate perror("fread()"); 1631*0Sstevel@tonic-gate exit(EX_IOERR); 1632*0Sstevel@tonic-gate } 1633*0Sstevel@tonic-gate tmp.lowpc = hdr32.lowpc; 1634*0Sstevel@tonic-gate tmp.highpc = hdr32.highpc; 1635*0Sstevel@tonic-gate tmp.ncnt = hdr32.ncnt; 1636*0Sstevel@tonic-gate hdrsize = sizeof (struct hdr32); 1637*0Sstevel@tonic-gate } 1638*0Sstevel@tonic-gate } 1639*0Sstevel@tonic-gate 1640*0Sstevel@tonic-gate /* 1641*0Sstevel@tonic-gate * perform sanity check on profiled file we've opened. 1642*0Sstevel@tonic-gate */ 1643*0Sstevel@tonic-gate if (tmp.lowpc >= tmp.highpc) { 1644*0Sstevel@tonic-gate if (rflag) 1645*0Sstevel@tonic-gate fprintf(stderr, "%s: badly formed profiled data.\n", 1646*0Sstevel@tonic-gate filename); 1647*0Sstevel@tonic-gate else 1648*0Sstevel@tonic-gate fprintf(stderr, "%s: badly formed gmon.out file.\n", 1649*0Sstevel@tonic-gate filename); 1650*0Sstevel@tonic-gate exit(EX_SOFTWARE); 1651*0Sstevel@tonic-gate } 1652*0Sstevel@tonic-gate 1653*0Sstevel@tonic-gate if (s_highpc != 0 && (tmp.lowpc != h.lowpc || 1654*0Sstevel@tonic-gate tmp.highpc != h.highpc || tmp.ncnt != h.ncnt)) { 1655*0Sstevel@tonic-gate fprintf(stderr, 1656*0Sstevel@tonic-gate "%s: incompatible with first gmon file\n", 1657*0Sstevel@tonic-gate filename); 1658*0Sstevel@tonic-gate exit(EX_IOERR); 1659*0Sstevel@tonic-gate } 1660*0Sstevel@tonic-gate h = tmp; 1661*0Sstevel@tonic-gate s_lowpc = h.lowpc; 1662*0Sstevel@tonic-gate s_highpc = h.highpc; 1663*0Sstevel@tonic-gate lowpc = h.lowpc / sizeof (UNIT); 1664*0Sstevel@tonic-gate highpc = h.highpc / sizeof (UNIT); 1665*0Sstevel@tonic-gate sampbytes = h.ncnt > hdrsize ? h.ncnt - hdrsize : 0; 1666*0Sstevel@tonic-gate nsamples = sampbytes / sizeof (unsigned_UNIT); 1667*0Sstevel@tonic-gate 1668*0Sstevel@tonic-gate #ifdef DEBUG 1669*0Sstevel@tonic-gate if (debug & SAMPLEDEBUG) { 1670*0Sstevel@tonic-gate printf("[openpfile] hdr.lowpc 0x%llx hdr.highpc " 1671*0Sstevel@tonic-gate "0x%llx hdr.ncnt %lld\n", 1672*0Sstevel@tonic-gate h.lowpc, h.highpc, h.ncnt); 1673*0Sstevel@tonic-gate printf("[openpfile] s_lowpc 0x%llx s_highpc 0x%llx\n", 1674*0Sstevel@tonic-gate s_lowpc, s_highpc); 1675*0Sstevel@tonic-gate printf("[openpfile] lowpc 0x%llx highpc 0x%llx\n", 1676*0Sstevel@tonic-gate lowpc, highpc); 1677*0Sstevel@tonic-gate printf("[openpfile] sampbytes %d nsamples %d\n", 1678*0Sstevel@tonic-gate sampbytes, nsamples); 1679*0Sstevel@tonic-gate } 1680*0Sstevel@tonic-gate #endif DEBUG 1681*0Sstevel@tonic-gate 1682*0Sstevel@tonic-gate return ((void *) pfile); 1683*0Sstevel@tonic-gate } 1684*0Sstevel@tonic-gate 1685*0Sstevel@tonic-gate /* 1686*0Sstevel@tonic-gate * Information from a gmon.out file depends on whether it's versioned 1687*0Sstevel@tonic-gate * or non-versioned, *old style* gmon.out. If old-style, it is in two 1688*0Sstevel@tonic-gate * parts : an array of sampling hits within pc ranges, and the arcs. If 1689*0Sstevel@tonic-gate * versioned, it contains a header, followed by any number of 1690*0Sstevel@tonic-gate * modules/callgraph/pcsample_buffer objects. 1691*0Sstevel@tonic-gate */ 1692*0Sstevel@tonic-gate static void 1693*0Sstevel@tonic-gate getpfile(char *filename) 1694*0Sstevel@tonic-gate { 1695*0Sstevel@tonic-gate void *handle; 1696*0Sstevel@tonic-gate size_t fsz; 1697*0Sstevel@tonic-gate 1698*0Sstevel@tonic-gate handle = openpfile(filename, &fsz); 1699*0Sstevel@tonic-gate 1700*0Sstevel@tonic-gate if (old_style) { 1701*0Sstevel@tonic-gate readsamples((FILE *) handle); 1702*0Sstevel@tonic-gate readarcs((FILE *) handle); 1703*0Sstevel@tonic-gate fclose((FILE *) handle); 1704*0Sstevel@tonic-gate return; 1705*0Sstevel@tonic-gate } 1706*0Sstevel@tonic-gate 1707*0Sstevel@tonic-gate getpfiledata((caddr_t) handle, fsz); 1708*0Sstevel@tonic-gate munmap(handle, fsz); 1709*0Sstevel@tonic-gate } 1710*0Sstevel@tonic-gate 1711*0Sstevel@tonic-gate main(int argc, char ** argv) 1712*0Sstevel@tonic-gate { 1713*0Sstevel@tonic-gate char **sp; 1714*0Sstevel@tonic-gate nltype **timesortnlp; 1715*0Sstevel@tonic-gate int c; 1716*0Sstevel@tonic-gate int errflg; 1717*0Sstevel@tonic-gate extern char *optarg; 1718*0Sstevel@tonic-gate extern int optind; 1719*0Sstevel@tonic-gate 1720*0Sstevel@tonic-gate prog_name = *argv; /* preserve program name */ 1721*0Sstevel@tonic-gate debug = 0; 1722*0Sstevel@tonic-gate nflag = FALSE; 1723*0Sstevel@tonic-gate bflag = TRUE; 1724*0Sstevel@tonic-gate lflag = FALSE; 1725*0Sstevel@tonic-gate Cflag = FALSE; 1726*0Sstevel@tonic-gate first_file = TRUE; 1727*0Sstevel@tonic-gate rflag = FALSE; 1728*0Sstevel@tonic-gate Bflag = FALSE; 1729*0Sstevel@tonic-gate errflg = FALSE; 1730*0Sstevel@tonic-gate 1731*0Sstevel@tonic-gate while ((c = getopt(argc, argv, "abd:CcDE:e:F:f:ln:sz")) != EOF) 1732*0Sstevel@tonic-gate switch (c) { 1733*0Sstevel@tonic-gate case 'a': 1734*0Sstevel@tonic-gate aflag = TRUE; 1735*0Sstevel@tonic-gate break; 1736*0Sstevel@tonic-gate case 'b': 1737*0Sstevel@tonic-gate bflag = FALSE; 1738*0Sstevel@tonic-gate break; 1739*0Sstevel@tonic-gate case 'c': 1740*0Sstevel@tonic-gate cflag = TRUE; 1741*0Sstevel@tonic-gate break; 1742*0Sstevel@tonic-gate case 'C': 1743*0Sstevel@tonic-gate Cflag = TRUE; 1744*0Sstevel@tonic-gate break; 1745*0Sstevel@tonic-gate case 'd': 1746*0Sstevel@tonic-gate dflag = TRUE; 1747*0Sstevel@tonic-gate debug |= atoi(optarg); 1748*0Sstevel@tonic-gate printf("[main] debug = 0x%x\n", debug); 1749*0Sstevel@tonic-gate break; 1750*0Sstevel@tonic-gate case 'D': 1751*0Sstevel@tonic-gate Dflag = TRUE; 1752*0Sstevel@tonic-gate break; 1753*0Sstevel@tonic-gate case 'E': 1754*0Sstevel@tonic-gate addlist(Elist, optarg); 1755*0Sstevel@tonic-gate Eflag = TRUE; 1756*0Sstevel@tonic-gate addlist(elist, optarg); 1757*0Sstevel@tonic-gate eflag = TRUE; 1758*0Sstevel@tonic-gate break; 1759*0Sstevel@tonic-gate case 'e': 1760*0Sstevel@tonic-gate addlist(elist, optarg); 1761*0Sstevel@tonic-gate eflag = TRUE; 1762*0Sstevel@tonic-gate break; 1763*0Sstevel@tonic-gate case 'F': 1764*0Sstevel@tonic-gate addlist(Flist, optarg); 1765*0Sstevel@tonic-gate Fflag = TRUE; 1766*0Sstevel@tonic-gate addlist(flist, optarg); 1767*0Sstevel@tonic-gate fflag = TRUE; 1768*0Sstevel@tonic-gate break; 1769*0Sstevel@tonic-gate case 'f': 1770*0Sstevel@tonic-gate addlist(flist, optarg); 1771*0Sstevel@tonic-gate fflag = TRUE; 1772*0Sstevel@tonic-gate break; 1773*0Sstevel@tonic-gate case 'l': 1774*0Sstevel@tonic-gate lflag = TRUE; 1775*0Sstevel@tonic-gate break; 1776*0Sstevel@tonic-gate case 'n': 1777*0Sstevel@tonic-gate nflag = TRUE; 1778*0Sstevel@tonic-gate number_funcs_toprint = atoi(optarg); 1779*0Sstevel@tonic-gate break; 1780*0Sstevel@tonic-gate case 's': 1781*0Sstevel@tonic-gate sflag = TRUE; 1782*0Sstevel@tonic-gate break; 1783*0Sstevel@tonic-gate case 'z': 1784*0Sstevel@tonic-gate zflag = TRUE; 1785*0Sstevel@tonic-gate break; 1786*0Sstevel@tonic-gate case '?': 1787*0Sstevel@tonic-gate errflg++; 1788*0Sstevel@tonic-gate 1789*0Sstevel@tonic-gate } 1790*0Sstevel@tonic-gate 1791*0Sstevel@tonic-gate if (errflg) { 1792*0Sstevel@tonic-gate (void) fprintf(stderr, 1793*0Sstevel@tonic-gate "usage: gprof [ -abcCDlsz ] [ -e function-name ] " 1794*0Sstevel@tonic-gate "[ -E function-name ]\n\t[ -f function-name ] " 1795*0Sstevel@tonic-gate "[ -F function-name ]\n\t[ image-file " 1796*0Sstevel@tonic-gate "[ profile-file ... ] ]\n"); 1797*0Sstevel@tonic-gate exit(EX_USAGE); 1798*0Sstevel@tonic-gate } 1799*0Sstevel@tonic-gate 1800*0Sstevel@tonic-gate if (optind < argc) { 1801*0Sstevel@tonic-gate a_outname = argv[optind++]; 1802*0Sstevel@tonic-gate } else { 1803*0Sstevel@tonic-gate a_outname = A_OUTNAME; 1804*0Sstevel@tonic-gate } 1805*0Sstevel@tonic-gate if (optind < argc) { 1806*0Sstevel@tonic-gate gmonname = argv[optind++]; 1807*0Sstevel@tonic-gate } else { 1808*0Sstevel@tonic-gate gmonname = GMONNAME; 1809*0Sstevel@tonic-gate } 1810*0Sstevel@tonic-gate /* 1811*0Sstevel@tonic-gate * turn off default functions 1812*0Sstevel@tonic-gate */ 1813*0Sstevel@tonic-gate for (sp = &defaultEs[0]; *sp; sp++) { 1814*0Sstevel@tonic-gate Eflag = TRUE; 1815*0Sstevel@tonic-gate addlist(Elist, *sp); 1816*0Sstevel@tonic-gate eflag = TRUE; 1817*0Sstevel@tonic-gate addlist(elist, *sp); 1818*0Sstevel@tonic-gate } 1819*0Sstevel@tonic-gate /* 1820*0Sstevel@tonic-gate * how many ticks per second? 1821*0Sstevel@tonic-gate * if we can't tell, report time in ticks. 1822*0Sstevel@tonic-gate */ 1823*0Sstevel@tonic-gate hz = sysconf(_SC_CLK_TCK); 1824*0Sstevel@tonic-gate if (hz == -1) { 1825*0Sstevel@tonic-gate hz = 1; 1826*0Sstevel@tonic-gate fprintf(stderr, "time is in ticks, not seconds\n"); 1827*0Sstevel@tonic-gate } 1828*0Sstevel@tonic-gate 1829*0Sstevel@tonic-gate getnfile(a_outname); 1830*0Sstevel@tonic-gate 1831*0Sstevel@tonic-gate /* 1832*0Sstevel@tonic-gate * get information about mon.out file(s). 1833*0Sstevel@tonic-gate */ 1834*0Sstevel@tonic-gate do { 1835*0Sstevel@tonic-gate getpfile(gmonname); 1836*0Sstevel@tonic-gate if (optind < argc) 1837*0Sstevel@tonic-gate gmonname = argv[optind++]; 1838*0Sstevel@tonic-gate else 1839*0Sstevel@tonic-gate optind++; 1840*0Sstevel@tonic-gate } while (optind <= argc); 1841*0Sstevel@tonic-gate /* 1842*0Sstevel@tonic-gate * dump out a gmon.sum file if requested 1843*0Sstevel@tonic-gate */ 1844*0Sstevel@tonic-gate if (sflag || Dflag) 1845*0Sstevel@tonic-gate dumpsum(GMONSUM); 1846*0Sstevel@tonic-gate 1847*0Sstevel@tonic-gate if (old_style) { 1848*0Sstevel@tonic-gate /* 1849*0Sstevel@tonic-gate * assign samples to procedures 1850*0Sstevel@tonic-gate */ 1851*0Sstevel@tonic-gate asgnsamples(); 1852*0Sstevel@tonic-gate } 1853*0Sstevel@tonic-gate 1854*0Sstevel@tonic-gate /* 1855*0Sstevel@tonic-gate * assemble the dynamic profile 1856*0Sstevel@tonic-gate */ 1857*0Sstevel@tonic-gate timesortnlp = doarcs(); 1858*0Sstevel@tonic-gate 1859*0Sstevel@tonic-gate /* 1860*0Sstevel@tonic-gate * print the dynamic profile 1861*0Sstevel@tonic-gate */ 1862*0Sstevel@tonic-gate #ifdef DEBUG 1863*0Sstevel@tonic-gate if (debug & ANYDEBUG) { 1864*0Sstevel@tonic-gate /* raw output of all symbols in all their glory */ 1865*0Sstevel@tonic-gate int i; 1866*0Sstevel@tonic-gate printf(" Name, pc_entry_pt, svalue, tix_in_routine, " 1867*0Sstevel@tonic-gate "#calls, selfcalls, index \n"); 1868*0Sstevel@tonic-gate for (i = 0; i < modules.nname; i++) { /* Print each symbol */ 1869*0Sstevel@tonic-gate if (timesortnlp[i]->name) 1870*0Sstevel@tonic-gate printf(" %s ", timesortnlp[i]->name); 1871*0Sstevel@tonic-gate else 1872*0Sstevel@tonic-gate printf(" <cycle> "); 1873*0Sstevel@tonic-gate printf(" %lld ", timesortnlp[i]->value); 1874*0Sstevel@tonic-gate printf(" %lld ", timesortnlp[i]->svalue); 1875*0Sstevel@tonic-gate printf(" %f ", timesortnlp[i]->time); 1876*0Sstevel@tonic-gate printf(" %lld ", timesortnlp[i]->ncall); 1877*0Sstevel@tonic-gate printf(" %lld ", timesortnlp[i]->selfcalls); 1878*0Sstevel@tonic-gate printf(" %d ", timesortnlp[i]->index); 1879*0Sstevel@tonic-gate printf(" \n"); 1880*0Sstevel@tonic-gate } 1881*0Sstevel@tonic-gate } 1882*0Sstevel@tonic-gate #endif DEBUG 1883*0Sstevel@tonic-gate 1884*0Sstevel@tonic-gate printgprof(timesortnlp); 1885*0Sstevel@tonic-gate /* 1886*0Sstevel@tonic-gate * print the flat profile 1887*0Sstevel@tonic-gate */ 1888*0Sstevel@tonic-gate printprof(); 1889*0Sstevel@tonic-gate /* 1890*0Sstevel@tonic-gate * print the index 1891*0Sstevel@tonic-gate */ 1892*0Sstevel@tonic-gate printindex(); 1893*0Sstevel@tonic-gate 1894*0Sstevel@tonic-gate /* 1895*0Sstevel@tonic-gate * print the modules 1896*0Sstevel@tonic-gate */ 1897*0Sstevel@tonic-gate printmodules(); 1898*0Sstevel@tonic-gate 1899*0Sstevel@tonic-gate done(); 1900*0Sstevel@tonic-gate /* NOTREACHED */ 1901*0Sstevel@tonic-gate return (0); 1902*0Sstevel@tonic-gate } 1903