diff options
author | nathan <nathan@138bc75d-0d04-0410-961f-82ee72b054a4> | 2002-08-04 17:06:10 +0000 |
---|---|---|
committer | nathan <nathan@138bc75d-0d04-0410-961f-82ee72b054a4> | 2002-08-04 17:06:10 +0000 |
commit | 3112c36a1b78930ec37b5bcd140bb1b1d361c2c6 (patch) | |
tree | 72b01de3cc426fed85947ddbf231d59f0bb124a6 /gcc/gcov.c | |
parent | d5a2461c1a136c27c301e94bc1696243ac6479d4 (diff) | |
download | gcc-3112c36a1b78930ec37b5bcd140bb1b1d361c2c6.tar.gz |
* gcov.c (bb_file_time): New static variable.
(object_directory): May also be object file.
(preserve_paths): New static variable.
(print_usage): Adjust.
(options): Adjust.
(process_args): Adjust.
(open_files): Simplify. Cope when OBJECT_DIRECTORY is an object
file. Find modification date on bb file.
(read_profile): Don't rewind a NULL file.
(format_hwint): New static function.
(function_summary): Use format_hwint.
(output_data): SOURCE_FILE_NAME is never relative to
OBJECT_DIRECTORY. Use format_hwint. Adjust gcov file name
mangling. Adjust output format to make it more machine readable.
* doc/gcov.texi: Document & clarify semantics.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@56028 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc/gcov.c')
-rw-r--r-- | gcc/gcov.c | 559 |
1 files changed, 302 insertions, 257 deletions
diff --git a/gcc/gcov.c b/gcc/gcov.c index 01e1a1dd77e..9925e95999b 100644 --- a/gcc/gcov.c +++ b/gcc/gcov.c @@ -160,6 +160,10 @@ struct bb_info_list { static struct bb_info_list *bb_graph_list = 0; +/* Modification time of data files. */ + +static time_t bb_file_time; + /* Name and file pointer of the input file for the basic block graph. */ static char *bbg_file_name; @@ -212,11 +216,15 @@ static int output_long_names = 0; static int output_function_summary = 0; -/* Object directory file prefix. This is the directory where .bb and .bbg - files are looked for, if non-zero. */ +/* Object directory file prefix. This is the directory/file + where .bb and .bbg files are looked for, if non-zero. */ static char *object_directory = 0; +/* Preserve all pathname components. Needed when object files and + source files are in subdirectories. */ +static int preserve_paths = 0; + /* Output the number of times a branch was taken as opposed to the percentage of times it was taken. Turned on by the -c option */ @@ -238,6 +246,7 @@ static void solve_program_flow_graph PARAMS ((struct bb_info_list *)); static void calculate_branch_probs PARAMS ((struct bb_info_list *, int, struct arcdata **, int)); static void function_summary PARAMS ((void)); +static const char *format_hwint PARAMS ((HOST_WIDEST_INT, HOST_WIDEST_INT, int)); extern int main PARAMS ((int, char **)); @@ -304,7 +313,8 @@ print_usage (error_p) fnotice (file, " -l, --long-file-names Use long output file names for included\n\ source files\n"); fnotice (file, " -f, --function-summaries Output summaries for each function\n"); - fnotice (file, " -o, --object-directory OBJDIR Search for object files in OBJDIR\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, "\nFor bug reporting instructions, please see:\n%s.\n", GCCBUGURL); exit (status); @@ -332,7 +342,9 @@ static const struct option options[] = { "no-output", no_argument, NULL, 'n' }, { "long-file-names", no_argument, NULL, 'l' }, { "function-summaries", no_argument, NULL, 'f' }, - { "object-directory", required_argument, NULL, 'o' } + { "preserve-paths", no_argument, NULL, 'p' }, + { "object-directory", required_argument, NULL, 'o' }, + { "object-file", required_argument, NULL, 'o' }, }; /* Parse the command line. */ @@ -344,7 +356,7 @@ process_args (argc, argv) { int opt; - while ((opt = getopt_long (argc, argv, "hvbclnfo:", options, NULL)) != -1) + while ((opt = getopt_long (argc, argv, "hvbclnfo:p", options, NULL)) != -1) { switch (opt) { @@ -372,6 +384,9 @@ process_args (argc, argv) case 'o': object_directory = optarg; break; + case 'p': + preserve_paths = 1; + break; default: print_usage (true); /* print_usage will exit. */ @@ -385,77 +400,66 @@ process_args (argc, argv) } -/* Find and open the .bb, .da, and .bbg files. */ +/* Find and open the .bb, .da, and .bbg files. If OBJECT_DIRECTORY is + not specified, these are looked for in the current directory, and + named from the basename of the input_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 input_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 open_files () { - int count, objdir_count; char *cptr; - - /* Determine the names of the .bb, .bbg, and .da files. Strip off the - extension, if any, and append the new extensions. */ - count = strlen (input_file_name); - if (object_directory) - objdir_count = strlen (object_directory); - else - objdir_count = 0; - - da_file_name = xmalloc (count + objdir_count + 4); - bb_file_name = xmalloc (count + objdir_count + 4); - bbg_file_name = xmalloc (count + objdir_count + 5); - - if (object_directory) + char *name; + int length = strlen (input_file_name); + int base; + + if (object_directory && object_directory[0]) { - strcpy (da_file_name, object_directory); - strcpy (bb_file_name, object_directory); - strcpy (bbg_file_name, object_directory); - - if (object_directory[objdir_count - 1] != '/') - { - strcat (da_file_name, "/"); - strcat (bb_file_name, "/"); - strcat (bbg_file_name, "/"); - } - - cptr = strrchr (input_file_name, '/'); - if (cptr) - { - strcat (da_file_name, cptr + 1); - strcat (bb_file_name, cptr + 1); - strcat (bbg_file_name, cptr + 1); - } - else - { - strcat (da_file_name, input_file_name); - strcat (bb_file_name, input_file_name); - strcat (bbg_file_name, input_file_name); - } + struct stat status; + + length += strlen (object_directory) + 2; + name = xmalloc (length); + name[0] = 0; + + base = !stat (object_directory, &status) && S_ISDIR (status.st_mode); + strcat (name, object_directory); + if (base && name[strlen (name) - 1] != '/') + strcat (name, "/"); } else { - strcpy (da_file_name, input_file_name); - strcpy (bb_file_name, input_file_name); - strcpy (bbg_file_name, input_file_name); + name = xmalloc (length + 1); + name[0] = 0; + base = 1; } + + if (base) + { + /* Append source file name */ + cptr = strrchr (input_file_name, '/'); + cptr = cptr ? cptr + 1 : input_file_name; - cptr = strrchr (bb_file_name, '.'); - if (cptr) - strcpy (cptr, ".bb"); - else - strcat (bb_file_name, ".bb"); - - cptr = strrchr (da_file_name, '.'); - if (cptr) - strcpy (cptr, ".da"); - else - strcat (da_file_name, ".da"); - - cptr = strrchr (bbg_file_name, '.'); + strcat (name, cptr); + } + /* Remove the extension. */ + cptr = strrchr (name, '.'); if (cptr) - strcpy (cptr, ".bbg"); - else - strcat (bbg_file_name, ".bbg"); + *cptr = 0; + + length = strlen (name); + da_file_name = xmalloc (length + 4); + bb_file_name = xmalloc (length + 4); + bbg_file_name = xmalloc (length + 5); + + strcpy (da_file_name, name); + strcpy (bb_file_name, name); + strcpy (bbg_file_name, name); + strcpy (da_file_name + length, ".da"); + strcpy (bb_file_name + length, ".bb"); + strcpy (bbg_file_name + length, ".bbg"); bb_file = fopen (bb_file_name, "rb"); if (bb_file == NULL) @@ -464,6 +468,21 @@ open_files () exit (FATAL_EXIT_CODE); } + bbg_file = fopen (bbg_file_name, "rb"); + if (bbg_file == NULL) + { + fnotice (stderr, "Could not open program flow graph file %s.\n", + bbg_file_name); + exit (FATAL_EXIT_CODE); + } + + { + struct stat status; + + if (!fstat (fileno (bb_file), &status)) + bb_file_time = status.st_mtime; + } + /* If none of the functions in the file were executed, then there won't be a .da file. Just assume that all counts are zero in this case. */ da_file = fopen (da_file_name, "rb"); @@ -473,14 +492,6 @@ open_files () fnotice (stderr, "Assuming that all execution counts are zero.\n"); } - bbg_file = fopen (bbg_file_name, "rb"); - if (bbg_file == NULL) - { - fnotice (stderr, "Could not open program flow graph file %s.\n", - bbg_file_name); - exit (FATAL_EXIT_CODE); - } - /* Check for empty .bbg file. This indicates that there is no executable code in this source file. */ /* Set the EOF condition if at the end of file. */ @@ -554,7 +565,6 @@ read_profile (function_name, cfg_checksum, instr_arcs) int function_name_buffer_len; profile = xmalloc (sizeof (gcov_type) * instr_arcs); - rewind (da_file); function_name_buffer_len = strlen (function_name) + 1; function_name_buffer = xmalloc (function_name_buffer_len + 1); @@ -564,6 +574,7 @@ read_profile (function_name, cfg_checksum, instr_arcs) if (!da_file) return profile; + rewind (da_file); while (1) { long magic, extra_bytes; @@ -1100,15 +1111,64 @@ calculate_branch_probs (current_graph, block_num, branch_probs, last_line_num) } } +/* Format a HOST_WIDE_INT 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_hwint (top, bottom, dp) + HOST_WIDEST_INT top, 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); + if (!percent && 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, HOST_WIDEST_INT_PRINT_DEC, top); + + return buffer; +} + + /* Output summary info for a function. */ static void function_summary () { if (function_source_lines) - fnotice (stdout, "%6.2f%% of %d source lines executed in function %s\n", - (((double) function_source_lines_executed / function_source_lines) - * 100), function_source_lines, function_name); + fnotice (stdout, "%s of %d source lines executed in function %s\n", + format_hwint (function_source_lines_executed, + function_source_lines, 2), + function_source_lines, function_name); else fnotice (stdout, "No executable source lines in function %s\n", function_name); @@ -1117,20 +1177,23 @@ function_summary () { if (function_branches) { - fnotice (stdout, "%6.2f%% of %d branches executed in function %s\n", - (((double) function_branches_executed / function_branches) - * 100), function_branches, function_name); + fnotice (stdout, "%s of %d branches executed in function %s\n", + format_hwint (function_branches_executed, + function_branches, 2), + function_branches, function_name); fnotice (stdout, - "%6.2f%% of %d branches taken at least once in function %s\n", - (((double) function_branches_taken / function_branches) - * 100), function_branches, function_name); + "%s of %d branches taken at least once in function %s\n", + format_hwint (function_branches_taken, + function_branches, 2), + function_branches, function_name); } else fnotice (stdout, "No branches in function %s\n", function_name); if (function_calls) - fnotice (stdout, "%6.2f%% of %d calls executed in function %s\n", - (((double) function_calls_executed / function_calls) - * 100), function_calls, function_name); + fnotice (stdout, "%s of %d calls executed in function %s\n", + format_hwint (function_calls_executed, + function_calls, 2), + function_calls, function_name); else fnotice (stdout, "No calls in function %s\n", function_name); } @@ -1185,21 +1248,7 @@ output_data () for (s_ptr = sources; s_ptr; s_ptr = s_ptr->next) { - /* If this is a relative file name, and an object directory has been - specified, then make it relative to the object directory name. */ - if (! IS_ABSOLUTE_PATHNAME (s_ptr->name) - && object_directory != 0 - && *object_directory != '\0') - { - int objdir_count = strlen (object_directory); - source_file_name = xmalloc (objdir_count + strlen (s_ptr->name) + 2); - strcpy (source_file_name, object_directory); - if (object_directory[objdir_count - 1] != '/') - source_file_name[objdir_count++] = '/'; - strcpy (source_file_name + objdir_count, s_ptr->name); - } - else - source_file_name = s_ptr->name; + source_file_name = s_ptr->name; line_counts = (gcov_type *) xcalloc (sizeof (gcov_type), s_ptr->maxlineno); line_exists = xcalloc (1, s_ptr->maxlineno); @@ -1376,9 +1425,10 @@ output_data () if (total_source_lines) fnotice (stdout, - "%6.2f%% of %d source lines executed in file %s\n", - (((double) total_source_lines_executed / total_source_lines) - * 100), total_source_lines, source_file_name); + "%s of %d source lines executed in file %s\n", + format_hwint (total_source_lines_executed, + total_source_lines, 2), + total_source_lines, source_file_name); else fnotice (stdout, "No executable source lines in file %s\n", source_file_name); @@ -1387,20 +1437,22 @@ output_data () { if (total_branches) { - fnotice (stdout, "%6.2f%% of %d branches executed in file %s\n", - (((double) total_branches_executed / total_branches) - * 100), total_branches, source_file_name); + fnotice (stdout, "%s of %d branches executed in file %s\n", + format_hwint (total_branches_executed, + total_branches, 2), + total_branches, source_file_name); fnotice (stdout, - "%6.2f%% of %d branches taken at least once in file %s\n", - (((double) total_branches_taken / total_branches) - * 100), total_branches, source_file_name); + "%s of %d branches taken at least once in file %s\n", + format_hwint (total_branches_taken, + total_branches, 2), + total_branches, source_file_name); } else fnotice (stdout, "No branches in file %s\n", source_file_name); if (total_calls) - fnotice (stdout, "%6.2f%% of %d calls executed in file %s\n", - (((double) total_calls_executed / total_calls) - * 100), total_calls, source_file_name); + fnotice (stdout, "%s of %d calls executed in file %s\n", + format_hwint (total_calls_executed, total_calls, 2), + total_calls, source_file_name); else fnotice (stdout, "No calls in file %s\n", source_file_name); } @@ -1410,51 +1462,70 @@ output_data () /* Now the statistics are ready. Read in the source file one line at a time, and output that line to the gcov file preceded by its execution count if non zero. */ - - source_file = fopen (source_file_name, "r"); - if (source_file == NULL) + char const *retval; + + /* Generate an output file name. LONG_OUTPUT_NAMES and + PRESERVE_PATHS affect name generation. With + preserve_paths we create a filename from all path + components of the source file, replacing '/' with '#', + without it we simply take the basename component. 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 + '##'. Also '.' filename components are removed and '..' + components are renamed to '^'. */ + gcov_file_name = xmalloc (strlen (source_file_name) + + strlen (input_file_name) + 10); + gcov_file_name[0] = 0; + if (output_long_names && strcmp (source_file_name, input_file_name)) { - fnotice (stderr, "Could not open source file %s.\n", - source_file_name); - free (line_counts); - free (line_exists); - continue; + /* Generate the input filename part. */ + cptr = preserve_paths ? NULL : strrchr (input_file_name, '/'); + cptr = cptr ? cptr + 1 : input_file_name; + strcat (gcov_file_name, cptr); + strcat (gcov_file_name, "##"); } + /* Generate the source filename part. */ + cptr = preserve_paths ? NULL : strrchr (source_file_name, '/'); + cptr = cptr ? cptr + 1 : source_file_name; + strcat (gcov_file_name, cptr); - count = strlen (source_file_name); - cptr = strrchr (s_ptr->name, '/'); - if (cptr) - cptr = cptr + 1; - else - cptr = s_ptr->name; - if (output_long_names && strcmp (cptr, input_file_name)) - { - gcov_file_name = xmalloc (count + 7 + strlen (input_file_name)); - - cptr = strrchr (input_file_name, '/'); - if (cptr) - strcpy (gcov_file_name, cptr + 1); - else - strcpy (gcov_file_name, input_file_name); - - strcat (gcov_file_name, "."); - - cptr = strrchr (source_file_name, '/'); - if (cptr) - strcat (gcov_file_name, cptr + 1); - else - strcat (gcov_file_name, source_file_name); - } - else + if (preserve_paths) { - gcov_file_name = xmalloc (count + 6); - cptr = strrchr (source_file_name, '/'); - if (cptr) - strcpy (gcov_file_name, cptr + 1); - else - strcpy (gcov_file_name, source_file_name); + /* Convert '/' to '#', remove '/./', convert '/../' to + '/^/' */ + char *prev; + + for (cptr = gcov_file_name; + (cptr = strchr ((prev = cptr), '/'));) + { + unsigned shift = 0; + + if (prev + 1 == cptr && prev[0] == '.') + { + /* Remove '.' */ + shift = 2; + } + else if (prev + 2 == cptr + && prev[0] == '.' && prev[1] == '.') + { + /* Convert '..' */ + shift = 1; + prev[1] = '^'; + } + else + *cptr++ = '#'; + if (shift) + { + cptr = prev; + do + prev[0] = prev[shift]; + while (*prev++); + } + } } - + /* Don't strip off the ending for compatibility with tcov, since this results in confusion if there is more than one file with the same basename, e.g. tmp.c and tmp.h. */ @@ -1466,7 +1537,6 @@ output_data () { fnotice (stderr, "Could not open output file %s.\n", gcov_file_name); - fclose (source_file); free (line_counts); free (line_exists); continue; @@ -1474,44 +1544,61 @@ output_data () fnotice (stdout, "Creating %s.\n", gcov_file_name); - for (count = 1; count < s_ptr->maxlineno; count++) + fprintf (gcov_file, "%9s:%5d:Source:%s\n", "-", 0, source_file_name); + fprintf (gcov_file, "%9s:%5d:Object:%s\n", "-", 0, bb_file_name); + + source_file = fopen (source_file_name, "r"); + if (source_file == NULL) + fnotice (stderr, "Could not open source file %s.\n", + source_file_name); + else { - char *retval; - int len; - - retval = fgets (string, STRING_SIZE, source_file); + struct stat status; - /* For lines which don't exist in the .bb file, print nothing - before the source line. For lines which exist but were never - executed, print ###### before the source line. Otherwise, - print the execution count before the source line. */ + if (!fstat (fileno (source_file), &status) + && status.st_mtime > bb_file_time) + { + fnotice (stderr, "Warning: source file %s is newer than %s\n", + source_file_name, bb_file_name); + fprintf (gcov_file, "%9s:%5d:Source is newer than compiler output\n", "-", 0); + } + } + + for (retval = source_file ? "" : NULL, count = 1; + count < s_ptr->maxlineno; count++) + { + /* For lines which don't exist in the .bb file, print + '-' before the source line. For lines which exist + but were never executed, print '#####' 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. */ - if (line_exists[count]) + fprintf (gcov_file, "%9s:%5ld:", + !line_exists[count] ? "-" + : !line_counts[count] ? "#####" + : format_hwint (line_counts[count], 0, -1), count); + + if (retval) { - if (line_counts[count]) + do { - char c[20]; - sprintf (c, HOST_WIDEST_INT_PRINT_DEC, (HOST_WIDEST_INT)line_counts[count]); - fprintf (gcov_file, "%12s %s", c, - string); + retval = fgets (string, STRING_SIZE, source_file); + if (!retval) + { + fnotice (stderr, + "Unexpected EOF while reading source file %s.\n", + source_file_name); + break; + } + fputs (retval, gcov_file); } - else - fprintf (gcov_file, " ###### %s", string); - } - else - fprintf (gcov_file, "\t\t%s", string); - - /* In case the source file line is larger than our buffer, keep - reading and outputting lines until we get a newline. */ - len = strlen (string); - while ((len == 0 || string[strlen (string) - 1] != '\n') - && retval != NULL) - { - retval = fgets (string, STRING_SIZE, source_file); - fputs (string, gcov_file); + while (!retval[0] || retval[strlen (retval) - 1] != '\n'); } - + if (!retval) + fputs ("??\n", gcov_file); + if (output_branch_probs) { for (i = 0, a_ptr = branch_probs[count]; a_ptr; @@ -1520,95 +1607,53 @@ output_data () if (a_ptr->call_insn) { if (a_ptr->total == 0) - fnotice (gcov_file, "call %d never executed\n", i); - else - { - if (output_branch_counts) - { - char c[20]; - sprintf (c, HOST_WIDEST_INT_PRINT_DEC, - a_ptr->total - a_ptr->hits); - fnotice (gcov_file, - "call %d returns = %s\n", i, c); - } - else - { - char c[20]; - sprintf (c, HOST_WIDEST_INT_PRINT_DEC, - 100 - ((a_ptr->hits * 100) - + (a_ptr->total >> 1)) - / a_ptr->total); - fnotice (gcov_file, - "call %d returns = %s%%\n", i, c); - } - } + fnotice (gcov_file, "call %2d: never executed\n", i); + else + fnotice + (gcov_file, "call %2d: returns %s\n", i, + format_hwint (a_ptr->total - a_ptr->hits, + a_ptr->total, + -output_branch_counts)); } else { if (a_ptr->total == 0) - fnotice (gcov_file, "branch %d never executed\n", + fnotice (gcov_file, "branch %2d: never executed\n", i); else - { - if (output_branch_counts) - { - char c[20]; - sprintf (c, HOST_WIDEST_INT_PRINT_DEC, - a_ptr->hits); - fnotice (gcov_file, - "branch %d taken = %s\n", i, c); - } - else - { - char c[20]; - sprintf (c, HOST_WIDEST_INT_PRINT_DEC, - ((a_ptr->hits * 100) - + (a_ptr->total >> 1)) - / a_ptr->total); - fnotice (gcov_file, - "branch %d taken = %s%%\n", i, c); - } - } + fnotice + (gcov_file, "branch %2d: taken %s\n", i, + format_hwint (a_ptr->hits, a_ptr->total, + -output_branch_counts)); } } } - - /* Gracefully handle errors while reading the source file. */ - if (retval == NULL) - { - fnotice (stderr, - "Unexpected EOF while reading source file %s.\n", - source_file_name); - break; - } } /* Handle all remaining source lines. There may be lines after the last line of code. */ + if (retval) + { + for (; (retval = fgets (string, STRING_SIZE, source_file)); + count++) + { + fprintf (gcov_file, "%9s:%5ld:%s", "-", count, retval); - { - char *retval = fgets (string, STRING_SIZE, source_file); - while (retval != NULL) - { - int len; - - fprintf (gcov_file, "\t\t%s", string); - - /* In case the source file line is larger than our buffer, keep - reading and outputting lines until we get a newline. */ - len = strlen (string); - while ((len == 0 || string[strlen (string) - 1] != '\n') - && retval != NULL) - { - retval = fgets (string, STRING_SIZE, source_file); - fputs (string, gcov_file); - } - - retval = fgets (string, STRING_SIZE, source_file); - } - } + while (!retval[0] || retval[strlen (retval) - 1] != '\n') + { + retval = fgets (string, STRING_SIZE, source_file); + if (!retval) + break; + fputs (retval, gcov_file); + } + } + } - fclose (source_file); + if (source_file) + fclose (source_file); + if (ferror (gcov_file)) + fnotice (stderr, "Error writing output file %s.\n", + gcov_file_name); fclose (gcov_file); } |