/* * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ /* $OpenBSD: gprof.c,v 1.3 1996/10/02 02:59:49 tholo Exp $ */ /* $NetBSD: gprof.c,v 1.8 1995/04/19 07:15:59 cgd Exp $ */ /* * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include "stuff/errors.h" #include "gprof.h" #ifdef vax #include "vax.h" #endif /* * Progname for error messages. */ char *progname = NULL; /* * Ticks per second. */ long hz = 0; /* * Filename of the a.out file. */ char *a_outname = NULL; /* * Filename of the gmon.out file. */ char *gmonname = NULL; __private_extern__ nltype *nl = NULL; /* the whole namelist */ nltype *npe = NULL; /* the virtual end of the namelist */ unsigned long nname = 0;/* the number of function names */ /* * The list of file names and the ranges their pc's cover used for building * order files with the -S option. */ struct file *files = NULL; unsigned long n_files = 0; /* * namelist entries for cycle headers. * the number of discovered cycles. */ nltype *cyclenl = NULL; /* cycle header namelist */ int ncycle = 0; /* number of cycles discovered */ /* * The information for the pc sample sets from the gmon.out file. */ struct sample_set *sample_sets = NULL; unsigned long nsample_sets = 0; #ifdef __OPENSTEP__ /* * The rld loaded state from the gmon.out file. */ struct rld_loaded_state *grld_loaded_state = NULL; unsigned long grld_nloaded_states = 0; #endif /* * The dyld images from the gmon.out file. */ unsigned long image_count = 0; struct dyld_image *dyld_images = NULL; unsigned char *textspace = NULL;/* text space of a.out in core */ double totime = 0.0; /* total time for all routines */ double printtime = 0.0; /* total of time being printed */ double actime = 0.0; /* accumulated time thus far for putprofline */ /* * Option flags, from a to z. */ enum bool aflag = FALSE; /* suppress static functions */ enum bool bflag = FALSE; /* blurbs, too */ enum bool cflag = FALSE; /* discovered call graph, too */ enum bool dflag = FALSE; /* debugging options */ enum bool eflag = FALSE; /* specific functions excluded */ enum bool Eflag = FALSE; /* functions excluded with time */ enum bool fflag = FALSE; /* specific functions requested */ enum bool Fflag = FALSE; /* functions requested with time */ enum bool sflag = FALSE; /* sum multiple gmon.out files */ enum bool Sflag = FALSE; /* produce order file for scatter loading */ enum bool xflag = FALSE; /* don't produce gmon.order file */ enum bool zflag = FALSE; /* zero time/called functions, too */ /* * The debug value for debugging gprof. */ unsigned long debug = 0; /* * Things which get -E excluded by default. */ static char *defaultEs[] = { /* from the original gprof code */ "mcount", /* "__mcleanup", obsolete */ /* missing from gprof for the 4.3bsd implementaion */ "_monitor", "_monstartup", "_moncontrol", "_profil", /* added to the NeXT implementation for 3.0 */ "_moninit", "_monoutput", "_monreset", /* added to the NeXT implementation for 3.1 */ "_monaddition", "_moncount", "_add_profil", 0 }; /* * Static function declarations. */ static void getpfile( char *filename); #ifdef __OPENSTEP__ static void read_rld_state( int fd, char *filename, unsigned long nbytes); #endif static void read_dyld_state( int fd, char *filename, unsigned long nbytes); static unsigned long new_sample_set( int fd, char *filename, unsigned long nbytes, enum bool old_style); static void readarcs( int fd, char *filename, unsigned long nbytes); static void readarcs_orders( int fd, char *filename, unsigned long nbytes); static void tally( unsigned long frompc, unsigned long selfpc, unsigned long count, unsigned long order); static void dumpsum( char *sumfile); static void asgnsamples( struct sample_set *s); static unsigned long min( unsigned long a, unsigned long b); static unsigned long max( unsigned long a, unsigned long b); static void alignentries( void); int main( int argc, char **argv, char **envp) { unsigned long i; char **sp; nltype **timesortnlp; progname = argv[0]; --argc; argv++; debug = 0; bflag = TRUE; while(*argv != 0 && **argv == '-'){ (*argv)++; switch(**argv){ case 'a': aflag = TRUE; break; case 'b': bflag = FALSE; break; case 'c': printf("%s: -c not supported\n", progname); /* cflag = TRUE; */ break; case 'd': dflag = TRUE; (*argv)++; debug |= atoi(*argv); debug |= ANYDEBUG; #ifdef DEBUG printf("[main] debug = %lu\n", debug); #else printf("%s: -d ignored\n", progname); #endif break; case 'E': ++argv; addlist(Elist, *argv); Eflag = TRUE; addlist(elist, *argv); eflag = TRUE; break; case 'e': addlist(elist, *++argv); eflag = TRUE; break; case 'F': ++argv; addlist(Flist, *argv); Fflag = TRUE; addlist(flist, *argv); fflag = TRUE; break; case 'f': addlist(flist, *++argv); fflag = TRUE; break; case 'S': Sflag = TRUE; break; case 's': sflag = TRUE; break; case 'x': xflag = TRUE; break; case 'z': zflag = TRUE; break; } argv++; } if(*argv != 0){ a_outname = *argv; argv++; } else{ a_outname = A_OUTNAME; } if(*argv != 0){ gmonname = *argv; argv++; } else{ gmonname = GMONNAME; } /* * Turn off default functions. */ for(sp = &defaultEs[0]; *sp; sp++){ Eflag = TRUE; addlist(Elist, *sp); eflag = TRUE; addlist(elist, *sp); } /* * Get information about a.out file. */ getnfile(); /* * Get information about mon.out file(s). */ hz = 0; do{ getpfile(gmonname); if(*argv != 0){ gmonname = *argv; } }while(*argv++ != 0); /* * How many ticks per second? If we can't tell, report time in ticks. * If this was picked up from the mon.out files use that value. */ #ifndef __OPENSTEP__ if(hz == 0) #endif hz = hertz(); if(hz == 0){ hz = 1; fprintf(stderr, "time is in ticks, not seconds\n"); } /* * Dump out a gmon.sum file if requested. */ if(sflag){ dumpsum(GMONSUM); exit(0); } /* * Assign samples to procedures. */ alignentries(); for(i = 0; i < nsample_sets; i++) asgnsamples(sample_sets + i); /* * Assemble the dynamic profile. */ timesortnlp = doarcs(); /* * Print the dynamic profile. */ printgprof(timesortnlp); /* * Print the flat profile. */ printprof(); /* * Print the index. */ printindex(); /* * Dump out the order files if requested. */ if(Sflag) printscatter(); return(0); } static void getpfile( char *filename) { unsigned long magic, left; struct gmon_data data; int fd; struct stat stat; if((fd = open(filename, O_RDONLY)) == -1) system_fatal("can't open: %s", filename); if(fstat(fd, &stat) == -1) system_fatal("can't stat: %s", filename); /* * See if this gmon.out file is an old format or new format by looking * for the magic number of the new format. */ if(read(fd, &magic, sizeof(unsigned long)) != sizeof(unsigned long)) system_fatal("malformed gmon.out file: %s (can't read magic " "number)", filename); if(magic == GMON_MAGIC){ /* * This is a new format gmon.out file. After the magic number comes * any number of pairs of gmon_data structs and some typed data. */ left = stat.st_size - sizeof(unsigned long); while(left >= sizeof(struct gmon_data)){ if(read(fd, &data, sizeof(struct gmon_data)) != sizeof(struct gmon_data)) system_fatal("malformed gmon.out file: %s (can't read " "gmon_data struct)", filename); left -= sizeof(struct gmon_data); if(left < data.size) fatal("truncated or malformed gmon.out file: %s (value in " "size field in gmon_data struct more than left in " "file)", filename); switch(data.type){ case GMONTYPE_SAMPLES: /* printf("GMONTYPE_SAMPLES\n"); */ new_sample_set(fd, filename, data.size, FALSE); break; case GMONTYPE_RAWARCS: /* printf("GMONTYPE_RAWARCS\n"); */ readarcs(fd, filename, data.size); break; case GMONTYPE_ARCS_ORDERS: /* printf("GMONTYPE_ARCS_ORDERS\n"); */ readarcs_orders(fd, filename, data.size); break; #ifdef __OPENSTEP__ case GMONTYPE_RLD_STATE: /* printf("GMONTYPE_RLD_STATE\n"); */ if(grld_nloaded_states == 0){ read_rld_state(fd, filename, data.size); get_rld_state_symbols(); } else{ warning("can't process more than one rld loaded state " "(ignoring rld state from: %s)", filename); lseek(fd, data.size, L_INCR); } break; #endif case GMONTYPE_DYLD_STATE: /* printf("GMONTYPE_DYLD_STATE\n"); */ if(image_count == 0){ read_dyld_state(fd, filename, data.size); get_dyld_state_symbols(); } else{ warning("can't process more than one dyld state " "(ignoring dyld state from: %s)", filename); lseek(fd, data.size, L_INCR); } break; default: /* printf("Unknown data.type = %d\n", data.type); */ fatal("truncated or malformed gmon.out file: %s (value in " "type field in gmon_data struct unknown)", filename); } left -= data.size; } if(left != 0) fatal("truncated or malformed gmon.out file: %s (file end in " "the middle of a gmon_data struct %lu)", filename, left); } else{ /* * This is an old format gmon.out file. It has a profile header * then * an array of sampling hits within pc ranges, and then arcs. */ lseek(fd, 0, L_SET); left = stat.st_size; left -= new_sample_set(fd, filename, left, TRUE); readarcs(fd, filename, left); } close(fd); } #ifdef __OPENSTEP__ static void read_rld_state( int fd, char *filename, unsigned long nbytes) { unsigned long i, j, size, size_read, str_size; char *strings; size_read = 0; size = sizeof(unsigned long); if(read(fd, &grld_nloaded_states, size) != size) system_fatal("malformed gmon.out file: %s (can't read number of " "rld states)", filename); size_read += size; grld_loaded_state = (struct rld_loaded_state *) malloc(grld_nloaded_states * sizeof(struct rld_loaded_state)); if(grld_loaded_state == NULL) fatal("no room for rld states (malloc failed)"); for(i = 0; i < grld_nloaded_states; i++){ size = sizeof(unsigned long); if(read(fd, &(grld_loaded_state[i].header_addr), size) != size) system_fatal("malformed gmon.out file: %s (can't read header " "address of rld state %lu)", filename, i); size_read += size; size = sizeof(unsigned long); if(read(fd, &(grld_loaded_state[i].nobject_filenames), size) != size) system_fatal("malformed gmon.out file: %s (can't read number " "of object file names of rld state %lu)", filename, i); size_read += size; grld_loaded_state[i].object_filenames = (char **) malloc((grld_loaded_state[i].nobject_filenames + 1) * sizeof(char *)); if(grld_loaded_state[i].object_filenames == NULL) fatal("no room for rld state %lu object file names (malloc " "failed)", i); size = grld_loaded_state[i].nobject_filenames * sizeof(char *); if(read(fd, grld_loaded_state[i].object_filenames, size) != size) system_fatal("malformed gmon.out file: %s (can't read offsets " "to file names of rld state %lu)", filename, i); size_read += size; size = sizeof(unsigned long); if(read(fd, &str_size, size) != size) system_fatal("malformed gmon.out file: %s (can't read string " "size of rld state %lu)", filename, i); size_read += size; strings = (char *)malloc(str_size); if(strings == NULL) fatal("no room for rld state %lu object file names (malloc " "failed)", i); size = str_size; if(read(fd, strings, size) != size) system_fatal("malformed gmon.out file: %s (can't read strings " "of file names of rld state %lu)", filename, i); size_read += size; for(j = 0; j < grld_loaded_state[i].nobject_filenames; j++){ if((unsigned long)(grld_loaded_state[i].object_filenames[j]) > str_size) fatal("malformed gmon.out file: %s (bad offset to object " "filename %lu of rld state %lu)", filename, j, i); grld_loaded_state[i].object_filenames[j] = strings + (unsigned long)(grld_loaded_state[i].object_filenames[j]); } grld_loaded_state[i].object_filenames[j] = NULL; } #ifdef DEBUG if(debug & RLDDEBUG){ printf("grld_nloaded_states = %lu\n", grld_nloaded_states); printf("grld_loaded_state 0x%x\n", (unsigned int)grld_loaded_state); for(i = 0; i < grld_nloaded_states; i++){ printf("state %lu\n\tnobject_filenames %lu\n\tobject_filenames " "0x%x\n\theader_addr 0x%x\n", i, grld_loaded_state[i].nobject_filenames, (unsigned int)(grld_loaded_state[i].object_filenames), (unsigned int)(grld_loaded_state[i].header_addr)); for(j = 0; j < grld_loaded_state[i].nobject_filenames; j++) printf("\t\t%s\n", grld_loaded_state[i].object_filenames[j]); } } #endif } #endif static void read_dyld_state( int fd, char *filename, unsigned long nbytes) { unsigned long i, offset; char *buf; buf = malloc(nbytes); if(buf == NULL) fatal("no room for dyld state (malloc failed)"); if(read(fd, buf, nbytes) != (int)nbytes) system_fatal("malformed gmon.out file: %s (can't read dyld state)", filename); offset = 0; if(offset + sizeof(unsigned long) > nbytes) fatal("truncated or malformed gmon.out file: %s (image count " "extends past the end of the dyld state)", filename); memcpy(&image_count, buf + offset, sizeof(unsigned long)); offset += sizeof(unsigned long); dyld_images = (struct dyld_image *)malloc(sizeof(struct dyld_image) * image_count); if(dyld_images == NULL) fatal("no room for dyld images (malloc failed)"); for(i = 0; i < image_count; i++){ if(offset + sizeof(unsigned long) > nbytes) fatal("truncated or malformed gmon.out file: %s (vmaddr_slide " "for image %lu extends past the end of the dyld state)", filename, i); memcpy(&(dyld_images[i].vmaddr_slide), buf + offset, sizeof(unsigned long)); offset += sizeof(unsigned long); dyld_images[i].name = buf + offset; while(buf[offset] != '\0' && offset < nbytes) offset++; if(buf[offset] == '\0' && offset < nbytes) offset++; if(offset > nbytes) fatal("truncated or malformed gmon.out file: %s (name " "for image %lu extends past the end of the dyld state)", filename, i); } #ifdef DEBUG if(debug & DYLDDEBUG){ printf("image_count = %lu\n", image_count); for(i = 0; i < image_count; i++){ printf("dyld_image[%lu].name = %s\n", i, dyld_images[i].name); printf("dyld_image[%lu].vmaddr_slide = 0x%x\n", i, (unsigned int)dyld_images[i].vmaddr_slide); } } #endif } static unsigned long new_sample_set( int fd, char *filename, unsigned long nbytes, enum bool old_style) { #ifdef __OPENSTEP__ struct phdr header; #else struct gmonhdr header; #endif unsigned long i, j, size; unsigned UNIT sample, *samples; if(nbytes < sizeof(header)) fatal("gmon.out file: %s malformed (byte count less than the " "size of the expected header)", filename); /* * Read the profile header and check to see if it is valid. */ if(read(fd, &header, sizeof(header)) != sizeof(header)) system_fatal("malformed gmon.out file: %s (can't read header)", filename); /* * The ncnt field is a byte count of the header and the number of * bytes of the 2byte samples that follow it. Try to make sure this * is reasonable and that what's left in the file is at least that big. */ if((size_t)header.ncnt < sizeof(header)) fatal("gmon.out file: %s malformed (ncnt field less than the " "size of the expected header)", filename); if((unsigned long)header.ncnt > nbytes) fatal("gmon.out file: %s malformed (ncnt field greater than " "the byte count for it in the file)", filename); if(header.hpc < header.lpc) fatal("gmon.out file: %s malformed (high pc less than low pc " "in header)", filename); if(((header.ncnt - sizeof(header)) % sizeof(unsigned short)) != 0) warning("gmon.out file: %s malformed (number of sample bytes " "not a multiple of sizeof(unsigned short))", filename); #ifndef __OPENSTEP__ if(header.version != GMONVERSION) warning("gmon.out file: %s unknown version (0x%x)", filename, header.version); if(hz == 0){ if(header.profrate == 0) warning("gmon.out file: %s profrate is zero", filename); else hz = header.profrate; } else{ if(hz != header.profrate) warning("gmon.out file: %s profrate (%d) does not prevous " "profrate (%ld)\n", filename, header.profrate, hz); } #endif /* * See if this sample range matches another range */ for(i = 0; i < nsample_sets; i++){ if(sample_sets[i].s_lowpc == (unsigned long)header.lpc && sample_sets[i].s_highpc == (unsigned long)header.hpc && sample_sets[i].sampbytes == header.ncnt - sizeof(header)) break; } /* * For old_style gmon.out files with one only pc sample range it must * match up if there are any ranges. */ if(old_style == TRUE && sample_sets != NULL && i != nsample_sets) fatal("gmon.out file: %s incompatible with first gmon.out file", filename); /* * If this range does not match up with a previous range then create * a new sample set for it. */ if(i == nsample_sets){ sample_sets = realloc(sample_sets, (nsample_sets + 1) * sizeof(struct sample_set)); if(sample_sets == NULL) fatal("no room for next pc sample set (malloc failed)"); i = nsample_sets; nsample_sets++; memset(sample_sets + i, '\0', sizeof(struct sample_set)); sample_sets[i].s_lowpc = (unsigned long)header.lpc; sample_sets[i].s_highpc = (unsigned long)header.hpc; sample_sets[i].lowpc = (unsigned long)header.lpc / sizeof(UNIT); sample_sets[i].highpc = (unsigned long)header.hpc / sizeof(UNIT); sample_sets[i].sampbytes = header.ncnt - sizeof(header); sample_sets[i].nsamples = sample_sets[i].sampbytes / sizeof(unsigned UNIT); sample_sets[i].scale = ((double)(sample_sets[i].highpc) - (double)(sample_sets[i].lowpc)) / (double)(sample_sets[i].nsamples); sample_sets[i].samples = calloc(sample_sets[i].sampbytes, sizeof(unsigned UNIT)); if(sample_sets[i].samples == NULL) fatal("no room for %lu sample pc's\n", sample_sets[i].sampbytes / sizeof(unsigned UNIT)); #ifndef __OPENSTEP__ sample_sets[i].version = header.version; sample_sets[i].profrate = header.profrate; sample_sets[i].spare[0] = header.spare[0]; sample_sets[i].spare[1] = header.spare[1]; sample_sets[i].spare[2] = header.spare[2]; #endif } #ifdef DEBUG if(debug & SAMPLEDEBUG){ printf("[new_sample_set] header.lpc 0x%x header.hpc 0x%x " "header.ncnt %d\n", (unsigned int)header.lpc, (unsigned int)header.hpc, header.ncnt); printf("[new_sample_set] s_lowpc 0x%x s_highpc 0x%x\n", (unsigned int)sample_sets[i].s_lowpc, (unsigned int)sample_sets[i].s_highpc); printf("[new_sample_set] lowpc 0x%x highpc 0x%x\n" , (unsigned int)sample_sets[i].lowpc, (unsigned int)sample_sets[i].highpc); printf("[new_sample_set] sampbytes %u nsamples %lu\n" , sample_sets[i].sampbytes, sample_sets[i].nsamples); #ifndef __OPENSTEP__ printf("[new_sample_set] version 0x%x\n", (unsigned int)(sample_sets[i].version)); printf("[new_sample_set] profrate %d\n" ,sample_sets[i].profrate); printf("[new_sample_set] spare[0] %d\n" ,sample_sets[i].spare[0]); printf("[new_sample_set] spare[1] %d\n" ,sample_sets[i].spare[1]); printf("[new_sample_set] spare[2] %d\n" ,sample_sets[i].spare[2]); #endif } #endif /* * Read and sum up the sample counts for this sample set. */ size = sample_sets[i].nsamples * sizeof(unsigned UNIT); samples = malloc(size); if(samples == NULL) system_fatal("can't allocate buffer of size: %lu for samples from " "gmon.out file: %s", size, filename); if(read(fd, samples, size) != (int)size) system_fatal("can't read samples from gmon.out file: %s", filename); for(j = 0; j < sample_sets[i].nsamples; j++){ sample = samples[j]; #ifdef DEBUG if(debug & 8192){ if(sample != 0) printf("sample[%ld] = %u\n", j, sample); } #endif sample_sets[i].samples[j] += sample; } free(samples); return(header.ncnt); } static void readarcs( int fd, char *filename, unsigned long nbytes) { unsigned long i; struct rawarc arc; /* * Arcs consists of a bunch of tuples. */ for(i = 0; i < nbytes; i += sizeof(struct rawarc)){ if(read(fd, &arc, sizeof(struct rawarc)) != sizeof(struct rawarc)) system_fatal("malformed gmon.out file: %s (can't read arcs)", filename); #ifdef DEBUG if(debug & SAMPLEDEBUG){ printf("[readarcs] frompc 0x%x selfpc 0x%x count %lu\n" , (unsigned int)arc.raw_frompc, (unsigned int)arc.raw_selfpc, arc.raw_count); } #endif /* * Add this arc. */ tally(arc.raw_frompc, arc.raw_selfpc, arc.raw_count, 0); } } static void readarcs_orders( int fd, char *filename, unsigned long nbytes) { unsigned long i; struct rawarc_order arc_order; /* * Arcs consists of a bunch of tuples. */ for(i = 0; i < nbytes; i += sizeof(struct rawarc_order)){ if(read(fd, &arc_order, sizeof(struct rawarc_order)) != sizeof(struct rawarc_order)) system_fatal("malformed gmon.out file: %s (can't read arcs)", filename); #ifdef DEBUG if(debug & SAMPLEDEBUG){ printf("[readarcs_order] frompc 0x%x selfpc 0x%x count %lu " "order %lu\n" , (unsigned int)arc_order.raw_frompc, (unsigned int)arc_order.raw_selfpc, arc_order.raw_count, arc_order.raw_order); } #endif /* * Add this arc. */ tally(arc_order.raw_frompc, arc_order.raw_selfpc, arc_order.raw_count, arc_order.raw_order); } } static void tally( unsigned long frompc, unsigned long selfpc, unsigned long count, unsigned long order) { unsigned long i; nltype *parentp; nltype *childp; parentp = nllookup(frompc); childp = nllookup(selfpc); if(childp == NULL || parentp == NULL){ fprintf(stderr, "bad arc from 0x%x to 0x%x, count %lu (ignored).\n", (unsigned int)frompc, (unsigned int)selfpc, count); return; } /* * See if this arc comes from inside a shlib's text. */ for(i = 0; i < nshlib_text_ranges; i++){ if(frompc >= shlib_text_ranges[i].lowpc && frompc < shlib_text_ranges[i].highpc) break; } if(parentp->name[0] != '+' && parentp->name[0] != '-' && nshlib_text_ranges != 0 && i != nshlib_text_ranges){ #ifdef DEBUG if(debug & SAMPLEDEBUG){ printf("[tally] tossing arc frompc 0x%x selfpc 0x%x " "count %lu order %lu\n", (unsigned int)frompc, (unsigned int)selfpc, count, order); } #endif return; } childp->ncall += count; /* * Set the call order to the lowest order in the arcs for a call this * child. Orders in the arcs are start at 1 and thus orders with the * value of zero are unordered. */ if(order != 0){ if(childp->order == 0) childp->order = order; else if(order < childp->order) childp->order = order; } #ifdef DEBUG if(debug & TALLYDEBUG){ printf("[tally] arc from %s to %s traversed %lu times\n", parentp->name, childp->name, count); } #endif /* DEBUG */ addarc(parentp, childp, count, order); } /* * Dump out the gmon.sum file. */ static void dumpsum( char *sumfile) { unsigned long i, j, magic; #ifdef __OPENSTEP__ unsigned long strsize; struct phdr header; #else struct gmonhdr header; #endif int fd; struct gmon_data data; nltype *nlp; arctype *arcp; struct rawarc_order arc_order; if((fd = open(sumfile, O_WRONLY | O_CREAT | O_TRUNC, 0666)) == -1) fatal("can't create gmon.sum file: %s", sumfile); /* * Write the magic number. */ magic = GMON_MAGIC; if(write(fd, &magic, sizeof(unsigned long)) != sizeof(unsigned long)) fatal("can't write magic number to gmon.sum file: %s", sumfile); #ifdef __OPENSTEP__ /* * Write the rld_state if any. */ if(grld_nloaded_states != 0){ data.type = GMONTYPE_RLD_STATE; data.size = sizeof(unsigned long) + sizeof(unsigned long) * 3 * grld_nloaded_states; for(i = 0; i < grld_nloaded_states; i++){ data.size += sizeof(unsigned long) * grld_loaded_state[i].nobject_filenames; for(j = 0; j < grld_loaded_state[i].nobject_filenames; j++) data.size += strlen(grld_loaded_state[i].object_filenames[j]) + 1; } if(write(fd, &data, sizeof(struct gmon_data)) != sizeof(struct gmon_data)) fatal("can't write gmon_data struct to gmon.sum file: %s", sumfile); if(write(fd, &grld_nloaded_states, sizeof(unsigned long)) != sizeof(unsigned long)) fatal("can't write to gmon.sum file: %s", sumfile); for(i = 0; i < grld_nloaded_states; i++){ if(write(fd, &(grld_loaded_state[i].header_addr), sizeof(unsigned long)) != sizeof(unsigned long)) fatal("can't write to gmon.sum file: %s", sumfile); if(write(fd, &(grld_loaded_state[i].nobject_filenames), sizeof(unsigned long)) != sizeof(unsigned long)) fatal("can't write to gmon.sum file: %s", sumfile); strsize = 0; for(j = 0; j < grld_loaded_state[i].nobject_filenames; j++){ if(write(fd, &strsize, sizeof(unsigned long)) != sizeof(unsigned long)) fatal("can't write to gmon.sum file: %s", sumfile); strsize += strlen(grld_loaded_state[i].object_filenames[j]) + 1; } if(write(fd, &strsize, sizeof(unsigned long)) != sizeof(unsigned long)) fatal("can't write to gmon.sum file: %s", sumfile); for(j = 0; j < grld_loaded_state[i].nobject_filenames; j++){ strsize = strlen(grld_loaded_state[i].object_filenames[j]) + 1; if(write(fd, grld_loaded_state[i].object_filenames[j], strsize) != strsize) fatal("can't write to gmon.sum file: %s", sumfile); } } } #endif /* * Write the samples. */ for(i = 0; i < nsample_sets; i++){ data.type = GMONTYPE_SAMPLES; data.size = sample_sets[i].sampbytes + sizeof(header); if(write(fd, &data, sizeof(struct gmon_data)) != sizeof(struct gmon_data)) fatal("can't write gmon_data struct to gmon.sum file: %s", sumfile); memset(&header, '\0', sizeof(header)); #ifdef __OPENSTEP__ header.lpc = (char *)sample_sets[i].s_lowpc; header.hpc = (char *)sample_sets[i].s_highpc; #else header.lpc = sample_sets[i].s_lowpc; header.hpc = sample_sets[i].s_highpc; header.version = sample_sets[i].version; header.profrate = sample_sets[i].profrate; header.spare[0] = sample_sets[i].spare[0]; header.spare[1] = sample_sets[i].spare[1]; header.spare[2] = sample_sets[i].spare[2]; #endif header.ncnt = sample_sets[i].sampbytes + sizeof(header); if(write(fd, &header, sizeof(header)) != sizeof(header)) fatal("can't write header to gmon.sum file: %s", sumfile); for(j = 0; j < sample_sets[i].nsamples; j++){ if(write(fd, &(sample_sets[i].samples[j]), sizeof(unsigned UNIT)) != sizeof(unsigned UNIT)) system_fatal("can't write samples to gmon.sum file: %s", sumfile); } } /* * Write the arc information */ data.type = GMONTYPE_ARCS_ORDERS; data.size = 0; for(nlp = nl; nlp < npe; nlp++){ for(arcp = nlp->children; arcp; arcp = arcp->arc_childlist){ data.size += sizeof(struct rawarc_order); } } if(write(fd, &data, sizeof(struct gmon_data)) != sizeof(struct gmon_data)) fatal("can't write gmon_data struct to gmon.sum file: %s", sumfile); for(nlp = nl; nlp < npe; nlp++){ for(arcp = nlp->children; arcp; arcp = arcp->arc_childlist){ arc_order.raw_frompc = arcp->arc_parentp->value; arc_order.raw_selfpc = arcp->arc_childp->value; arc_order.raw_count = arcp->arc_count; arc_order.raw_order = arcp->arc_order; if(write(fd, &arc_order, sizeof(struct rawarc_order)) != sizeof(struct rawarc_order)) fatal("can't write arc to gmon.sum file: %s", sumfile); #ifdef DEBUG if(debug & SAMPLEDEBUG){ printf("[dumpsum] frompc 0x%x selfpc 0x%x count %lu " "order %lu\n" , (unsigned int)arc_order.raw_frompc, (unsigned int)arc_order.raw_selfpc, arc_order.raw_count, arc_order.raw_order); } #endif } } close(fd); } /* * Assign samples to the procedures to which they belong. * * There are three cases as to where pcl and pch can be * with respect to the routine entry addresses svalue0 and svalue1 * as shown in the following diagram. overlap computes the * distance between the arrows, the fraction of the sample * that is to be credited to the routine which starts at svalue0. * * svalue0 svalue1 * | | * v v * * +-----------------------------------------------+ * | | * | ->| |<- ->| |<- ->| |<- | * | | | | | | * +---------+ +---------+ +---------+ * * ^ ^ ^ ^ ^ ^ * | | | | | | * pcl pch pcl pch pcl pch * * For the vax we assert that samples will never fall in the first * two bytes of any routine, since that is the entry mask, * thus we give call alignentries() to adjust the entry points if * the entry mask falls in one bucket but the code for the routine * doesn't start until the next bucket. In conjunction with the * alignment of routine addresses, this should allow us to have * only one sample for every four bytes of text space and never * have any overlap (the two end cases, above). */ static void asgnsamples( struct sample_set *s) { unsigned long i, j; unsigned UNIT ccnt; double time; unsigned long pcl, pch, overlap, svalue0, svalue1; /* read samples and assign to namelist symbols */ for(i = 0, j = 1; i < s->nsamples; i++){ ccnt = s->samples[i]; if (ccnt == 0) continue; pcl = s->lowpc + s->scale * i; pch = s->lowpc + s->scale * (i + 1); time = ccnt; #ifdef DEBUG if(debug & SAMPLEDEBUG){ printf("[asgnsamples] pcl 0x%x pch 0x%x ccnt %d\n", (unsigned int)pcl, (unsigned int)pch, ccnt); } #endif totime += time; for(j = j - 1; j < nname; j++){ svalue0 = nl[j].svalue; svalue1 = nl[j+1].svalue; /* * if high end of tick is below entry address, * go for next tick. */ if(pch < svalue0) break; /* * if low end of tick into next routine, * go for next routine. */ if(pcl >= svalue1) continue; overlap = min(pch, svalue1) - max(pcl, svalue0); if(overlap > 0){ #ifdef DEBUG if (debug & SAMPLEDEBUG) { printf("[asgnsamples] (0x%x->0x%x-0x%x) %s gets %f " "ticks %lu overlap\n", (unsigned int)(nl[j].value/sizeof(UNIT)), (unsigned int)svalue0, (unsigned int)svalue1, nl[j].name, overlap * time / s->scale, overlap); } #endif nl[j].time += overlap * time / s->scale; } } } #ifdef DEBUG if(debug & SAMPLEDEBUG){ printf("[asgnsamples] totime %f\n", totime); } #endif } static unsigned long min( unsigned long a, unsigned long b) { if(a < b) return(a); return(b); } static unsigned long max( unsigned long a, unsigned long b) { if(a > b) return(a); return(b); } /* * Calculate scaled entry point addresses (to save time in asgnsamples), * and possibly push the scaled entry points over the entry mask (vax), * if it turns out that the entry point is in one bucket and the code * for a routine is in the next bucket. */ static void alignentries( void) { struct nl *nlp; #ifdef vax unsigned long i; unsigned long bucket_of_entry; unsigned long bucket_of_code; #endif /* vax */ for(nlp = nl; nlp < npe; nlp++){ nlp->svalue = nlp->value / sizeof(UNIT); #ifdef vax for(i = 0; i < nsample_sets; i++){ if(nlp->svalue >= sample_sets[i].s_lowpc && nlp->svalue < sample_sets[i].s_lowpc) break; } if(i == nsample_sets) continue; bucket_of_entry = (nlp->svalue - sample_sets[i].lowpc) / sample_sets[i].scale; bucket_of_code = (nlp->svalue + UNITS_TO_CODE - sample_sets[i].lowpc) / sample_sets[i].scale; if(bucket_of_entry < bucket_of_code) { #ifdef DEBUG if(debug & SAMPLEDEBUG){ printf("[alignentries] pushing svalue 0x%x to 0x%x\n", (unsigned int)nlp->svalue, (unsigned int)(nlp->svalue + UNITS_TO_CODE)); } #endif /* DEBUG */ nlp->svalue += UNITS_TO_CODE; } #endif /* vax */ } }