12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513 |
- /* Gcov.c: prepend line execution counts and branch probabilities to a
- source file.
- Copyright (C) 1990-2015 Free Software Foundation, Inc.
- Contributed by James E. Wilson of Cygnus Support.
- Mangled by Bob Manson of Cygnus Support.
- Mangled further by Nathan Sidwell <nathan@codesourcery.com>
- Gcov is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3, or (at your option)
- any later version.
- Gcov is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with Gcov; see the file COPYING3. If not see
- <http://www.gnu.org/licenses/>. */
- /* ??? Print a list of the ten blocks with the highest execution counts,
- and list the line numbers corresponding to those blocks. Also, perhaps
- list the line numbers with the highest execution counts, only printing
- the first if there are several which are all listed in the same block. */
- /* ??? Should have an option to print the number of basic blocks, and the
- percent of them that are covered. */
- /* Need an option to show individual block counts, and show
- probabilities of fall through arcs. */
- #include "config.h"
- #include "system.h"
- #include "coretypes.h"
- #include "tm.h"
- #include "intl.h"
- #include "diagnostic.h"
- #include "version.h"
- #include "demangle.h"
- #include <getopt.h>
- #define IN_GCOV 1
- #include "gcov-io.h"
- #include "gcov-io.c"
- /* The gcno file is generated by -ftest-coverage option. The gcda file is
- generated by a program compiled with -fprofile-arcs. Their formats
- are documented in gcov-io.h. */
- /* The functions in this file for creating and solution program flow graphs
- are very similar to functions in the gcc source file profile.c. In
- some places we make use of the knowledge of how profile.c works to
- select particular algorithms here. */
- /* The code validates that the profile information read in corresponds
- to the code currently being compiled. Rather than checking for
- identical files, the code below compares a checksum on the CFG
- (based on the order of basic blocks and the arcs in the CFG). If
- the CFG checksum in the gcda file match the CFG checksum in the
- gcno file, the profile data will be used. */
- /* This is the size of the buffer used to read in source file lines. */
- struct function_info;
- struct block_info;
- struct source_info;
- /* Describes an arc between two basic blocks. */
- typedef struct arc_info
- {
- /* source and destination blocks. */
- struct block_info *src;
- struct block_info *dst;
- /* transition counts. */
- gcov_type count;
- /* used in cycle search, so that we do not clobber original counts. */
- gcov_type cs_count;
- unsigned int count_valid : 1;
- unsigned int on_tree : 1;
- unsigned int fake : 1;
- unsigned int fall_through : 1;
- /* Arc to a catch handler. */
- unsigned int is_throw : 1;
- /* Arc is for a function that abnormally returns. */
- unsigned int is_call_non_return : 1;
- /* Arc is for catch/setjmp. */
- unsigned int is_nonlocal_return : 1;
- /* Is an unconditional branch. */
- unsigned int is_unconditional : 1;
- /* Loop making arc. */
- unsigned int cycle : 1;
- /* Next branch on line. */
- struct arc_info *line_next;
- /* Links to next arc on src and dst lists. */
- struct arc_info *succ_next;
- struct arc_info *pred_next;
- } arc_t;
- /* Describes a basic block. Contains lists of arcs to successor and
- predecessor blocks. */
- typedef struct block_info
- {
- /* Chain of exit and entry arcs. */
- arc_t *succ;
- arc_t *pred;
- /* Number of unprocessed exit and entry arcs. */
- gcov_type num_succ;
- gcov_type num_pred;
- /* Block execution count. */
- gcov_type count;
- unsigned flags : 12;
- unsigned count_valid : 1;
- unsigned valid_chain : 1;
- unsigned invalid_chain : 1;
- unsigned exceptional : 1;
- /* Block is a call instrumenting site. */
- unsigned is_call_site : 1; /* Does the call. */
- unsigned is_call_return : 1; /* Is the return. */
- /* Block is a landing pad for longjmp or throw. */
- unsigned is_nonlocal_return : 1;
- union
- {
- struct
- {
- /* Array of line numbers and source files. source files are
- introduced by a linenumber of zero, the next 'line number' is
- the number of the source file. Always starts with a source
- file. */
- unsigned *encoding;
- unsigned num;
- } line; /* Valid until blocks are linked onto lines */
- struct
- {
- /* Single line graph cycle workspace. Used for all-blocks
- mode. */
- arc_t *arc;
- unsigned ident;
- } cycle; /* Used in all-blocks mode, after blocks are linked onto
- lines. */
- } u;
- /* Temporary chain for solving graph, and for chaining blocks on one
- line. */
- struct block_info *chain;
- } block_t;
- /* Describes a single function. Contains an array of basic blocks. */
- typedef struct function_info
- {
- /* Name of function. */
- char *name;
- char *demangled_name;
- unsigned ident;
- unsigned lineno_checksum;
- unsigned cfg_checksum;
- /* The graph contains at least one fake incoming edge. */
- unsigned has_catch : 1;
- /* Array of basic blocks. Like in GCC, the entry block is
- at blocks[0] and the exit block is at blocks[1]. */
- #define ENTRY_BLOCK (0)
- #define EXIT_BLOCK (1)
- block_t *blocks;
- unsigned num_blocks;
- unsigned blocks_executed;
- /* Raw arc coverage counts. */
- gcov_type *counts;
- unsigned num_counts;
- /* First line number & file. */
- unsigned line;
- unsigned src;
- /* Next function in same source file. */
- struct function_info *line_next;
- /* Next function. */
- struct function_info *next;
- } function_t;
- /* Describes coverage of a file or function. */
- typedef struct coverage_info
- {
- int lines;
- int lines_executed;
- int branches;
- int branches_executed;
- int branches_taken;
- int calls;
- int calls_executed;
- char *name;
- } coverage_t;
- /* Describes a single line of source. Contains a chain of basic blocks
- with code on it. */
- typedef struct line_info
- {
- gcov_type count; /* execution count */
- union
- {
- arc_t *branches; /* branches from blocks that end on this
- line. Used for branch-counts when not
- all-blocks mode. */
- block_t *blocks; /* blocks which start on this line. Used
- in all-blocks mode. */
- } u;
- unsigned exists : 1;
- unsigned unexceptional : 1;
- } line_t;
- /* Describes a file mentioned in the block graph. Contains an array
- of line info. */
- typedef struct source_info
- {
- /* Canonical name of source file. */
- char *name;
- time_t file_time;
- /* Array of line information. */
- line_t *lines;
- unsigned num_lines;
- coverage_t coverage;
- /* Functions in this source file. These are in ascending line
- number order. */
- function_t *functions;
- } source_t;
- typedef struct name_map
- {
- char *name; /* Source file name */
- unsigned src; /* Source file */
- } name_map_t;
- /* Holds a list of function basic block graphs. */
- static function_t *functions;
- static function_t **fn_end = &functions;
- static source_t *sources; /* Array of source files */
- static unsigned n_sources; /* Number of sources */
- static unsigned a_sources; /* Allocated sources */
- static name_map_t *names; /* Mapping of file names to sources */
- static unsigned n_names; /* Number of names */
- static unsigned a_names; /* Allocated names */
- /* This holds data summary information. */
- static unsigned object_runs;
- static unsigned program_count;
- static unsigned total_lines;
- static unsigned total_executed;
- /* Modification time of graph file. */
- static time_t bbg_file_time;
- /* Name of the notes (gcno) output file. The "bbg" prefix is for
- historical reasons, when the notes file contained only the
- basic block graph notes. */
- static char *bbg_file_name;
- /* Stamp of the bbg file */
- static unsigned bbg_stamp;
- /* Name and file pointer of the input file for the count data (gcda). */
- static char *da_file_name;
- /* Data file is missing. */
- static int no_data_file;
- /* If there is several input files, compute and display results after
- reading all data files. This way if two or more gcda file refer to
- the same source file (eg inline subprograms in a .h file), the
- counts are added. */
- static int multiple_files = 0;
- /* Output branch probabilities. */
- static int flag_branches = 0;
- /* Show unconditional branches too. */
- static int flag_unconditional = 0;
- /* Output a gcov file if this is true. This is on by default, and can
- be turned off by the -n option. */
- static int flag_gcov_file = 1;
- /* Output progress indication if this is true. This is off by default
- and can be turned on by the -d option. */
- static int flag_display_progress = 0;
- /* Output *.gcov file in intermediate format used by 'lcov'. */
- static int flag_intermediate_format = 0;
- /* Output demangled function names. */
- static int flag_demangled_names = 0;
- /* For included files, make the gcov output file name include the name
- of the input source file. For example, if x.h is included in a.c,
- then the output file name is a.c##x.h.gcov instead of x.h.gcov. */
- static int flag_long_names = 0;
- /* Output count information for every basic block, not merely those
- that contain line number information. */
- static int flag_all_blocks = 0;
- /* Output summary info for each function. */
- static int flag_function_summary = 0;
- /* Object directory file prefix. This is the directory/file where the
- graph and data files are looked for, if nonzero. */
- static char *object_directory = 0;
- /* Source directory prefix. This is removed from source pathnames
- that match, when generating the output file name. */
- static char *source_prefix = 0;
- static size_t source_length = 0;
- /* Only show data for sources with relative pathnames. Absolute ones
- usually indicate a system header file, which although it may
- contain inline functions, is usually uninteresting. */
- static int flag_relative_only = 0;
- /* Preserve all pathname components. Needed when object files and
- source files are in subdirectories. '/' is mangled as '#', '.' is
- elided and '..' mangled to '^'. */
- static int flag_preserve_paths = 0;
- /* Output the number of times a branch was taken as opposed to the percentage
- of times it was taken. */
- static int flag_counts = 0;
- /* Forward declarations. */
- static int process_args (int, char **);
- static void print_usage (int) ATTRIBUTE_NORETURN;
- static void print_version (void) ATTRIBUTE_NORETURN;
- static void process_file (const char *);
- static void generate_results (const char *);
- static void create_file_names (const char *);
- static int name_search (const void *, const void *);
- static int name_sort (const void *, const void *);
- static char *canonicalize_name (const char *);
- static unsigned find_source (const char *);
- static function_t *read_graph_file (void);
- static int read_count_file (function_t *);
- static void solve_flow_graph (function_t *);
- static void find_exception_blocks (function_t *);
- static void add_branch_counts (coverage_t *, const arc_t *);
- static void add_line_counts (coverage_t *, function_t *);
- static void executed_summary (unsigned, unsigned);
- static void function_summary (const coverage_t *, const char *);
- static const char *format_gcov (gcov_type, gcov_type, int);
- static void accumulate_line_counts (source_t *);
- static void output_gcov_file (const char *, source_t *);
- static int output_branch_count (FILE *, int, const arc_t *);
- static void output_lines (FILE *, const source_t *);
- static char *make_gcov_file_name (const char *, const char *);
- static char *mangle_name (const char *, char *);
- static void release_structures (void);
- static void release_function (function_t *);
- extern int main (int, char **);
- int
- main (int argc, char **argv)
- {
- int argno;
- int first_arg;
- const char *p;
- p = argv[0] + strlen (argv[0]);
- while (p != argv[0] && !IS_DIR_SEPARATOR (p[-1]))
- --p;
- progname = p;
- xmalloc_set_program_name (progname);
- /* Unlock the stdio streams. */
- unlock_std_streams ();
- gcc_init_libintl ();
- diagnostic_initialize (global_dc, 0);
- /* Handle response files. */
- expandargv (&argc, &argv);
- a_names = 10;
- names = XNEWVEC (name_map_t, a_names);
- a_sources = 10;
- sources = XNEWVEC (source_t, a_sources);
-
- argno = process_args (argc, argv);
- if (optind == argc)
- print_usage (true);
- if (argc - argno > 1)
- multiple_files = 1;
- first_arg = argno;
-
- for (; argno != argc; argno++)
- {
- if (flag_display_progress)
- printf ("Processing file %d out of %d\n",
- argno - first_arg + 1, argc - first_arg);
- process_file (argv[argno]);
- }
- generate_results (multiple_files ? NULL : argv[argc - 1]);
- release_structures ();
- return 0;
- }
- /* Print a usage message and exit. If ERROR_P is nonzero, this is an error,
- otherwise the output of --help. */
- static void
- print_usage (int error_p)
- {
- FILE *file = error_p ? stderr : stdout;
- int status = error_p ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE;
- fnotice (file, "Usage: gcov [OPTION]... SOURCE|OBJ...\n\n");
- fnotice (file, "Print code coverage information.\n\n");
- fnotice (file, " -h, --help Print this help, then exit\n");
- fnotice (file, " -a, --all-blocks Show information for every basic block\n");
- fnotice (file, " -b, --branch-probabilities Include branch probabilities in output\n");
- fnotice (file, " -c, --branch-counts Output counts of branches taken\n\
- rather than percentages\n");
- fnotice (file, " -d, --display-progress Display progress information\n");
- fnotice (file, " -f, --function-summaries Output summaries for each function\n");
- fnotice (file, " -i, --intermediate-format Output .gcov file in intermediate text format\n");
- fnotice (file, " -l, --long-file-names Use long output file names for included\n\
- source files\n");
- fnotice (file, " -m, --demangled-names Output demangled function names\n");
- fnotice (file, " -n, --no-output Do not create an output file\n");
- fnotice (file, " -o, --object-directory DIR|FILE Search for object files in DIR or called FILE\n");
- fnotice (file, " -p, --preserve-paths Preserve all pathname components\n");
- fnotice (file, " -r, --relative-only Only show data for relative sources\n");
- fnotice (file, " -s, --source-prefix DIR Source prefix to elide\n");
- fnotice (file, " -u, --unconditional-branches Show unconditional branch counts too\n");
- fnotice (file, " -v, --version Print version number, then exit\n");
- fnotice (file, "\nFor bug reporting instructions, please see:\n%s.\n",
- bug_report_url);
- exit (status);
- }
- /* Print version information and exit. */
- static void
- print_version (void)
- {
- fnotice (stdout, "gcov %s%s\n", pkgversion_string, version_string);
- fprintf (stdout, "Copyright %s 2015 Free Software Foundation, Inc.\n",
- _("(C)"));
- fnotice (stdout,
- _("This is free software; see the source for copying conditions.\n"
- "There is NO warranty; not even for MERCHANTABILITY or \n"
- "FITNESS FOR A PARTICULAR PURPOSE.\n\n"));
- exit (SUCCESS_EXIT_CODE);
- }
- static const struct option options[] =
- {
- { "help", no_argument, NULL, 'h' },
- { "version", no_argument, NULL, 'v' },
- { "all-blocks", no_argument, NULL, 'a' },
- { "branch-probabilities", no_argument, NULL, 'b' },
- { "branch-counts", no_argument, NULL, 'c' },
- { "intermediate-format", no_argument, NULL, 'i' },
- { "no-output", no_argument, NULL, 'n' },
- { "long-file-names", no_argument, NULL, 'l' },
- { "function-summaries", no_argument, NULL, 'f' },
- { "demangled-names", no_argument, NULL, 'm' },
- { "preserve-paths", no_argument, NULL, 'p' },
- { "relative-only", no_argument, NULL, 'r' },
- { "object-directory", required_argument, NULL, 'o' },
- { "object-file", required_argument, NULL, 'o' },
- { "source-prefix", required_argument, NULL, 's' },
- { "unconditional-branches", no_argument, NULL, 'u' },
- { "display-progress", no_argument, NULL, 'd' },
- { 0, 0, 0, 0 }
- };
- /* Process args, return index to first non-arg. */
- static int
- process_args (int argc, char **argv)
- {
- int opt;
- while ((opt = getopt_long (argc, argv, "abcdfhilmno:s:pruv", options, NULL)) !=
- -1)
- {
- switch (opt)
- {
- case 'a':
- flag_all_blocks = 1;
- break;
- case 'b':
- flag_branches = 1;
- break;
- case 'c':
- flag_counts = 1;
- break;
- case 'f':
- flag_function_summary = 1;
- break;
- case 'h':
- print_usage (false);
- /* print_usage will exit. */
- case 'l':
- flag_long_names = 1;
- break;
- case 'm':
- flag_demangled_names = 1;
- break;
- case 'n':
- flag_gcov_file = 0;
- break;
- case 'o':
- object_directory = optarg;
- break;
- case 's':
- source_prefix = optarg;
- source_length = strlen (source_prefix);
- break;
- case 'r':
- flag_relative_only = 1;
- break;
- case 'p':
- flag_preserve_paths = 1;
- break;
- case 'u':
- flag_unconditional = 1;
- break;
- case 'i':
- flag_intermediate_format = 1;
- flag_gcov_file = 1;
- break;
- case 'd':
- flag_display_progress = 1;
- break;
- case 'v':
- print_version ();
- /* print_version will exit. */
- default:
- print_usage (true);
- /* print_usage will exit. */
- }
- }
- return optind;
- }
- /* Get the name of the gcov file. The return value must be free'd.
- It appends the '.gcov' extension to the *basename* of the file.
- The resulting file name will be in PWD.
- e.g.,
- input: foo.da, output: foo.da.gcov
- input: a/b/foo.cc, output: foo.cc.gcov */
- static char *
- get_gcov_intermediate_filename (const char *file_name)
- {
- const char *gcov = ".gcov";
- char *result;
- const char *cptr;
- /* Find the 'basename'. */
- cptr = lbasename (file_name);
- result = XNEWVEC (char, strlen (cptr) + strlen (gcov) + 1);
- sprintf (result, "%s%s", cptr, gcov);
- return result;
- }
- /* Output the result in intermediate format used by 'lcov'.
- The intermediate format contains a single file named 'foo.cc.gcov',
- with no source code included. A sample output is
- file:foo.cc
- function:5,1,_Z3foov
- function:13,1,main
- function:19,1,_GLOBAL__sub_I__Z3foov
- function:19,1,_Z41__static_initialization_and_destruction_0ii
- lcount:5,1
- lcount:7,9
- lcount:9,8
- lcount:11,1
- file:/.../iostream
- lcount:74,1
- file:/.../basic_ios.h
- file:/.../ostream
- file:/.../ios_base.h
- function:157,0,_ZStorSt12_Ios_IostateS_
- lcount:157,0
- file:/.../char_traits.h
- function:258,0,_ZNSt11char_traitsIcE6lengthEPKc
- lcount:258,0
- ...
- The default gcov outputs multiple files: 'foo.cc.gcov',
- 'iostream.gcov', 'ios_base.h.gcov', etc. with source code
- included. Instead the intermediate format here outputs only a single
- file 'foo.cc.gcov' similar to the above example. */
- static void
- output_intermediate_file (FILE *gcov_file, source_t *src)
- {
- unsigned line_num; /* current line number. */
- const line_t *line; /* current line info ptr. */
- function_t *fn; /* current function info ptr. */
- fprintf (gcov_file, "file:%s\n", src->name); /* source file name */
- for (fn = src->functions; fn; fn = fn->line_next)
- {
- /* function:<name>,<line_number>,<execution_count> */
- fprintf (gcov_file, "function:%d,%s,%s\n", fn->line,
- format_gcov (fn->blocks[0].count, 0, -1),
- flag_demangled_names ? fn->demangled_name : fn->name);
- }
- for (line_num = 1, line = &src->lines[line_num];
- line_num < src->num_lines;
- line_num++, line++)
- {
- arc_t *arc;
- if (line->exists)
- fprintf (gcov_file, "lcount:%u,%s\n", line_num,
- format_gcov (line->count, 0, -1));
- if (flag_branches)
- for (arc = line->u.branches; arc; arc = arc->line_next)
- {
- if (!arc->is_unconditional && !arc->is_call_non_return)
- {
- const char *branch_type;
- /* branch:<line_num>,<branch_coverage_type>
- branch_coverage_type
- : notexec (Branch not executed)
- : taken (Branch executed and taken)
- : nottaken (Branch executed, but not taken)
- */
- if (arc->src->count)
- branch_type = (arc->count > 0) ? "taken" : "nottaken";
- else
- branch_type = "notexec";
- fprintf (gcov_file, "branch:%d,%s\n", line_num, branch_type);
- }
- }
- }
- }
- /* Process a single input file. */
- static void
- process_file (const char *file_name)
- {
- function_t *fns;
- create_file_names (file_name);
- fns = read_graph_file ();
- if (!fns)
- return;
-
- read_count_file (fns);
- while (fns)
- {
- function_t *fn = fns;
- fns = fn->next;
- fn->next = NULL;
- if (fn->counts)
- {
- unsigned src = fn->src;
- unsigned line = fn->line;
- unsigned block_no;
- function_t *probe, **prev;
-
- /* Now insert it into the source file's list of
- functions. Normally functions will be encountered in
- ascending order, so a simple scan is quick. Note we're
- building this list in reverse order. */
- for (prev = &sources[src].functions;
- (probe = *prev); prev = &probe->line_next)
- if (probe->line <= line)
- break;
- fn->line_next = probe;
- *prev = fn;
- /* Mark last line in files touched by function. */
- for (block_no = 0; block_no != fn->num_blocks; block_no++)
- {
- unsigned *enc = fn->blocks[block_no].u.line.encoding;
- unsigned num = fn->blocks[block_no].u.line.num;
- for (; num--; enc++)
- if (!*enc)
- {
- if (enc[1] != src)
- {
- if (line >= sources[src].num_lines)
- sources[src].num_lines = line + 1;
- line = 0;
- src = enc[1];
- }
- enc++;
- num--;
- }
- else if (*enc > line)
- line = *enc;
- }
- if (line >= sources[src].num_lines)
- sources[src].num_lines = line + 1;
-
- solve_flow_graph (fn);
- if (fn->has_catch)
- find_exception_blocks (fn);
- *fn_end = fn;
- fn_end = &fn->next;
- }
- else
- /* The function was not in the executable -- some other
- instance must have been selected. */
- release_function (fn);
- }
- }
- static void
- output_gcov_file (const char *file_name, source_t *src)
- {
- char *gcov_file_name = make_gcov_file_name (file_name, src->coverage.name);
- if (src->coverage.lines)
- {
- FILE *gcov_file = fopen (gcov_file_name, "w");
- if (gcov_file)
- {
- fnotice (stdout, "Creating '%s'\n", gcov_file_name);
- output_lines (gcov_file, src);
- if (ferror (gcov_file))
- fnotice (stderr, "Error writing output file '%s'\n", gcov_file_name);
- fclose (gcov_file);
- }
- else
- fnotice (stderr, "Could not open output file '%s'\n", gcov_file_name);
- }
- else
- {
- unlink (gcov_file_name);
- fnotice (stdout, "Removing '%s'\n", gcov_file_name);
- }
- free (gcov_file_name);
- }
- static void
- generate_results (const char *file_name)
- {
- unsigned ix;
- source_t *src;
- function_t *fn;
- FILE *gcov_intermediate_file = NULL;
- char *gcov_intermediate_filename = NULL;
- for (ix = n_sources, src = sources; ix--; src++)
- if (src->num_lines)
- src->lines = XCNEWVEC (line_t, src->num_lines);
- for (fn = functions; fn; fn = fn->next)
- {
- coverage_t coverage;
- memset (&coverage, 0, sizeof (coverage));
- coverage.name = flag_demangled_names ? fn->demangled_name : fn->name;
- add_line_counts (flag_function_summary ? &coverage : NULL, fn);
- if (flag_function_summary)
- {
- function_summary (&coverage, "Function");
- fnotice (stdout, "\n");
- }
- }
- if (file_name)
- {
- name_map_t *name_map = (name_map_t *)bsearch
- (file_name, names, n_names, sizeof (*names), name_search);
- if (name_map)
- file_name = sources[name_map->src].coverage.name;
- else
- file_name = canonicalize_name (file_name);
- }
- if (flag_gcov_file && flag_intermediate_format)
- {
- /* Open the intermediate file. */
- gcov_intermediate_filename =
- get_gcov_intermediate_filename (file_name);
- gcov_intermediate_file = fopen (gcov_intermediate_filename, "w");
- if (!gcov_intermediate_file)
- {
- fnotice (stderr, "Cannot open intermediate output file %s\n",
- gcov_intermediate_filename);
- return;
- }
- }
- for (ix = n_sources, src = sources; ix--; src++)
- {
- if (flag_relative_only)
- {
- /* Ignore this source, if it is an absolute path (after
- source prefix removal). */
- char first = src->coverage.name[0];
-
- #if HAVE_DOS_BASED_FILE_SYSTEM
- if (first && src->coverage.name[1] == ':')
- first = src->coverage.name[2];
- #endif
- if (IS_DIR_SEPARATOR (first))
- continue;
- }
-
- accumulate_line_counts (src);
- function_summary (&src->coverage, "File");
- total_lines += src->coverage.lines;
- total_executed += src->coverage.lines_executed;
- if (flag_gcov_file)
- {
- if (flag_intermediate_format)
- /* Output the intermediate format without requiring source
- files. This outputs a section to a *single* file. */
- output_intermediate_file (gcov_intermediate_file, src);
- else
- output_gcov_file (file_name, src);
- fnotice (stdout, "\n");
- }
- }
- if (flag_gcov_file && flag_intermediate_format)
- {
- /* Now we've finished writing the intermediate file. */
- fclose (gcov_intermediate_file);
- XDELETEVEC (gcov_intermediate_filename);
- }
- if (!file_name)
- executed_summary (total_lines, total_executed);
- }
- /* Release a function structure */
- static void
- release_function (function_t *fn)
- {
- unsigned ix;
- block_t *block;
- for (ix = fn->num_blocks, block = fn->blocks; ix--; block++)
- {
- arc_t *arc, *arc_n;
- for (arc = block->succ; arc; arc = arc_n)
- {
- arc_n = arc->succ_next;
- free (arc);
- }
- }
- free (fn->blocks);
- free (fn->counts);
- if (flag_demangled_names && fn->demangled_name != fn->name)
- free (fn->demangled_name);
- free (fn->name);
- }
- /* Release all memory used. */
- static void
- release_structures (void)
- {
- unsigned ix;
- function_t *fn;
- for (ix = n_sources; ix--;)
- free (sources[ix].lines);
- free (sources);
-
- for (ix = n_names; ix--;)
- free (names[ix].name);
- free (names);
- while ((fn = functions))
- {
- functions = fn->next;
- release_function (fn);
- }
- }
- /* Generate the names of the graph and data files. If OBJECT_DIRECTORY
- is not specified, these are named from FILE_NAME sans extension. If
- OBJECT_DIRECTORY is specified and is a directory, the files are in that
- directory, but named from the basename of the FILE_NAME, sans extension.
- Otherwise OBJECT_DIRECTORY is taken to be the name of the object *file*
- and the data files are named from that. */
- static void
- create_file_names (const char *file_name)
- {
- char *cptr;
- char *name;
- int length = strlen (file_name);
- int base;
- /* Free previous file names. */
- free (bbg_file_name);
- free (da_file_name);
- da_file_name = bbg_file_name = NULL;
- bbg_file_time = 0;
- bbg_stamp = 0;
- if (object_directory && object_directory[0])
- {
- struct stat status;
- length += strlen (object_directory) + 2;
- name = XNEWVEC (char, length);
- name[0] = 0;
- base = !stat (object_directory, &status) && S_ISDIR (status.st_mode);
- strcat (name, object_directory);
- if (base && (! IS_DIR_SEPARATOR (name[strlen (name) - 1])))
- strcat (name, "/");
- }
- else
- {
- name = XNEWVEC (char, length + 1);
- strcpy (name, file_name);
- base = 0;
- }
- if (base)
- {
- /* Append source file name. */
- const char *cptr = lbasename (file_name);
- strcat (name, cptr ? cptr : file_name);
- }
- /* Remove the extension. */
- cptr = strrchr (CONST_CAST (char *, lbasename (name)), '.');
- if (cptr)
- *cptr = 0;
- length = strlen (name);
- bbg_file_name = XNEWVEC (char, length + strlen (GCOV_NOTE_SUFFIX) + 1);
- strcpy (bbg_file_name, name);
- strcpy (bbg_file_name + length, GCOV_NOTE_SUFFIX);
- da_file_name = XNEWVEC (char, length + strlen (GCOV_DATA_SUFFIX) + 1);
- strcpy (da_file_name, name);
- strcpy (da_file_name + length, GCOV_DATA_SUFFIX);
- free (name);
- return;
- }
- /* A is a string and B is a pointer to name_map_t. Compare for file
- name orderability. */
- static int
- name_search (const void *a_, const void *b_)
- {
- const char *a = (const char *)a_;
- const name_map_t *b = (const name_map_t *)b_;
- #if HAVE_DOS_BASED_FILE_SYSTEM
- return strcasecmp (a, b->name);
- #else
- return strcmp (a, b->name);
- #endif
- }
- /* A and B are a pointer to name_map_t. Compare for file name
- orderability. */
- static int
- name_sort (const void *a_, const void *b_)
- {
- const name_map_t *a = (const name_map_t *)a_;
- return name_search (a->name, b_);
- }
- /* Find or create a source file structure for FILE_NAME. Copies
- FILE_NAME on creation */
- static unsigned
- find_source (const char *file_name)
- {
- name_map_t *name_map;
- char *canon;
- unsigned idx;
- struct stat status;
- if (!file_name)
- file_name = "<unknown>";
- name_map = (name_map_t *)bsearch
- (file_name, names, n_names, sizeof (*names), name_search);
- if (name_map)
- {
- idx = name_map->src;
- goto check_date;
- }
- if (n_names + 2 > a_names)
- {
- /* Extend the name map array -- we'll be inserting one or two
- entries. */
- a_names *= 2;
- name_map = XNEWVEC (name_map_t, a_names);
- memcpy (name_map, names, n_names * sizeof (*names));
- free (names);
- names = name_map;
- }
-
- /* Not found, try the canonical name. */
- canon = canonicalize_name (file_name);
- name_map = (name_map_t *)bsearch
- (canon, names, n_names, sizeof (*names), name_search);
- if (!name_map)
- {
- /* Not found with canonical name, create a new source. */
- source_t *src;
-
- if (n_sources == a_sources)
- {
- a_sources *= 2;
- src = XNEWVEC (source_t, a_sources);
- memcpy (src, sources, n_sources * sizeof (*sources));
- free (sources);
- sources = src;
- }
- idx = n_sources;
- name_map = &names[n_names++];
- name_map->name = canon;
- name_map->src = idx;
- src = &sources[n_sources++];
- memset (src, 0, sizeof (*src));
- src->name = canon;
- src->coverage.name = src->name;
- if (source_length
- #if HAVE_DOS_BASED_FILE_SYSTEM
- /* You lose if separators don't match exactly in the
- prefix. */
- && !strncasecmp (source_prefix, src->coverage.name, source_length)
- #else
- && !strncmp (source_prefix, src->coverage.name, source_length)
- #endif
- && IS_DIR_SEPARATOR (src->coverage.name[source_length]))
- src->coverage.name += source_length + 1;
- if (!stat (src->name, &status))
- src->file_time = status.st_mtime;
- }
- else
- idx = name_map->src;
- if (name_search (file_name, name_map))
- {
- /* Append the non-canonical name. */
- name_map = &names[n_names++];
- name_map->name = xstrdup (file_name);
- name_map->src = idx;
- }
- /* Resort the name map. */
- qsort (names, n_names, sizeof (*names), name_sort);
-
- check_date:
- if (sources[idx].file_time > bbg_file_time)
- {
- static int info_emitted;
- fnotice (stderr, "%s:source file is newer than notes file '%s'\n",
- file_name, bbg_file_name);
- if (!info_emitted)
- {
- fnotice (stderr,
- "(the message is displayed only once per source file)\n");
- info_emitted = 1;
- }
- sources[idx].file_time = 0;
- }
- return idx;
- }
- /* Read the notes file. Return list of functions read -- in reverse order. */
- static function_t *
- read_graph_file (void)
- {
- unsigned version;
- unsigned current_tag = 0;
- function_t *fn = NULL;
- function_t *fns = NULL;
- function_t **fns_end = &fns;
- unsigned src_idx = 0;
- unsigned ix;
- unsigned tag;
- if (!gcov_open (bbg_file_name, 1))
- {
- fnotice (stderr, "%s:cannot open notes file\n", bbg_file_name);
- return fns;
- }
- bbg_file_time = gcov_time ();
- if (!gcov_magic (gcov_read_unsigned (), GCOV_NOTE_MAGIC))
- {
- fnotice (stderr, "%s:not a gcov notes file\n", bbg_file_name);
- gcov_close ();
- return fns;
- }
- version = gcov_read_unsigned ();
- if (version != GCOV_VERSION)
- {
- char v[4], e[4];
- GCOV_UNSIGNED2STRING (v, version);
- GCOV_UNSIGNED2STRING (e, GCOV_VERSION);
- fnotice (stderr, "%s:version '%.4s', prefer '%.4s'\n",
- bbg_file_name, v, e);
- }
- bbg_stamp = gcov_read_unsigned ();
- while ((tag = gcov_read_unsigned ()))
- {
- unsigned length = gcov_read_unsigned ();
- gcov_position_t base = gcov_position ();
- if (tag == GCOV_TAG_FUNCTION)
- {
- char *function_name;
- unsigned ident, lineno;
- unsigned lineno_checksum, cfg_checksum;
- ident = gcov_read_unsigned ();
- lineno_checksum = gcov_read_unsigned ();
- cfg_checksum = gcov_read_unsigned ();
- function_name = xstrdup (gcov_read_string ());
- src_idx = find_source (gcov_read_string ());
- lineno = gcov_read_unsigned ();
- fn = XCNEW (function_t);
- fn->name = function_name;
- if (flag_demangled_names)
- {
- fn->demangled_name = cplus_demangle (fn->name, DMGL_PARAMS);
- if (!fn->demangled_name)
- fn->demangled_name = fn->name;
- }
- fn->ident = ident;
- fn->lineno_checksum = lineno_checksum;
- fn->cfg_checksum = cfg_checksum;
- fn->src = src_idx;
- fn->line = lineno;
- fn->line_next = NULL;
- fn->next = NULL;
- *fns_end = fn;
- fns_end = &fn->next;
- current_tag = tag;
- }
- else if (fn && tag == GCOV_TAG_BLOCKS)
- {
- if (fn->blocks)
- fnotice (stderr, "%s:already seen blocks for '%s'\n",
- bbg_file_name, fn->name);
- else
- {
- unsigned ix, num_blocks = GCOV_TAG_BLOCKS_NUM (length);
- fn->num_blocks = num_blocks;
- fn->blocks = XCNEWVEC (block_t, fn->num_blocks);
- for (ix = 0; ix != num_blocks; ix++)
- fn->blocks[ix].flags = gcov_read_unsigned ();
- }
- }
- else if (fn && tag == GCOV_TAG_ARCS)
- {
- unsigned src = gcov_read_unsigned ();
- unsigned num_dests = GCOV_TAG_ARCS_NUM (length);
- block_t *src_blk = &fn->blocks[src];
- unsigned mark_catches = 0;
- struct arc_info *arc;
- if (src >= fn->num_blocks || fn->blocks[src].succ)
- goto corrupt;
- while (num_dests--)
- {
- unsigned dest = gcov_read_unsigned ();
- unsigned flags = gcov_read_unsigned ();
- if (dest >= fn->num_blocks)
- goto corrupt;
- arc = XCNEW (arc_t);
- arc->dst = &fn->blocks[dest];
- arc->src = src_blk;
- arc->count = 0;
- arc->count_valid = 0;
- arc->on_tree = !!(flags & GCOV_ARC_ON_TREE);
- arc->fake = !!(flags & GCOV_ARC_FAKE);
- arc->fall_through = !!(flags & GCOV_ARC_FALLTHROUGH);
- arc->succ_next = src_blk->succ;
- src_blk->succ = arc;
- src_blk->num_succ++;
- arc->pred_next = fn->blocks[dest].pred;
- fn->blocks[dest].pred = arc;
- fn->blocks[dest].num_pred++;
- if (arc->fake)
- {
- if (src)
- {
- /* Exceptional exit from this function, the
- source block must be a call. */
- fn->blocks[src].is_call_site = 1;
- arc->is_call_non_return = 1;
- mark_catches = 1;
- }
- else
- {
- /* Non-local return from a callee of this
- function. The destination block is a setjmp. */
- arc->is_nonlocal_return = 1;
- fn->blocks[dest].is_nonlocal_return = 1;
- }
- }
- if (!arc->on_tree)
- fn->num_counts++;
- }
-
- if (mark_catches)
- {
- /* We have a fake exit from this block. The other
- non-fall through exits must be to catch handlers.
- Mark them as catch arcs. */
- for (arc = src_blk->succ; arc; arc = arc->succ_next)
- if (!arc->fake && !arc->fall_through)
- {
- arc->is_throw = 1;
- fn->has_catch = 1;
- }
- }
- }
- else if (fn && tag == GCOV_TAG_LINES)
- {
- unsigned blockno = gcov_read_unsigned ();
- unsigned *line_nos = XCNEWVEC (unsigned, length - 1);
- if (blockno >= fn->num_blocks || fn->blocks[blockno].u.line.encoding)
- goto corrupt;
- for (ix = 0; ; )
- {
- unsigned lineno = gcov_read_unsigned ();
- if (lineno)
- {
- if (!ix)
- {
- line_nos[ix++] = 0;
- line_nos[ix++] = src_idx;
- }
- line_nos[ix++] = lineno;
- }
- else
- {
- const char *file_name = gcov_read_string ();
- if (!file_name)
- break;
- src_idx = find_source (file_name);
- line_nos[ix++] = 0;
- line_nos[ix++] = src_idx;
- }
- }
- fn->blocks[blockno].u.line.encoding = line_nos;
- fn->blocks[blockno].u.line.num = ix;
- }
- else if (current_tag && !GCOV_TAG_IS_SUBTAG (current_tag, tag))
- {
- fn = NULL;
- current_tag = 0;
- }
- gcov_sync (base, length);
- if (gcov_is_error ())
- {
- corrupt:;
- fnotice (stderr, "%s:corrupted\n", bbg_file_name);
- break;
- }
- }
- gcov_close ();
- if (!fns)
- fnotice (stderr, "%s:no functions found\n", bbg_file_name);
- return fns;
- }
- /* Reads profiles from the count file and attach to each
- function. Return nonzero if fatal error. */
- static int
- read_count_file (function_t *fns)
- {
- unsigned ix;
- unsigned version;
- unsigned tag;
- function_t *fn = NULL;
- int error = 0;
- if (!gcov_open (da_file_name, 1))
- {
- fnotice (stderr, "%s:cannot open data file, assuming not executed\n",
- da_file_name);
- no_data_file = 1;
- return 0;
- }
- if (!gcov_magic (gcov_read_unsigned (), GCOV_DATA_MAGIC))
- {
- fnotice (stderr, "%s:not a gcov data file\n", da_file_name);
- cleanup:;
- gcov_close ();
- return 1;
- }
- version = gcov_read_unsigned ();
- if (version != GCOV_VERSION)
- {
- char v[4], e[4];
- GCOV_UNSIGNED2STRING (v, version);
- GCOV_UNSIGNED2STRING (e, GCOV_VERSION);
- fnotice (stderr, "%s:version '%.4s', prefer version '%.4s'\n",
- da_file_name, v, e);
- }
- tag = gcov_read_unsigned ();
- if (tag != bbg_stamp)
- {
- fnotice (stderr, "%s:stamp mismatch with notes file\n", da_file_name);
- goto cleanup;
- }
- while ((tag = gcov_read_unsigned ()))
- {
- unsigned length = gcov_read_unsigned ();
- unsigned long base = gcov_position ();
- if (tag == GCOV_TAG_PROGRAM_SUMMARY)
- {
- struct gcov_summary summary;
- gcov_read_summary (&summary);
- object_runs += summary.ctrs[GCOV_COUNTER_ARCS].runs;
- program_count++;
- }
- else if (tag == GCOV_TAG_FUNCTION && !length)
- ; /* placeholder */
- else if (tag == GCOV_TAG_FUNCTION && length == GCOV_TAG_FUNCTION_LENGTH)
- {
- unsigned ident;
- struct function_info *fn_n;
- /* Try to find the function in the list. To speed up the
- search, first start from the last function found. */
- ident = gcov_read_unsigned ();
- fn_n = fns;
- for (fn = fn ? fn->next : NULL; ; fn = fn->next)
- {
- if (fn)
- ;
- else if ((fn = fn_n))
- fn_n = NULL;
- else
- {
- fnotice (stderr, "%s:unknown function '%u'\n",
- da_file_name, ident);
- break;
- }
- if (fn->ident == ident)
- break;
- }
- if (!fn)
- ;
- else if (gcov_read_unsigned () != fn->lineno_checksum
- || gcov_read_unsigned () != fn->cfg_checksum)
- {
- mismatch:;
- fnotice (stderr, "%s:profile mismatch for '%s'\n",
- da_file_name, fn->name);
- goto cleanup;
- }
- }
- else if (tag == GCOV_TAG_FOR_COUNTER (GCOV_COUNTER_ARCS) && fn)
- {
- if (length != GCOV_TAG_COUNTER_LENGTH (fn->num_counts))
- goto mismatch;
- if (!fn->counts)
- fn->counts = XCNEWVEC (gcov_type, fn->num_counts);
- for (ix = 0; ix != fn->num_counts; ix++)
- fn->counts[ix] += gcov_read_counter ();
- }
- gcov_sync (base, length);
- if ((error = gcov_is_error ()))
- {
- fnotice (stderr, error < 0 ? "%s:overflowed\n" : "%s:corrupted\n",
- da_file_name);
- goto cleanup;
- }
- }
- gcov_close ();
- return 0;
- }
- /* Solve the flow graph. Propagate counts from the instrumented arcs
- to the blocks and the uninstrumented arcs. */
- static void
- solve_flow_graph (function_t *fn)
- {
- unsigned ix;
- arc_t *arc;
- gcov_type *count_ptr = fn->counts;
- block_t *blk;
- block_t *valid_blocks = NULL; /* valid, but unpropagated blocks. */
- block_t *invalid_blocks = NULL; /* invalid, but inferable blocks. */
- /* The arcs were built in reverse order. Fix that now. */
- for (ix = fn->num_blocks; ix--;)
- {
- arc_t *arc_p, *arc_n;
- for (arc_p = NULL, arc = fn->blocks[ix].succ; arc;
- arc_p = arc, arc = arc_n)
- {
- arc_n = arc->succ_next;
- arc->succ_next = arc_p;
- }
- fn->blocks[ix].succ = arc_p;
- for (arc_p = NULL, arc = fn->blocks[ix].pred; arc;
- arc_p = arc, arc = arc_n)
- {
- arc_n = arc->pred_next;
- arc->pred_next = arc_p;
- }
- fn->blocks[ix].pred = arc_p;
- }
- if (fn->num_blocks < 2)
- fnotice (stderr, "%s:'%s' lacks entry and/or exit blocks\n",
- bbg_file_name, fn->name);
- else
- {
- if (fn->blocks[ENTRY_BLOCK].num_pred)
- fnotice (stderr, "%s:'%s' has arcs to entry block\n",
- bbg_file_name, fn->name);
- else
- /* We can't deduce the entry block counts from the lack of
- predecessors. */
- fn->blocks[ENTRY_BLOCK].num_pred = ~(unsigned)0;
- if (fn->blocks[EXIT_BLOCK].num_succ)
- fnotice (stderr, "%s:'%s' has arcs from exit block\n",
- bbg_file_name, fn->name);
- else
- /* Likewise, we can't deduce exit block counts from the lack
- of its successors. */
- fn->blocks[EXIT_BLOCK].num_succ = ~(unsigned)0;
- }
- /* Propagate the measured counts, this must be done in the same
- order as the code in profile.c */
- for (ix = 0, blk = fn->blocks; ix != fn->num_blocks; ix++, blk++)
- {
- block_t const *prev_dst = NULL;
- int out_of_order = 0;
- int non_fake_succ = 0;
- for (arc = blk->succ; arc; arc = arc->succ_next)
- {
- if (!arc->fake)
- non_fake_succ++;
- if (!arc->on_tree)
- {
- if (count_ptr)
- arc->count = *count_ptr++;
- arc->count_valid = 1;
- blk->num_succ--;
- arc->dst->num_pred--;
- }
- if (prev_dst && prev_dst > arc->dst)
- out_of_order = 1;
- prev_dst = arc->dst;
- }
- if (non_fake_succ == 1)
- {
- /* If there is only one non-fake exit, it is an
- unconditional branch. */
- for (arc = blk->succ; arc; arc = arc->succ_next)
- if (!arc->fake)
- {
- arc->is_unconditional = 1;
- /* If this block is instrumenting a call, it might be
- an artificial block. It is not artificial if it has
- a non-fallthrough exit, or the destination of this
- arc has more than one entry. Mark the destination
- block as a return site, if none of those conditions
- hold. */
- if (blk->is_call_site && arc->fall_through
- && arc->dst->pred == arc && !arc->pred_next)
- arc->dst->is_call_return = 1;
- }
- }
- /* Sort the successor arcs into ascending dst order. profile.c
- normally produces arcs in the right order, but sometimes with
- one or two out of order. We're not using a particularly
- smart sort. */
- if (out_of_order)
- {
- arc_t *start = blk->succ;
- unsigned changes = 1;
- while (changes)
- {
- arc_t *arc, *arc_p, *arc_n;
- changes = 0;
- for (arc_p = NULL, arc = start; (arc_n = arc->succ_next);)
- {
- if (arc->dst > arc_n->dst)
- {
- changes = 1;
- if (arc_p)
- arc_p->succ_next = arc_n;
- else
- start = arc_n;
- arc->succ_next = arc_n->succ_next;
- arc_n->succ_next = arc;
- arc_p = arc_n;
- }
- else
- {
- arc_p = arc;
- arc = arc_n;
- }
- }
- }
- blk->succ = start;
- }
- /* Place it on the invalid chain, it will be ignored if that's
- wrong. */
- blk->invalid_chain = 1;
- blk->chain = invalid_blocks;
- invalid_blocks = blk;
- }
- while (invalid_blocks || valid_blocks)
- {
- while ((blk = invalid_blocks))
- {
- gcov_type total = 0;
- const arc_t *arc;
- invalid_blocks = blk->chain;
- blk->invalid_chain = 0;
- if (!blk->num_succ)
- for (arc = blk->succ; arc; arc = arc->succ_next)
- total += arc->count;
- else if (!blk->num_pred)
- for (arc = blk->pred; arc; arc = arc->pred_next)
- total += arc->count;
- else
- continue;
- blk->count = total;
- blk->count_valid = 1;
- blk->chain = valid_blocks;
- blk->valid_chain = 1;
- valid_blocks = blk;
- }
- while ((blk = valid_blocks))
- {
- gcov_type total;
- arc_t *arc, *inv_arc;
- valid_blocks = blk->chain;
- blk->valid_chain = 0;
- if (blk->num_succ == 1)
- {
- block_t *dst;
- total = blk->count;
- inv_arc = NULL;
- for (arc = blk->succ; arc; arc = arc->succ_next)
- {
- total -= arc->count;
- if (!arc->count_valid)
- inv_arc = arc;
- }
- dst = inv_arc->dst;
- inv_arc->count_valid = 1;
- inv_arc->count = total;
- blk->num_succ--;
- dst->num_pred--;
- if (dst->count_valid)
- {
- if (dst->num_pred == 1 && !dst->valid_chain)
- {
- dst->chain = valid_blocks;
- dst->valid_chain = 1;
- valid_blocks = dst;
- }
- }
- else
- {
- if (!dst->num_pred && !dst->invalid_chain)
- {
- dst->chain = invalid_blocks;
- dst->invalid_chain = 1;
- invalid_blocks = dst;
- }
- }
- }
- if (blk->num_pred == 1)
- {
- block_t *src;
- total = blk->count;
- inv_arc = NULL;
- for (arc = blk->pred; arc; arc = arc->pred_next)
- {
- total -= arc->count;
- if (!arc->count_valid)
- inv_arc = arc;
- }
- src = inv_arc->src;
- inv_arc->count_valid = 1;
- inv_arc->count = total;
- blk->num_pred--;
- src->num_succ--;
- if (src->count_valid)
- {
- if (src->num_succ == 1 && !src->valid_chain)
- {
- src->chain = valid_blocks;
- src->valid_chain = 1;
- valid_blocks = src;
- }
- }
- else
- {
- if (!src->num_succ && !src->invalid_chain)
- {
- src->chain = invalid_blocks;
- src->invalid_chain = 1;
- invalid_blocks = src;
- }
- }
- }
- }
- }
- /* If the graph has been correctly solved, every block will have a
- valid count. */
- for (ix = 0; ix < fn->num_blocks; ix++)
- if (!fn->blocks[ix].count_valid)
- {
- fnotice (stderr, "%s:graph is unsolvable for '%s'\n",
- bbg_file_name, fn->name);
- break;
- }
- }
- /* Mark all the blocks only reachable via an incoming catch. */
- static void
- find_exception_blocks (function_t *fn)
- {
- unsigned ix;
- block_t **queue = XALLOCAVEC (block_t *, fn->num_blocks);
- /* First mark all blocks as exceptional. */
- for (ix = fn->num_blocks; ix--;)
- fn->blocks[ix].exceptional = 1;
- /* Now mark all the blocks reachable via non-fake edges */
- queue[0] = fn->blocks;
- queue[0]->exceptional = 0;
- for (ix = 1; ix;)
- {
- block_t *block = queue[--ix];
- const arc_t *arc;
-
- for (arc = block->succ; arc; arc = arc->succ_next)
- if (!arc->fake && !arc->is_throw && arc->dst->exceptional)
- {
- arc->dst->exceptional = 0;
- queue[ix++] = arc->dst;
- }
- }
- }
- /* Increment totals in COVERAGE according to arc ARC. */
- static void
- add_branch_counts (coverage_t *coverage, const arc_t *arc)
- {
- if (arc->is_call_non_return)
- {
- coverage->calls++;
- if (arc->src->count)
- coverage->calls_executed++;
- }
- else if (!arc->is_unconditional)
- {
- coverage->branches++;
- if (arc->src->count)
- coverage->branches_executed++;
- if (arc->count)
- coverage->branches_taken++;
- }
- }
- /* Format a GCOV_TYPE integer as either a percent ratio, or absolute
- count. If dp >= 0, format TOP/BOTTOM * 100 to DP decimal places.
- If DP is zero, no decimal point is printed. Only print 100% when
- TOP==BOTTOM and only print 0% when TOP=0. If dp < 0, then simply
- format TOP. Return pointer to a static string. */
- static char const *
- format_gcov (gcov_type top, gcov_type bottom, int dp)
- {
- static char buffer[20];
- if (dp >= 0)
- {
- float ratio = bottom ? (float)top / bottom : 0;
- int ix;
- unsigned limit = 100;
- unsigned percent;
- for (ix = dp; ix--; )
- limit *= 10;
- percent = (unsigned) (ratio * limit + (float)0.5);
- if (percent <= 0 && top)
- percent = 1;
- else if (percent >= limit && top != bottom)
- percent = limit - 1;
- ix = sprintf (buffer, "%.*u%%", dp + 1, percent);
- if (dp)
- {
- dp++;
- do
- {
- buffer[ix+1] = buffer[ix];
- ix--;
- }
- while (dp--);
- buffer[ix + 1] = '.';
- }
- }
- else
- sprintf (buffer, "%"PRId64, (int64_t)top);
- return buffer;
- }
- /* Summary of execution */
- static void
- executed_summary (unsigned lines, unsigned executed)
- {
- if (lines)
- fnotice (stdout, "Lines executed:%s of %d\n",
- format_gcov (executed, lines, 2), lines);
- else
- fnotice (stdout, "No executable lines\n");
- }
-
- /* Output summary info for a function or file. */
- static void
- function_summary (const coverage_t *coverage, const char *title)
- {
- fnotice (stdout, "%s '%s'\n", title, coverage->name);
- executed_summary (coverage->lines, coverage->lines_executed);
- if (flag_branches)
- {
- if (coverage->branches)
- {
- fnotice (stdout, "Branches executed:%s of %d\n",
- format_gcov (coverage->branches_executed,
- coverage->branches, 2),
- coverage->branches);
- fnotice (stdout, "Taken at least once:%s of %d\n",
- format_gcov (coverage->branches_taken,
- coverage->branches, 2),
- coverage->branches);
- }
- else
- fnotice (stdout, "No branches\n");
- if (coverage->calls)
- fnotice (stdout, "Calls executed:%s of %d\n",
- format_gcov (coverage->calls_executed, coverage->calls, 2),
- coverage->calls);
- else
- fnotice (stdout, "No calls\n");
- }
- }
- /* Canonicalize the filename NAME by canonicalizing directory
- separators, eliding . components and resolving .. components
- appropriately. Always returns a unique string. */
- static char *
- canonicalize_name (const char *name)
- {
- /* The canonical name cannot be longer than the incoming name. */
- char *result = XNEWVEC (char, strlen (name) + 1);
- const char *base = name, *probe;
- char *ptr = result;
- char *dd_base;
- int slash = 0;
- #if HAVE_DOS_BASED_FILE_SYSTEM
- if (base[0] && base[1] == ':')
- {
- result[0] = base[0];
- result[1] = ':';
- base += 2;
- ptr += 2;
- }
- #endif
- for (dd_base = ptr; *base; base = probe)
- {
- size_t len;
-
- for (probe = base; *probe; probe++)
- if (IS_DIR_SEPARATOR (*probe))
- break;
- len = probe - base;
- if (len == 1 && base[0] == '.')
- /* Elide a '.' directory */
- ;
- else if (len == 2 && base[0] == '.' && base[1] == '.')
- {
- /* '..', we can only elide it and the previous directory, if
- we're not a symlink. */
- struct stat ATTRIBUTE_UNUSED buf;
- *ptr = 0;
- if (dd_base == ptr
- #if defined (S_ISLNK)
- /* S_ISLNK is not POSIX.1-1996. */
- || stat (result, &buf) || S_ISLNK (buf.st_mode)
- #endif
- )
- {
- /* Cannot elide, or unreadable or a symlink. */
- dd_base = ptr + 2 + slash;
- goto regular;
- }
- while (ptr != dd_base && *ptr != '/')
- ptr--;
- slash = ptr != result;
- }
- else
- {
- regular:
- /* Regular pathname component. */
- if (slash)
- *ptr++ = '/';
- memcpy (ptr, base, len);
- ptr += len;
- slash = 1;
- }
- for (; IS_DIR_SEPARATOR (*probe); probe++)
- continue;
- }
- *ptr = 0;
- return result;
- }
- /* Generate an output file name. INPUT_NAME is the canonicalized main
- input file and SRC_NAME is the canonicalized file name.
- LONG_OUTPUT_NAMES and PRESERVE_PATHS affect name generation. With
- long_output_names we prepend the processed name of the input file
- to each output name (except when the current source file is the
- input file, so you don't get a double concatenation). The two
- components are separated by '##'. With preserve_paths we create a
- filename from all path components of the source file, replacing '/'
- with '#', and .. with '^', without it we simply take the basename
- component. (Remember, the canonicalized name will already have
- elided '.' components and converted \\ separators.) */
- static char *
- make_gcov_file_name (const char *input_name, const char *src_name)
- {
- char *ptr;
- char *result;
- if (flag_long_names && input_name && strcmp (src_name, input_name))
- {
- /* Generate the input filename part. */
- result = XNEWVEC (char, strlen (input_name) + strlen (src_name) + 10);
-
- ptr = result;
- ptr = mangle_name (input_name, ptr);
- ptr[0] = ptr[1] = '#';
- ptr += 2;
- }
- else
- {
- result = XNEWVEC (char, strlen (src_name) + 10);
- ptr = result;
- }
- ptr = mangle_name (src_name, ptr);
- strcpy (ptr, ".gcov");
-
- return result;
- }
- static char *
- mangle_name (char const *base, char *ptr)
- {
- size_t len;
-
- /* Generate the source filename part. */
- if (!flag_preserve_paths)
- {
- base = lbasename (base);
- len = strlen (base);
- memcpy (ptr, base, len);
- ptr += len;
- }
- else
- {
- /* Convert '/' to '#', convert '..' to '^',
- convert ':' to '~' on DOS based file system. */
- const char *probe;
- #if HAVE_DOS_BASED_FILE_SYSTEM
- if (base[0] && base[1] == ':')
- {
- ptr[0] = base[0];
- ptr[1] = '~';
- ptr += 2;
- base += 2;
- }
- #endif
- for (; *base; base = probe)
- {
- size_t len;
- for (probe = base; *probe; probe++)
- if (*probe == '/')
- break;
- len = probe - base;
- if (len == 2 && base[0] == '.' && base[1] == '.')
- *ptr++ = '^';
- else
- {
- memcpy (ptr, base, len);
- ptr += len;
- }
- if (*probe)
- {
- *ptr++ = '#';
- probe++;
- }
- }
- }
-
- return ptr;
- }
- /* Scan through the bb_data for each line in the block, increment
- the line number execution count indicated by the execution count of
- the appropriate basic block. */
- static void
- add_line_counts (coverage_t *coverage, function_t *fn)
- {
- unsigned ix;
- line_t *line = NULL; /* This is propagated from one iteration to the
- next. */
- /* Scan each basic block. */
- for (ix = 0; ix != fn->num_blocks; ix++)
- {
- block_t *block = &fn->blocks[ix];
- unsigned *encoding;
- const source_t *src = NULL;
- unsigned jx;
- if (block->count && ix && ix + 1 != fn->num_blocks)
- fn->blocks_executed++;
- for (jx = 0, encoding = block->u.line.encoding;
- jx != block->u.line.num; jx++, encoding++)
- if (!*encoding)
- {
- src = &sources[*++encoding];
- jx++;
- }
- else
- {
- line = &src->lines[*encoding];
- if (coverage)
- {
- if (!line->exists)
- coverage->lines++;
- if (!line->count && block->count)
- coverage->lines_executed++;
- }
- line->exists = 1;
- if (!block->exceptional)
- line->unexceptional = 1;
- line->count += block->count;
- }
- free (block->u.line.encoding);
- block->u.cycle.arc = NULL;
- block->u.cycle.ident = ~0U;
- if (!ix || ix + 1 == fn->num_blocks)
- /* Entry or exit block */;
- else if (flag_all_blocks)
- {
- line_t *block_line = line;
- if (!block_line)
- block_line = &sources[fn->src].lines[fn->line];
- block->chain = block_line->u.blocks;
- block_line->u.blocks = block;
- }
- else if (flag_branches)
- {
- arc_t *arc;
- for (arc = block->succ; arc; arc = arc->succ_next)
- {
- arc->line_next = line->u.branches;
- line->u.branches = arc;
- if (coverage && !arc->is_unconditional)
- add_branch_counts (coverage, arc);
- }
- }
- }
- if (!line)
- fnotice (stderr, "%s:no lines for '%s'\n", bbg_file_name, fn->name);
- }
- /* Accumulate the line counts of a file. */
- static void
- accumulate_line_counts (source_t *src)
- {
- line_t *line;
- function_t *fn, *fn_p, *fn_n;
- unsigned ix;
- /* Reverse the function order. */
- for (fn = src->functions, fn_p = NULL; fn;
- fn_p = fn, fn = fn_n)
- {
- fn_n = fn->line_next;
- fn->line_next = fn_p;
- }
- src->functions = fn_p;
- for (ix = src->num_lines, line = src->lines; ix--; line++)
- {
- if (!flag_all_blocks)
- {
- arc_t *arc, *arc_p, *arc_n;
- /* Total and reverse the branch information. */
- for (arc = line->u.branches, arc_p = NULL; arc;
- arc_p = arc, arc = arc_n)
- {
- arc_n = arc->line_next;
- arc->line_next = arc_p;
- add_branch_counts (&src->coverage, arc);
- }
- line->u.branches = arc_p;
- }
- else if (line->u.blocks)
- {
- /* The user expects the line count to be the number of times
- a line has been executed. Simply summing the block count
- will give an artificially high number. The Right Thing
- is to sum the entry counts to the graph of blocks on this
- line, then find the elementary cycles of the local graph
- and add the transition counts of those cycles. */
- block_t *block, *block_p, *block_n;
- gcov_type count = 0;
- /* Reverse the block information. */
- for (block = line->u.blocks, block_p = NULL; block;
- block_p = block, block = block_n)
- {
- block_n = block->chain;
- block->chain = block_p;
- block->u.cycle.ident = ix;
- }
- line->u.blocks = block_p;
- /* Sum the entry arcs. */
- for (block = line->u.blocks; block; block = block->chain)
- {
- arc_t *arc;
- for (arc = block->pred; arc; arc = arc->pred_next)
- {
- if (arc->src->u.cycle.ident != ix)
- count += arc->count;
- if (flag_branches)
- add_branch_counts (&src->coverage, arc);
- }
- /* Initialize the cs_count. */
- for (arc = block->succ; arc; arc = arc->succ_next)
- arc->cs_count = arc->count;
- }
- /* Find the loops. This uses the algorithm described in
- Tiernan 'An Efficient Search Algorithm to Find the
- Elementary Circuits of a Graph', CACM Dec 1970. We hold
- the P array by having each block point to the arc that
- connects to the previous block. The H array is implicitly
- held because of the arc ordering, and the block's
- previous arc pointer.
- Although the algorithm is O(N^3) for highly connected
- graphs, at worst we'll have O(N^2), as most blocks have
- only one or two exits. Most graphs will be small.
- For each loop we find, locate the arc with the smallest
- transition count, and add that to the cumulative
- count. Decrease flow over the cycle and remove the arc
- from consideration. */
- for (block = line->u.blocks; block; block = block->chain)
- {
- block_t *head = block;
- arc_t *arc;
- next_vertex:;
- arc = head->succ;
- current_vertex:;
- while (arc)
- {
- block_t *dst = arc->dst;
- if (/* Already used that arc. */
- arc->cycle
- /* Not to same graph, or before first vertex. */
- || dst->u.cycle.ident != ix
- /* Already in path. */
- || dst->u.cycle.arc)
- {
- arc = arc->succ_next;
- continue;
- }
- if (dst == block)
- {
- /* Found a closing arc. */
- gcov_type cycle_count = arc->cs_count;
- arc_t *cycle_arc = arc;
- arc_t *probe_arc;
- /* Locate the smallest arc count of the loop. */
- for (dst = head; (probe_arc = dst->u.cycle.arc);
- dst = probe_arc->src)
- if (cycle_count > probe_arc->cs_count)
- {
- cycle_count = probe_arc->cs_count;
- cycle_arc = probe_arc;
- }
- count += cycle_count;
- cycle_arc->cycle = 1;
- /* Remove the flow from the cycle. */
- arc->cs_count -= cycle_count;
- for (dst = head; (probe_arc = dst->u.cycle.arc);
- dst = probe_arc->src)
- probe_arc->cs_count -= cycle_count;
- /* Unwind to the cyclic arc. */
- while (head != cycle_arc->src)
- {
- arc = head->u.cycle.arc;
- head->u.cycle.arc = NULL;
- head = arc->src;
- }
- /* Move on. */
- arc = arc->succ_next;
- continue;
- }
- /* Add new block to chain. */
- dst->u.cycle.arc = arc;
- head = dst;
- goto next_vertex;
- }
- /* We could not add another vertex to the path. Remove
- the last vertex from the list. */
- arc = head->u.cycle.arc;
- if (arc)
- {
- /* It was not the first vertex. Move onto next arc. */
- head->u.cycle.arc = NULL;
- head = arc->src;
- arc = arc->succ_next;
- goto current_vertex;
- }
- /* Mark this block as unusable. */
- block->u.cycle.ident = ~0U;
- }
- line->count = count;
- }
- if (line->exists)
- {
- src->coverage.lines++;
- if (line->count)
- src->coverage.lines_executed++;
- }
- }
- }
- /* Output information about ARC number IX. Returns nonzero if
- anything is output. */
- static int
- output_branch_count (FILE *gcov_file, int ix, const arc_t *arc)
- {
- if (arc->is_call_non_return)
- {
- if (arc->src->count)
- {
- fnotice (gcov_file, "call %2d returned %s\n", ix,
- format_gcov (arc->src->count - arc->count,
- arc->src->count, -flag_counts));
- }
- else
- fnotice (gcov_file, "call %2d never executed\n", ix);
- }
- else if (!arc->is_unconditional)
- {
- if (arc->src->count)
- fnotice (gcov_file, "branch %2d taken %s%s\n", ix,
- format_gcov (arc->count, arc->src->count, -flag_counts),
- arc->fall_through ? " (fallthrough)"
- : arc->is_throw ? " (throw)" : "");
- else
- fnotice (gcov_file, "branch %2d never executed\n", ix);
- }
- else if (flag_unconditional && !arc->dst->is_call_return)
- {
- if (arc->src->count)
- fnotice (gcov_file, "unconditional %2d taken %s\n", ix,
- format_gcov (arc->count, arc->src->count, -flag_counts));
- else
- fnotice (gcov_file, "unconditional %2d never executed\n", ix);
- }
- else
- return 0;
- return 1;
- }
- static const char *
- read_line (FILE *file)
- {
- static char *string;
- static size_t string_len;
- size_t pos = 0;
- char *ptr;
- if (!string_len)
- {
- string_len = 200;
- string = XNEWVEC (char, string_len);
- }
- while ((ptr = fgets (string + pos, string_len - pos, file)))
- {
- size_t len = strlen (string + pos);
- if (string[pos + len - 1] == '\n')
- {
- string[pos + len - 1] = 0;
- return string;
- }
- pos += len;
- string = XRESIZEVEC (char, string, string_len * 2);
- string_len *= 2;
- }
-
- return pos ? string : NULL;
- }
- /* Read in the source file one line at a time, and output that line to
- the gcov file preceded by its execution count and other
- information. */
- static void
- output_lines (FILE *gcov_file, const source_t *src)
- {
- FILE *source_file;
- unsigned line_num; /* current line number. */
- const line_t *line; /* current line info ptr. */
- const char *retval = ""; /* status of source file reading. */
- function_t *fn = NULL;
- fprintf (gcov_file, "%9s:%5d:Source:%s\n", "-", 0, src->coverage.name);
- if (!multiple_files)
- {
- fprintf (gcov_file, "%9s:%5d:Graph:%s\n", "-", 0, bbg_file_name);
- fprintf (gcov_file, "%9s:%5d:Data:%s\n", "-", 0,
- no_data_file ? "-" : da_file_name);
- fprintf (gcov_file, "%9s:%5d:Runs:%u\n", "-", 0, object_runs);
- }
- fprintf (gcov_file, "%9s:%5d:Programs:%u\n", "-", 0, program_count);
- source_file = fopen (src->name, "r");
- if (!source_file)
- {
- fnotice (stderr, "Cannot open source file %s\n", src->name);
- retval = NULL;
- }
- else if (src->file_time == 0)
- fprintf (gcov_file, "%9s:%5d:Source is newer than graph\n", "-", 0);
- if (flag_branches)
- fn = src->functions;
- for (line_num = 1, line = &src->lines[line_num];
- line_num < src->num_lines; line_num++, line++)
- {
- for (; fn && fn->line == line_num; fn = fn->line_next)
- {
- arc_t *arc = fn->blocks[EXIT_BLOCK].pred;
- gcov_type return_count = fn->blocks[EXIT_BLOCK].count;
- gcov_type called_count = fn->blocks[ENTRY_BLOCK].count;
- for (; arc; arc = arc->pred_next)
- if (arc->fake)
- return_count -= arc->count;
- fprintf (gcov_file, "function %s", flag_demangled_names ?
- fn->demangled_name : fn->name);
- fprintf (gcov_file, " called %s",
- format_gcov (called_count, 0, -1));
- fprintf (gcov_file, " returned %s",
- format_gcov (return_count, called_count, 0));
- fprintf (gcov_file, " blocks executed %s",
- format_gcov (fn->blocks_executed, fn->num_blocks - 2, 0));
- fprintf (gcov_file, "\n");
- }
- if (retval)
- retval = read_line (source_file);
- /* For lines which don't exist in the .bb file, print '-' before
- the source line. For lines which exist but were never
- executed, print '#####' or '=====' before the source line.
- Otherwise, print the execution count before the source line.
- There are 16 spaces of indentation added before the source
- line so that tabs won't be messed up. */
- fprintf (gcov_file, "%9s:%5u:%s\n",
- !line->exists ? "-" : line->count
- ? format_gcov (line->count, 0, -1)
- : line->unexceptional ? "#####" : "=====", line_num,
- retval ? retval : "/*EOF*/");
- if (flag_all_blocks)
- {
- block_t *block;
- arc_t *arc;
- int ix, jx;
- for (ix = jx = 0, block = line->u.blocks; block;
- block = block->chain)
- {
- if (!block->is_call_return)
- fprintf (gcov_file, "%9s:%5u-block %2d\n",
- !line->exists ? "-" : block->count
- ? format_gcov (block->count, 0, -1)
- : block->exceptional ? "%%%%%" : "$$$$$",
- line_num, ix++);
- if (flag_branches)
- for (arc = block->succ; arc; arc = arc->succ_next)
- jx += output_branch_count (gcov_file, jx, arc);
- }
- }
- else if (flag_branches)
- {
- int ix;
- arc_t *arc;
- for (ix = 0, arc = line->u.branches; arc; arc = arc->line_next)
- ix += output_branch_count (gcov_file, ix, arc);
- }
- }
- /* Handle all remaining source lines. There may be lines after the
- last line of code. */
- if (retval)
- {
- for (; (retval = read_line (source_file)); line_num++)
- fprintf (gcov_file, "%9s:%5u:%s\n", "-", line_num, retval);
- }
- if (source_file)
- fclose (source_file);
- }
|