diff options
author | Ken Raeburn <raeburn@cygnus> | 1995-02-07 22:34:18 +0000 |
---|---|---|
committer | Ken Raeburn <raeburn@cygnus> | 1995-02-07 22:34:18 +0000 |
commit | 5489fcc3d9dbb1f529dddb19b615e23d8ed59dc7 (patch) | |
tree | 1a79cfffd92f2afb67780dd7372c1759c49791aa /gprof/gprof.c | |
parent | 2559e01429d193b7957c106a6a8b0598476f9845 (diff) | |
download | gdb-5489fcc3d9dbb1f529dddb19b615e23d8ed59dc7.zip gdb-5489fcc3d9dbb1f529dddb19b615e23d8ed59dc7.tar.gz gdb-5489fcc3d9dbb1f529dddb19b615e23d8ed59dc7.tar.bz2 |
Lots of changes from David Mosberger-Tang; see ChangeLog and NOTES for details:
Alpha support.
Long options.
New file format to support more information; backwards compatibility.
Line-level profiling, on systems where bfd_find_nearest_line works.
Selective display of data.
Diffstat (limited to 'gprof/gprof.c')
-rw-r--r-- | gprof/gprof.c | 1130 |
1 files changed, 407 insertions, 723 deletions
diff --git a/gprof/gprof.c b/gprof/gprof.c index 15eb7fd..eb0e1c5 100644 --- a/gprof/gprof.c +++ b/gprof/gprof.c @@ -16,776 +16,460 @@ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ +#include "getopt.h" +#include "libiberty.h" +#include "gprof.h" +#include "basic_blocks.h" +#include "call_graph.h" +#include "cg_arcs.h" +#include "cg_print.h" +#include "core.h" +#include "gmon_io.h" +#include "hertz.h" +#include "hist.h" +#include "source.h" +#include "sym_ids.h" + +#define VERSION "2.6" + +const char *whoami; +const char *a_out_name = A_OUTNAME; +long hz = HZ_WRONG; -#define VERSION "5.6 (Cygnus)" +/* + * Default options values: + */ +int debug_level = 0; +int output_style = 0; +int output_width = 80; +bool bsd_style_output = FALSE; +bool discard_underscores = TRUE; +bool ignore_direct_calls = FALSE; +bool ignore_static_funcs = FALSE; +bool ignore_zeros = TRUE; +bool line_granularity = FALSE; +bool print_descriptions = TRUE; +bool print_path = FALSE; +File_Format file_format = FF_AUTO; + +bool first_output = TRUE; -#ifndef lint char copyright[] = "@(#) Copyright (c) 1983 Regents of the University of California.\n\ All rights reserved.\n"; -#endif /* not lint */ - -#ifndef lint -static char sccsid[] = "@(#)gprof.c 5.6 (Berkeley) 6/1/90"; -#endif /* not lint */ -#include "gprof.h" - -#ifndef FOPEN_RB -#define FOPEN_RB "r" -#endif +static char *gmon_name = GMONNAME; /* profile filename */ bfd *abfd; -char *whoami; +/* + * Functions that get excluded by default: + */ +static char *default_excluded_list[] = { + "_gprof_mcount", "mcount", "_mcount", "__mcleanup", + "<locore>", "<hicore>", + 0 +}; +static struct option long_options[] = +{ + {"line", no_argument, 0, 'l'}, + {"no-static", no_argument, 0, 'a'}, + + /* output styles: */ + + {"annotated-source", optional_argument, 0, 'A'}, + {"no-annotated-source", optional_argument, 0, 'J'}, + {"flat-profile", optional_argument, 0, 'p'}, + {"no-flat-profile", optional_argument, 0, 'P'}, + {"graph", optional_argument, 0, 'q'}, + {"no-graph", optional_argument, 0, 'Q'}, + {"exec-counts", optional_argument, 0, 'C'}, + {"no-exec-counts", optional_argument, 0, 'Z'}, + {"file-info", no_argument, 0, 'i'}, + {"sum", no_argument, 0, 's'}, + + /* various options to affect output: */ + + {"all-lines", no_argument, 0, 'x'}, + {"directory-path", required_argument, 0, 'I'}, + {"display-unused-functions", no_argument, 0, 'z'}, + {"min-count", required_argument, 0, 'm'}, + {"print-path", no_argument, 0, 'L'}, + {"separate-files", no_argument, 0, 'y'}, + {"static-call-graph", no_argument, 0, 'c'}, + {"table-length", required_argument, 0, 't'}, + {"time", required_argument, 0, 'n'}, + {"no-time", required_argument, 0, 'N'}, + {"width", required_argument, 0, 'w'}, /* - * things which get -E excluded by default. + * These are for backwards-compatibility only. Their functionality + * is provided by the output style options already: */ -char *defaultEs[] = { "mcount" , "__mcleanup" , 0 }; - -int discard_underscores = 1; /* Should we discard initial underscores? */ -int bsd_style_output = 0; /* As opposed to FSF style output */ - -main(argc, argv) - int argc; - char **argv; + {"", required_argument, 0, 'e'}, + {"", required_argument, 0, 'E'}, + {"", required_argument, 0, 'f'}, + {"", required_argument, 0, 'F'}, + {"", required_argument, 0, 'k'}, + + /* miscellaneous: */ + + {"brief", no_argument, 0, 'b'}, + {"debug", optional_argument, 0, 'd'}, + {"help", no_argument, 0, 'h'}, + {"file-format", required_argument, 0, 'O'}, + {"traditional", no_argument, 0, 'T'}, + {"version", no_argument, 0, 'v'}, + {0, no_argument, 0, 0} +}; + + +static void +DEFUN(usage, (stream, status), FILE *stream AND int status) +{ + fprintf(stream, "\ +Usage: %s [-[abchilLsTvwxyz]] [-[ACeEfFJnNOpPqQZ][name]] [-I dirs]\n\ + [-d[num]] [-k from/to] [-m min-count] [-t table-length]\n\ + [--[no-]annotated-source[=name]] [--[no-]exec-counts[=name]]\n\ + [--[no-]flat-profile[=name]] [--[no-]graph[=name]]\n\ + [--[no-]time=name] [--all-lines] [--brief] [--debug[=level]]\n\ + [--directory-path=dirs] [--display-unused-functions]\n\ + [--file-format=name] [--file-info] [--help] [--line] [--min-count=n]\n\ + [--no-static] [--print-path] [--separate-files]\n\ + [--static-call-graph] [--sum] [--table-length=len] [--traditional]\n\ + [--version] [--width=n]\n\ + [image-file] [profile-file...]\n", + whoami); + done(status); +} /* usage */ + + +int +DEFUN(main, (argc, argv), int argc AND char **argv) { - char **sp; - nltype **timesortnlp; + char **sp, *str; + Sym **cg = 0; + int ch, user_specified = 0; whoami = argv[0]; - --argc; - argv++; - debug = 0; - bflag = TRUE; - while ( *argv != 0 && **argv == '-' ) { - (*argv)++; - switch ( **argv ) { - case 'a': - aflag = TRUE; + xmalloc_set_program_name(whoami); + + while ((ch = getopt_long(argc, argv, + "aA::bBcCd::e:E:f:F:hiI:J::k:lLm:n::N::O:p::P::q::Q::st:Tvw:xyzZ::", + long_options, 0)) + != EOF) + { + switch (ch) { + case 'a': ignore_static_funcs = TRUE; break; + case 'A': + if (optarg) { + sym_id_add(optarg, INCL_ANNO); + } /* if */ + output_style |= STYLE_ANNOTATED_SOURCE; + user_specified |= STYLE_ANNOTATED_SOURCE; break; - case 'b': - bflag = FALSE; + case 'b': print_descriptions = FALSE; break; + case 'B': + output_style |= STYLE_CALL_GRAPH; + user_specified |= STYLE_CALL_GRAPH; break; - case 'c': - cflag = TRUE; + case 'c': ignore_direct_calls = TRUE; break; + case 'C': + if (optarg) { + sym_id_add(optarg, INCL_EXEC); + } /* if */ + output_style |= STYLE_EXEC_COUNTS; + user_specified |= STYLE_EXEC_COUNTS; break; - case 'd': - dflag = TRUE; - (*argv)++; - debug |= atoi( *argv ); - debug |= ANYDEBUG; -# ifdef DEBUG - printf("[main] debug = %d\n", debug); -# else not DEBUG - printf("%s: -d ignored\n", whoami); -# endif DEBUG + case 'd': + if (optarg) { + debug_level |= atoi(optarg); + debug_level |= ANYDEBUG; + } else { + debug_level = ~0; + } /* if */ + DBG(ANYDEBUG, printf("[main] debug-level=0x%x\n", debug_level)); +#ifndef DEBUG + printf("%s: debugging not supported; -d ignored\n", whoami); +#endif DEBUG break; - case 'E': - ++argv; - addlist( Elist , *argv ); - Eflag = TRUE; - addlist( elist , *argv ); - eflag = TRUE; + case 'E': sym_id_add(optarg, EXCL_TIME); + case 'e': sym_id_add(optarg, EXCL_GRAPH); break; + case 'F': sym_id_add(optarg, INCL_TIME); + case 'f': sym_id_add(optarg, INCL_GRAPH); break; + case 'g': sym_id_add(optarg, EXCL_FLAT); break; + case 'G': sym_id_add(optarg, INCL_FLAT); break; + case 'h': usage(stdout, 0); + case 'i': + output_style |= STYLE_GMON_INFO; + user_specified |= STYLE_GMON_INFO; break; - case 'e': - addlist( elist , *++argv ); - eflag = TRUE; + case 'I': search_list_append(&src_search_list, optarg); break; + case 'J': + if (optarg) { + sym_id_add(optarg, EXCL_ANNO); + output_style |= STYLE_ANNOTATED_SOURCE; + } else { + output_style &= ~STYLE_ANNOTATED_SOURCE; + } /* if */ + user_specified |= STYLE_ANNOTATED_SOURCE; break; - case 'F': - ++argv; - addlist( Flist , *argv ); - Fflag = TRUE; - addlist( flist , *argv ); - fflag = TRUE; + case 'k': sym_id_add(optarg, EXCL_ARCS); break; + case 'l': line_granularity = TRUE; break; + case 'L': print_path = TRUE; break; + case 'm': bb_min_calls = atoi(optarg); break; + case 'n': sym_id_add(optarg, INCL_TIME); break; + case 'N': sym_id_add(optarg, EXCL_TIME); break; + case 'O': + switch (optarg[0]) { + case 'a': file_format = FF_AUTO; break; + case 'm': file_format = FF_MAGIC; break; + case 'b': file_format = FF_BSD; break; + case 'p': file_format = FF_PROF; break; + default: + fprintf(stderr, "%s: unknown file format %s\n", + optarg, whoami); + done(1); + } /* switch */ break; - case 'f': - addlist( flist , *++argv ); - fflag = TRUE; + case 'p': + if (optarg) { + sym_id_add(optarg, INCL_FLAT); + } /* if */ + output_style |= STYLE_FLAT_PROFILE; + user_specified |= STYLE_FLAT_PROFILE; break; - case 'k': - addlist( kfromlist , *++argv ); - addlist( ktolist , *++argv ); - kflag = TRUE; + case 'P': + if (optarg) { + sym_id_add(optarg, EXCL_FLAT); + output_style |= STYLE_FLAT_PROFILE; + } else { + output_style &= ~STYLE_FLAT_PROFILE; + } /* if */ + user_specified |= STYLE_FLAT_PROFILE; break; - case 's': - sflag = TRUE; + case 'q': + if (optarg) { + if (strchr(optarg, '/')) { + sym_id_add(optarg, INCL_ARCS); + } else { + sym_id_add(optarg, INCL_GRAPH); + } /* if */ + } /* if */ + output_style |= STYLE_CALL_GRAPH; + user_specified |= STYLE_CALL_GRAPH; break; - case 'T': /* "Traditional" output format */ - bsd_style_output = 1; + case 'Q': + if (optarg) { + if (strchr(optarg, '/')) { + sym_id_add(optarg, EXCL_ARCS); + } else { + sym_id_add(optarg, EXCL_GRAPH); + } /* if */ + output_style |= STYLE_CALL_GRAPH; + } else { + output_style &= ~STYLE_CALL_GRAPH; + } /* if */ + user_specified |= STYLE_CALL_GRAPH; break; - case 'v': - printf ("gprof version %s\n", VERSION); - exit(0); + case 's': + output_style |= STYLE_SUMMARY_FILE; + user_specified |= STYLE_SUMMARY_FILE; break; - case 'z': - zflag = TRUE; + case 't': + bb_table_length = atoi(optarg); + if (bb_table_length < 0) { + bb_table_length = 0; + } /* if */ break; - default: - fprintf (stderr, "\ -Usage: %s [-a] [-b] [-c] [-d[num]] [-E function-name] [-e function-name]\n\ - [-F function-name] [-f function-name] [-k from to] [-s] [-T] [-z]\n\ - [image-file] [profile-file...]\n", whoami); - exit (1); - } - 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 ); - } - /* - * how many ticks per second? - * if we can't tell, report time in ticks. - */ - hz = hertz(); - if (hz == 0) { - hz = 1; - fprintf(stderr, "time is in ticks, not seconds\n"); - } - /* - * get information about a.out file. - */ - getnfile(); - /* - * get information about mon.out file(s). - */ - do { - getpfile( gmonname ); - if ( *argv != 0 ) { - gmonname = *argv; - } - } while ( *argv++ != 0 ); - /* - * dump out a gmon.sum file if requested - */ - if ( sflag ) { - dumpsum( GMONSUM ); - } - /* - * assign samples to procedures - */ - asgnsamples(); - /* - * assemble the dynamic profile - */ - timesortnlp = doarcs(); - - if (bsd_style_output) { - printgprof( timesortnlp ); /* print the dynamic profile */ - printprof(); /* print the flat profile */ - } else { - printprof(); /* print the flat profile */ - printgprof( timesortnlp ); /* print the dynamic profile */ - } - /* - * print the index - */ - printindex(); - done(); -} + case 'T': bsd_style_output = TRUE; break; + case 'v': printf ("%s version %s\n", whoami, VERSION); done(0); + case 'w': + output_width = atoi(optarg); + if (output_width < 1) { + output_width = 1; + } /* if */ + break; + case 'x': bb_annotate_all_lines = TRUE; break; + case 'y': create_annotation_files = TRUE; break; + case 'z': ignore_zeros = FALSE; break; + case 'Z': + if (optarg) { + sym_id_add(optarg, EXCL_EXEC); + output_style |= STYLE_EXEC_COUNTS; + } else { + output_style &= ~STYLE_EXEC_COUNTS; + } /* if */ + user_specified |= STYLE_ANNOTATED_SOURCE; + break; + default: usage(stderr, 1); + } /* switch */ + } /* while */ + + /* append value of GPROF_PATH to source search list if set: */ + str = getenv("GPROF_PATH"); + if (str) { + search_list_append(&src_search_list, str); + } /* if */ + + if (optind < argc) { + a_out_name = argv[optind++]; + } /* if */ + if (optind < argc) { + gmon_name = argv[optind++]; + } /* if */ /* - * Set up string and symbol tables from a.out. - * and optionally the text space. - * On return symbol table is sorted by value. + * Turn off default functions: */ -getnfile() -{ - int valcmp(); - - abfd = bfd_openr (a_outname, NULL); - - if (abfd == NULL) { - perror (a_outname); - done(); - } - - if (!bfd_check_format (abfd, bfd_object)) { - fprintf (stderr, "%s: %s: bad format\n", whoami, a_outname); - done(); - } - -/* getstrtab(nfile); */ - getsymtab(abfd); - gettextspace( abfd ); - qsort(nl, nname, sizeof(nltype), valcmp); - -# ifdef DEBUG - if ( debug & AOUTDEBUG ) { - register int j; - - for (j = 0; j < nname; j++){ - printf("[getnfile] 0X%08x\t%s\n", nl[j].value, nl[j].name); - } - } -# endif DEBUG -} - -/* - * Read in symbol table - */ -getsymtab(abfd) -bfd *abfd; -{ - register long i; - int askfor; - long nosyms; - asymbol **syms; - i = bfd_get_symtab_upper_bound (abfd);/* This will probably give us more - * than we need, but that's ok. - */ - if (i < 0) - { - fprintf (stderr, "%s: %s: %s\n", whoami, a_outname, - bfd_errmsg (bfd_get_error ())); - exit (1); - } - syms = (asymbol**)xmalloc (i); - nosyms = bfd_canonicalize_symtab (abfd, syms); - if (nosyms < 0) - { - fprintf (stderr, "%s: %s: %s\n", whoami, a_outname, - bfd_errmsg (bfd_get_error ())); - exit (1); - } - - nname = 0; - for (i = 0; i < nosyms; i++) { - if (!funcsymbol (syms[i])) - continue; - nname++; - } - - if (nname == 0) { - fprintf(stderr, "%s: %s: no symbols\n", whoami , a_outname ); - done(); - } - askfor = nname + 1; - nl = (nltype *) calloc( askfor , sizeof(nltype) ); - if (nl == 0) { - fprintf(stderr, "%s: No room for %d bytes of symbol table\n", - whoami, askfor * sizeof(nltype) ); - done(); - } - - /* pass2 - read symbols */ - npe = nl; - nname = 0; - for (i = 0; i < nosyms; i++) { - if (!funcsymbol (syms[i])) { -# ifdef DEBUG - if ( debug & AOUTDEBUG ) { - printf( "[getsymtab] rejecting: 0x%x %s\n" , - syms[i]->value, syms[i]->name); - } -# endif DEBUG - continue; - } - /* Symbol offsets are always section-relative. */ - npe->value = syms[i]->value + syms[i]->section->vma; - npe->name = syms[i]->name; - - /* If we see "main" without an initial '_', we assume - names are *not* prefixed by '_'. */ - if (npe->name[0] == 'm' && discard_underscores - && strcmp(npe->name, "main") == 0) - discard_underscores = 0; - -# ifdef DEBUG - if ( debug & AOUTDEBUG ) { - printf( "[getsymtab] %d %s 0x%08x\n" , - nname , npe -> name , npe -> value ); - } -# endif DEBUG - npe++; - nname++; - } - npe->value = -1; -} - -/* - * read in the text space of an a.out file - */ -gettextspace( abfd ) - bfd *abfd; -{ - asection *texsec; - - if ( cflag == 0 ) { - return; - } - - texsec = bfd_get_section_by_name (abfd, ".text"); - if (texsec == NULL) { - return; - } - - textspace = (u_char *) malloc( texsec->_cooked_size ); - - if ( textspace == 0 ) { - fprintf( stderr , "%s: ran out room for %d bytes of text space: " , - whoami , texsec->_cooked_size); - fprintf( stderr , "can't do -c\n" ); - return; - } - bfd_get_section_contents (abfd, texsec, textspace, texsec->filepos, - texsec->_cooked_size); -} -/* - * information from a gmon.out file is in two parts: - * an array of sampling hits within pc ranges, - * and the arcs. - */ -getpfile(filename) - char *filename; -{ - FILE *pfile; - FILE *openpfile(); - struct rawarc arc; - struct veryrawarc rawarc; - - pfile = openpfile(filename); - readsamples(pfile); - /* - * the rest of the file consists of - * a bunch of <from,self,count> tuples. - */ - while ( fread( &rawarc , sizeof rawarc , 1 , pfile ) == 1 ) { - arc.raw_frompc = bfd_get_32 (abfd, (bfd_byte *) rawarc.raw_frompc); - arc.raw_selfpc = bfd_get_32 (abfd, (bfd_byte *) rawarc.raw_selfpc); - arc.raw_count = bfd_get_32 (abfd, (bfd_byte *) rawarc.raw_count); -# ifdef DEBUG - if ( debug & SAMPLEDEBUG ) { - printf( "[getpfile] frompc 0x%x selfpc 0x%x count %d\n" , - arc.raw_frompc , arc.raw_selfpc , arc.raw_count ); - } -# endif DEBUG - /* - * add this arc - */ - tally( &arc ); - } - fclose(pfile); -} - -FILE * -openpfile(filename) - char *filename; -{ - struct hdr tmp; - struct rawhdr raw; - FILE *pfile; - - if((pfile = fopen(filename, FOPEN_RB)) == NULL) { - perror(filename); - done(); - } - if (sizeof(struct rawhdr) != fread(&raw, 1, sizeof(struct rawhdr), pfile)) - { - fprintf(stderr, "%s: file too short to be a gmon file\n", filename); - done(); - } - tmp.lowpc = (UNIT *)bfd_get_32 (abfd, (bfd_byte *) &raw.lowpc[0]); - tmp.highpc = (UNIT *)bfd_get_32 (abfd, (bfd_byte *) &raw.highpc[0]); - tmp.ncnt = bfd_get_32 (abfd, (bfd_byte *) &raw.ncnt[0]); - - if ( s_highpc != 0 && ( tmp.lowpc != h.lowpc || - tmp.highpc != h.highpc || tmp.ncnt != h.ncnt ) ) { - fprintf(stderr, "%s: incompatible with first gmon file\n", filename); - done(); - } - h = tmp; - s_lowpc = (unsigned long) h.lowpc; - s_highpc = (unsigned long) h.highpc; - lowpc = (unsigned long)h.lowpc / sizeof(UNIT); - highpc = (unsigned long)h.highpc / sizeof(UNIT); - sampbytes = h.ncnt - sizeof(struct rawhdr); - nsamples = sampbytes / sizeof (UNIT); -# ifdef DEBUG - if ( debug & SAMPLEDEBUG ) { - printf( "[openpfile] hdr.lowpc 0x%x hdr.highpc 0x%x hdr.ncnt %d\n", - h.lowpc , h.highpc , h.ncnt ); - printf( "[openpfile] s_lowpc 0x%x s_highpc 0x%x\n" , - s_lowpc , s_highpc ); - printf( "[openpfile] lowpc 0x%x highpc 0x%x\n" , - lowpc , highpc ); - printf( "[openpfile] sampbytes %d nsamples %d\n" , - sampbytes , nsamples ); - } -# endif DEBUG - return(pfile); -} - -tally( rawp ) - struct rawarc *rawp; -{ - nltype *parentp; - nltype *childp; - - parentp = nllookup( rawp -> raw_frompc ); - childp = nllookup( rawp -> raw_selfpc ); - if ( kflag - && onlist( kfromlist , parentp -> name ) - && onlist( ktolist , childp -> name ) ) { - return; - } - childp -> ncall += rawp -> raw_count; -# ifdef DEBUG - if ( debug & TALLYDEBUG ) { - printf( "[tally] arc from %s to %s traversed %d times\n" , - parentp -> name , childp -> name , rawp -> raw_count ); - } -# endif DEBUG - addarc( parentp , childp , rawp -> raw_count ); -} + for (sp = &default_excluded_list[0]; *sp; sp++) { + sym_id_add(*sp, EXCL_TIME); + sym_id_add(*sp, EXCL_GRAPH); +#ifdef __osf__ + sym_id_add(*sp, EXCL_FLAT); +#endif + } /* for */ -/* - * dump out the gmon.sum file - */ -dumpsum( sumfile ) - char *sumfile; -{ - register nltype *nlp; - register arctype *arcp; - struct rawarc arc; - FILE *sfile; - - if ( ( sfile = fopen ( sumfile , "w" ) ) == NULL ) { - perror( sumfile ); - done(); - } /* - * dump the header; use the last header read in + * For line-by-line profiling, also want to keep those + * functions off the flat profile: */ - if ( fwrite( &h , sizeof h , 1 , sfile ) != 1 ) { - perror( sumfile ); - done(); - } + if (line_granularity) { + for (sp = &default_excluded_list[0]; *sp; sp++) { + sym_id_add(*sp, EXCL_FLAT); + } /* for */ + } /* if */ + /* - * dump the samples + * Read symbol table from core file: */ - if (fwrite(samples, sizeof (UNIT), nsamples, sfile) != nsamples) { - perror( sumfile ); - done(); - } + core_init(a_out_name); + /* - * dump the normalized raw arc information + * If we should ignore direct function calls, we need to load + * to core's text-space: */ - for ( nlp = nl ; nlp < npe ; nlp++ ) { - for ( arcp = nlp -> children ; arcp ; arcp = arcp -> arc_childlist ) { - arc.raw_frompc = arcp -> arc_parentp -> value; - arc.raw_selfpc = arcp -> arc_childp -> value; - arc.raw_count = arcp -> arc_count; - if ( fwrite ( &arc , sizeof arc , 1 , sfile ) != 1 ) { - perror( sumfile ); - done(); - } -# ifdef DEBUG - if ( debug & SAMPLEDEBUG ) { - printf( "[dumpsum] frompc 0x%x selfpc 0x%x count %d\n" , - arc.raw_frompc , arc.raw_selfpc , arc.raw_count ); - } -# endif DEBUG - } - } - fclose( sfile ); -} - -valcmp(p1, p2) - nltype *p1, *p2; -{ - if ( p1 -> value < p2 -> value ) { - return LESSTHAN; - } - if ( p1 -> value > p2 -> value ) { - return GREATERTHAN; - } - return EQUALTO; -} - -readsamples(pfile) - FILE *pfile; -{ - register int i; - - - if (samples == 0) { - samples = (int *) calloc (nsamples, sizeof(int)); - if (samples == 0) { - fprintf( stderr , "%s: No room for %d sample pc's\n", - whoami , nsamples); - done(); - } - } - for (i = 0; i < nsamples; i++) { - UNIT raw; - int value; - - fread(raw, sizeof (raw), 1, pfile); - value = bfd_get_16 (abfd, (bfd_byte *) raw); - if (feof(pfile)) - break; - samples[i] += value; - } - if (i != nsamples) { - fprintf(stderr, - "%s: unexpected EOF after reading %d/%d samples\n", - whoami , --i , nsamples ); - done(); - } -} - -/* - * 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). - */ -asgnsamples() -{ - register int j; - int ccnt; - double time; - unsigned long pcl, pch; - register int i; - unsigned long overlap; - unsigned long svalue0, svalue1; - - /* read samples and assign to namelist symbols */ - scale = highpc - lowpc; - scale /= nsamples - 1; - alignentries(); - for (i = 0, j = 1; i < nsamples; i++) { - ccnt = samples[i]; - if (ccnt == 0) - continue; - pcl = lowpc + scale * i; - pch = lowpc + scale * (i + 1); - time = ccnt; -# ifdef DEBUG - if ( debug & SAMPLEDEBUG ) { - printf( "[asgnsamples] pcl 0x%x pch 0x%x ccnt %d\n" , - pcl , pch , ccnt ); - } -# endif DEBUG - 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 %d overlap\n", - nl[j].value/sizeof(UNIT), svalue0, svalue1, - nl[j].name, - overlap * time / scale, overlap); - } -# endif DEBUG - nl[j].time += overlap * time / scale; - } - } - } -# ifdef DEBUG - if (debug & SAMPLEDEBUG) { - printf("[asgnsamples] totime %f\n", totime); - } -# endif DEBUG -} - - -unsigned long -min(a, b) - unsigned long a,b; -{ - if (a<b) - return(a); - return(b); -} + if (ignore_direct_calls) { + core_get_text_space(core_bfd); + } /* if */ -unsigned long -max(a, b) - unsigned long a,b; -{ - if (a>b) - return(a); - return(b); -} + /* + * Create symbols from core image: + */ + if (line_granularity) { + core_create_line_syms(core_bfd); + } else { + core_create_function_syms(core_bfd); + } /* if */ /* - * calculate scaled entry point addresses (to save time in asgnsamples), - * and possibly push the scaled entry points over the entry mask, - * if it turns out that the entry point is in one bucket and the code - * for a routine is in the next bucket. + * Translate sym specs into syms: */ -alignentries() -{ - register struct nl *nlp; - unsigned long bucket_of_entry; - unsigned long bucket_of_code; - - for (nlp = nl; nlp < npe; nlp++) { - nlp -> svalue = nlp -> value / sizeof(UNIT); - bucket_of_entry = (nlp->svalue - lowpc) / scale; - bucket_of_code = (nlp->svalue + UNITS_TO_CODE - lowpc) / scale; - if (bucket_of_entry < bucket_of_code) { -# ifdef DEBUG - if (debug & SAMPLEDEBUG) { - printf("[alignentries] pushing svalue 0x%x to 0x%x\n", - nlp->svalue, nlp->svalue + UNITS_TO_CODE); - } -# endif DEBUG - nlp->svalue += UNITS_TO_CODE; - } - } -} + sym_id_parse(); -bool -funcsymbol( symp ) - asymbol *symp; -{ - extern char *strtab; /* string table from a.out */ - extern int aflag; /* if static functions aren't desired */ - CONST char *name; - int i; - char symprefix; - symbol_info syminfo; - - /* - * must be a text symbol, - * and static text symbols don't qualify if aflag set. - */ - - - if (!symp->section) - return FALSE; - - if (aflag && (symp->flags&BSF_LOCAL)) { -#if defined(DEBUG) - fprintf (stderr, "%s(%d): %s: not a function\n", __FILE__, __LINE__, symp->name); + if (file_format == FF_PROF) { +#ifdef PROF_SUPPORT_IMPLEMENTED + /* + * Get information about mon.out file(s): + */ + do { + mon_out_read(gmon_name); + if (optind < argc) { + gmon_name = argv[optind]; + } /* if */ + } while (optind++ < argc); +#else + fprintf(stderr, + "%s: sorry, file format `prof' is not yet supported\n", + whoami); + done(1); #endif - return FALSE; - } - + } else { + /* + * Get information about gmon.out file(s): + */ + do { + gmon_out_read(gmon_name); + if (optind < argc) { + gmon_name = argv[optind]; + } /* if */ + } while (optind++ < argc); + } /* if */ - symprefix = bfd_get_symbol_leading_char (abfd); - bfd_get_symbol_info (abfd, symp, &syminfo); - i = syminfo.type; -#if defined(DEBUG) && 0 - if (i != 'T' && i != 't') - fprintf (stderr, "%s(%d): %s is of class %c\n", __FILE__, __LINE__, symp->name, i); -#endif + /* + * If user did not specify output style, try to guess something + * reasonable: + */ + if (output_style == 0) { + if (gmon_input & (INPUT_HISTOGRAM | INPUT_CALL_GRAPH)) { + output_style = STYLE_FLAT_PROFILE | STYLE_CALL_GRAPH; + } else { + output_style = STYLE_EXEC_COUNTS; + } /* if */ + output_style &= ~user_specified; + } /* if */ - /* - * Any external text symbol should be okay. (Only problem would be - * variables in the text section.) - */ - - if (i == 'T') - return TRUE; - - /* - * 't' is static text; -a says to ignore it. So if it's not - * a static text symbol, *or* it is and the user gave -a, we - * ignore it. - */ - - if (i != 't' || aflag) - return FALSE; - - /* - * can't have any `funny' characters in name, - * where `funny' includes `.', .o file names - * and `$', pascal labels. - */ - if (!symp->name) - return FALSE; - - for (name = symp->name; *name; name++) { - if ( *name == '.' || *name == '$' ) { - return FALSE; - } - } - - /* On systems where the C compiler adds an underscore to all names, - * static names without underscores seem usually to be labels in - * hand written assembler in the library. We don't want these - * names. This is certainly necessary on a Sparc running SunOS 4.1 - * (try profiling a program that does a lot of division). I don't - * know whether it has harmful side effects on other systems. - * Perhaps it should be made configurable. - */ - - if (symprefix && symprefix != *symp->name - /* Gcc may add special symbols to help gdb figure out the file - language. We want to ignore these, since sometimes they - mask the real function. (dj@ctron) */ - || !strncmp (symp->name, "__gnu_compiled", 14) - || !strncmp (symp->name, "___gnu_compiled", 15)) - return FALSE; - - return TRUE; + /* + * Dump a gmon.sum file if requested (before any other processing!): + */ + if (output_style & STYLE_SUMMARY_FILE) { + gmon_out_write(GMONSUM); + } /* if */ + + if (gmon_input & INPUT_HISTOGRAM) { + hist_assign_samples(); + } /* if */ + + if (gmon_input & INPUT_CALL_GRAPH) { + cg = cg_assemble(); + } /* if */ + + /* do some simple sanity checks: */ + + if ((output_style & STYLE_FLAT_PROFILE) + && !(gmon_input & INPUT_HISTOGRAM)) + { + fprintf(stderr, "%s: gmon.out file is missing histogram\n", whoami); + done(1); + } /* if */ + + if ((output_style & STYLE_CALL_GRAPH) && !(gmon_input & INPUT_CALL_GRAPH)) + { + fprintf(stderr, + "%s: gmon.out file is missing call-graph data\n", whoami); + done(1); + } /* if */ + + /* output whatever user whishes to see: */ + + if (cg && (output_style & STYLE_CALL_GRAPH) && bsd_style_output) { + cg_print(cg); /* print the dynamic profile */ + } /* if */ + + if (output_style & STYLE_FLAT_PROFILE) { + hist_print(); /* print the flat profile */ + } /* if */ + + if (cg && (output_style & STYLE_CALL_GRAPH)) { + if (!bsd_style_output) { + cg_print(cg); /* print the dynamic profile */ + } /* if */ + cg_print_index(); + } /* if */ + + if (output_style & STYLE_EXEC_COUNTS) { + print_exec_counts(); + } /* if */ + + if (output_style & STYLE_ANNOTATED_SOURCE) { + print_annotated_source(); + } /* if */ + return 0; } -done() +void +done (status) + int status; { - - exit(0); + exit(status); } |