diff options
Diffstat (limited to 'gcc-4.4.3/gcc/gcov.c')
-rw-r--r-- | gcc-4.4.3/gcc/gcov.c | 501 |
1 files changed, 500 insertions, 1 deletions
diff --git a/gcc-4.4.3/gcc/gcov.c b/gcc-4.4.3/gcc/gcov.c index d67e621bc..38937917e 100644 --- a/gcc-4.4.3/gcc/gcov.c +++ b/gcc-4.4.3/gcc/gcov.c @@ -209,6 +209,15 @@ typedef struct coverage_info char *name; } coverage_t; +/* Describes PMU profile data for either one source file or for the + entire program. */ + +typedef struct pmu_data +{ + ll_infos_t ll_infos; + brm_infos_t brm_infos; +} pmu_data_t; + /* Describes a single line of source. Contains a chain of basic blocks with code on it. */ @@ -242,6 +251,8 @@ typedef struct source_info coverage_t coverage; + pmu_data_t *pmu_data; /* PMU profile information for this file. */ + /* Functions in this source file. These are in ascending line number order. */ function_t *functions; @@ -301,6 +312,10 @@ static int flag_branches = 0; /* Show unconditional branches too. */ static int flag_unconditional = 0; +/* Output performance monitoring unit (PMU) data, if available. */ + +static int flag_pmu_profile = 0; + /* Output a gcov file if this is true. This is on by default, and can be turned off by the -n option. */ @@ -340,6 +355,18 @@ static int flag_preserve_paths = 0; static int flag_counts = 0; +/* PMU profile default filename. */ + +static char pmu_profile_default_filename[] = "pmuprofile.gcda"; + +/* PMU profile filename where the PMU profile data is read from. */ + +static char *pmu_profile_filename = 0; + +/* PMU data for the entire program. */ + +static pmu_data_t pmu_global_info; + /* Forward declarations. */ static void fnotice (FILE *, const char *, ...) ATTRIBUTE_PRINTF_2; static int process_args (int, char **); @@ -361,6 +388,17 @@ 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 void release_structures (void); +static void process_pmu_profile (void); +static void filter_pmu_data_lines (source_t *src); +static void output_pmu_data_header (FILE *gcov_file, pmu_data_t *pmu_data); +static void output_pmu_data (FILE *gcov_file, const source_t *src, + const unsigned line_num); +static void output_load_latency_line (FILE *fp, + const gcov_pmu_ll_info_t *ll_info, + gcov_pmu_tool_header_t *tool_header); +static void output_branch_mispredict_line (FILE *fp, + const gcov_pmu_brm_info_t *brm_info); + extern int main (int, char **); int @@ -383,6 +421,15 @@ main (int argc, char **argv) if (argc - argno > 1) multiple_files = 1; + /* We read pmu profile first because we later filter + src:line_numbers for each source. */ + if (flag_pmu_profile) + { + if (!pmu_profile_filename) + pmu_profile_filename = pmu_profile_default_filename; + process_pmu_profile (); + } + for (; argno != argc; argno++) process_file (argv[argno]); @@ -420,6 +467,7 @@ print_usage (int error_p) fnotice (file, " -b, --branch-probabilities Include branch probabilities in output\n"); fnotice (file, " -c, --branch-counts Given counts of branches taken\n\ rather than percentages\n"); + fnotice (file, " -m, --pmu-profile Output PMU profile data if available\n"); fnotice (file, " -n, --no-output Do not create an output file\n"); fnotice (file, " -l, --long-file-names Use long output file names for included\n\ source files\n"); @@ -432,6 +480,7 @@ print_usage (int error_p) applications. It will output a single\n\ .gcov file per .gcda file. No source file\n\ is required.\n"); + fnotice (file, " -q, --pmu_profile-path Path for PMU profile (default pmuprofile.gcda)\n"); fnotice (file, "\nFor bug reporting instructions, please see:\n%s.\n", bug_report_url); exit (status); @@ -459,6 +508,7 @@ static const struct option options[] = { "all-blocks", no_argument, NULL, 'a' }, { "branch-probabilities", no_argument, NULL, 'b' }, { "branch-counts", no_argument, NULL, 'c' }, + { "pmu-profile", no_argument, NULL, 'm' }, { "no-output", no_argument, NULL, 'n' }, { "long-file-names", no_argument, NULL, 'l' }, { "function-summaries", no_argument, NULL, 'f' }, @@ -467,6 +517,7 @@ static const struct option options[] = { "object-file", required_argument, NULL, 'o' }, { "unconditional-branches", no_argument, NULL, 'u' }, { "intermediate-format", no_argument, NULL, 'i' }, + { "pmu_profile-path", required_argument, NULL, 'q' }, { 0, 0, 0, 0 } }; @@ -477,7 +528,8 @@ process_args (int argc, char **argv) { int opt; - while ((opt = getopt_long (argc, argv, "abcfhilno:puv", options, NULL)) != -1) + while ((opt = getopt_long (argc, argv, "abcfhilmno:q:puv", options, NULL)) + != -1) { switch (opt) { @@ -499,6 +551,9 @@ process_args (int argc, char **argv) case 'l': flag_long_names = 1; break; + case 'm': + flag_pmu_profile = 1; + break; case 'n': flag_gcov_file = 0; break; @@ -508,6 +563,9 @@ process_args (int argc, char **argv) case 'p': flag_preserve_paths = 1; break; + case 'q': + pmu_profile_filename = optarg; + break; case 'u': flag_unconditional = 1; break; @@ -747,6 +805,8 @@ release_structures (void) { function_t *fn; source_t *src; + ll_infos_t *ll_infos = &pmu_global_info.ll_infos; + brm_infos_t *brm_infos = &pmu_global_info.brm_infos; while ((src = sources)) { @@ -754,6 +814,14 @@ release_structures (void) free (src->name); free (src->lines); + if (src->pmu_data) + { + if (src->pmu_data->ll_infos.ll_array) + free (src->pmu_data->ll_infos.ll_array); + if (src->pmu_data->brm_infos.brm_array) + free (src->pmu_data->brm_infos.brm_array); + free (src->pmu_data); + } } while ((fn = functions)) @@ -775,6 +843,42 @@ release_structures (void) free (fn->blocks); free (fn->counts); } + + /* Cleanup PMU load latency info. */ + if (ll_infos->ll_count) + { + unsigned i; + + /* delete each element */ + for (i = 0; i < ll_infos->ll_count; ++i) + { + if (ll_infos->ll_array[i]->filename) + XDELETE (ll_infos->ll_array[i]->filename); + XDELETE (ll_infos->ll_array[i]); + } + /* delete the array itself */ + XDELETE (ll_infos->ll_array); + ll_infos->ll_array = NULL; + ll_infos->ll_count = 0; + } + + /* Cleanup PMU branch mispredict info. */ + if (brm_infos->brm_count) + { + unsigned i; + + /* delete each element */ + for (i = 0; i < brm_infos->brm_count; ++i) + { + if (brm_infos->brm_array[i]->filename) + XDELETE (brm_infos->brm_array[i]->filename); + XDELETE (brm_infos->brm_array[i]); + } + /* delete the array itself */ + XDELETE (brm_infos->brm_array); + brm_infos->brm_array = NULL; + brm_infos->brm_count = 0; + } } /* Generate the names of the graph and data files. If OBJECT_DIRECTORY @@ -871,6 +975,7 @@ find_source (const char *file_name) src->coverage.name = src->name; src->index = source_index++; src->next = sources; + src->pmu_data = 0; sources = src; if (!stat (file_name, &status)) @@ -1792,6 +1897,148 @@ add_line_counts (coverage_t *coverage, function_t *fn) fnotice (stderr, "%s:no lines for '%s'\n", bbg_file_name, fn->name); } +/* Filter PMU profile global data for lines for SRC. Save PMU info + matching the source file and sort them by line number for later + line by line processing. */ + +static void +filter_pmu_data_lines (source_t *src) +{ + unsigned i; + int changed; + ll_infos_t *ll_infos; /* load latency information for this source */ + brm_infos_t *brm_infos; /* branch mispredict information for this source */ + + if (pmu_global_info.ll_infos.ll_count == 0 && + pmu_global_info.brm_infos.brm_count == 0) + /* If there are no global entries, there is nothing to filter. */ + return; + + src->pmu_data = XCNEW (pmu_data_t); + ll_infos = &src->pmu_data->ll_infos; + brm_infos = &src->pmu_data->brm_infos; + ll_infos->pmu_tool_header = pmu_global_info.ll_infos.pmu_tool_header; + brm_infos->pmu_tool_header = pmu_global_info.brm_infos.pmu_tool_header; + ll_infos->ll_array = 0; + brm_infos->brm_array = 0; + + /* Go over all the load latency entries and save the ones + corresponding to this source file. */ + for (i = 0; i < pmu_global_info.ll_infos.ll_count; ++i) + { + gcov_pmu_ll_info_t *ll_info = pmu_global_info.ll_infos.ll_array[i]; + if (0 == strcmp (src->name, ll_info->filename)) + { + if (!ll_infos->ll_array) + { + ll_infos->ll_count = 0; + ll_infos->alloc_ll_count = 64; + ll_infos->ll_array = XCNEWVEC (gcov_pmu_ll_info_t *, + ll_infos->alloc_ll_count); + } + /* Found a matching entry, save it. */ + ll_infos->ll_count++; + if (ll_infos->ll_count >= ll_infos->alloc_ll_count) + { + /* need to realloc */ + ll_infos->ll_array = (gcov_pmu_ll_info_t **) + xrealloc (ll_infos->ll_array, 2 * ll_infos->alloc_ll_count); + if (ll_infos->ll_array == NULL) { + fprintf (stderr, "Cannot allocate memory for load latency.\n"); + return; + } + } + ll_infos->ll_array[ll_infos->ll_count - 1] = ll_info; + } + } + + /* Go over all the branch mispredict entries and save the ones + corresponding to this source file. */ + for (i = 0; i < pmu_global_info.brm_infos.brm_count; ++i) + { + gcov_pmu_brm_info_t *brm_info = pmu_global_info.brm_infos.brm_array[i]; + if (0 == strcmp (src->name, brm_info->filename)) + { + if (!brm_infos->brm_array) + { + brm_infos->brm_count = 0; + brm_infos->alloc_brm_count = 64; + brm_infos->brm_array = XCNEWVEC (gcov_pmu_brm_info_t *, + brm_infos->alloc_brm_count); + } + /* Found a matching entry, save it. */ + brm_infos->brm_count++; + if (brm_infos->brm_count >= brm_infos->alloc_brm_count) + { + /* need to realloc */ + brm_infos->brm_array = (gcov_pmu_brm_info_t **) + xrealloc (brm_infos->brm_array, 2 * brm_infos->alloc_brm_count); + if (brm_infos->brm_array == NULL) { + fprintf (stderr, "Cannot allocate memory for load latency.\n"); + return; + } + } + brm_infos->brm_array[brm_infos->brm_count - 1] = brm_info; + } + } + + /* Sort the load latency data according to the line numbers because + we later iterate over sources in line number order. Normally we + expect the PMU tool to provide sorted data, but a few entries can + be out of order. Thus we use a very simple bubble sort here. */ + if (ll_infos->ll_count > 1) + { + changed = 1; + while (changed) + { + changed = 0; + for (i = 0; i < ll_infos->ll_count - 1; ++i) + { + gcov_pmu_ll_info_t *item1 = ll_infos->ll_array[i]; + gcov_pmu_ll_info_t *item2 = ll_infos->ll_array[i+1]; + if (item1->line > item2->line) + { + /* swap */ + gcov_pmu_ll_info_t *tmp = ll_infos->ll_array[i]; + ll_infos->ll_array[i] = ll_infos->ll_array[i+1]; + ll_infos->ll_array[i+1] = tmp; + changed = 1; + } + } + } + } + + /* Similarly, sort branch mispredict info as well. */ + if (brm_infos->brm_count > 1) + { + changed = 1; + while (changed) + { + changed = 0; + for (i = 0; i < brm_infos->brm_count - 1; ++i) + { + gcov_pmu_brm_info_t *item1 = brm_infos->brm_array[i]; + gcov_pmu_brm_info_t *item2 = brm_infos->brm_array[i+1]; + if (item1->line > item2->line) + { + /* swap */ + gcov_pmu_brm_info_t *tmp = brm_infos->brm_array[i]; + brm_infos->brm_array[i] = brm_infos->brm_array[i+1]; + brm_infos->brm_array[i+1] = tmp; + changed = 1; + } + } + } + } + + /* If no matching PMU info was found, relase the structures. */ + if (!brm_infos->brm_array && !ll_infos->ll_array) + { + free (src->pmu_data); + src->pmu_data = 0; + } +} + /* Accumulate the line counts of a file. */ static void @@ -1801,6 +2048,10 @@ accumulate_line_counts (source_t *src) function_t *fn, *fn_p, *fn_n; unsigned ix; + if (flag_pmu_profile) + /* Filter PMU profile by source files and save into matching line(s). */ + filter_pmu_data_lines (src); + /* Reverse the function order. */ for (fn = src->functions, fn_p = NULL; fn; fn_p = fn, fn = fn_n) @@ -2048,6 +2299,9 @@ output_lines (FILE *gcov_file, const source_t *src) else if (src->file_time == 0) fprintf (gcov_file, "%9s:%5d:Source is newer than graph\n", "-", 0); + if (src->pmu_data) + output_pmu_data_header (gcov_file, src->pmu_data); + if (flag_branches) fn = src->functions; @@ -2125,6 +2379,10 @@ output_lines (FILE *gcov_file, const source_t *src) for (ix = 0, arc = line->u.branches; arc; arc = arc->line_next) ix += output_branch_count (gcov_file, ix, arc); } + + /* Output PMU profile info if available. */ + if (flag_pmu_profile) + output_pmu_data (gcov_file, src, line_num); } /* Handle all remaining source lines. There may be lines after the @@ -2148,3 +2406,244 @@ output_lines (FILE *gcov_file, const source_t *src) if (source_file) fclose (source_file); } + +/* Print an explanatory header for PMU_DATA into GCOV_FILE. */ + +static void +output_pmu_data_header (FILE *gcov_file, pmu_data_t *pmu_data) +{ + /* Print header for the applicable PMU events. */ + fprintf (gcov_file, "%9s:%5d\n", "-", 0); + if (pmu_data->ll_infos.ll_count) + { + char *text = pmu_data->ll_infos.pmu_tool_header->column_description; + char c; + fprintf (gcov_file, "%9s:%5u: %s", "PMU_LL", 0, + pmu_data->ll_infos.pmu_tool_header->column_header); + /* The column description is multiline text and we want to print + each line separately after formatting it. */ + fprintf (gcov_file, "%9s:%5u: ", "PMU_LL", 0); + while ((c = *text++)) + { + fprintf (gcov_file, "%c", c); + /* Do not print a new header on trailing newline. */ + if (c == '\n' && text[1]) + fprintf (gcov_file, "%9s:%5u: ", "PMU_LL", 0); + } + fprintf (gcov_file, "%9s:%5d\n", "-", 0); + } + + if (pmu_data->brm_infos.brm_count) + { + + fprintf (gcov_file, "%9s:%5d:PMU BRM: line: %s %s %s\n", + "-", 0, "count", "self", "address"); + fprintf (gcov_file, "%9s:%5d: " + "count: number of branch mispredicts sampled at this address\n", + "-", 0); + fprintf (gcov_file, "%9s:%5d: " + "self: branch mispredicts as percentage of the entire program\n", + "-", 0); + fprintf (gcov_file, "%9s:%5d\n", "-", 0); + } +} + +/* Output pmu data corresponding to SRC and LINE_NUM into GCOV_FILE. */ + +static void +output_pmu_data (FILE *gcov_file, const source_t *src, const unsigned line_num) +{ + unsigned i; + ll_infos_t *ll_infos; + brm_infos_t *brm_infos; + gcov_pmu_tool_header_t *tool_header; + + if (!src->pmu_data) + return; + + ll_infos = &src->pmu_data->ll_infos; + brm_infos = &src->pmu_data->brm_infos; + + if (ll_infos->ll_array) + { + tool_header = src->pmu_data->ll_infos.pmu_tool_header; + + /* Search PMU load latency data for the matching line + numbers. There could be multiple entries with the same line + number. We use the fact that line numbers are sorted in + ll_array. */ + for (i = 0; i < ll_infos->ll_count && + ll_infos->ll_array[i]->line <= line_num; ++i) + { + gcov_pmu_ll_info_t *ll_info = ll_infos->ll_array[i]; + if (ll_info->line == line_num) + output_load_latency_line (gcov_file, ll_info, tool_header); + } + } + + if (brm_infos->brm_array) + { + tool_header = src->pmu_data->brm_infos.pmu_tool_header; + + /* Search PMU branch mispredict data for the matching line + numbers. There could be multiple entries with the same line + number. We use the fact that line numbers are sorted in + brm_array. */ + for (i = 0; i < brm_infos->brm_count && + brm_infos->brm_array[i]->line <= line_num; ++i) + { + gcov_pmu_brm_info_t *brm_info = brm_infos->brm_array[i]; + if (brm_info->line == line_num) + output_branch_mispredict_line (gcov_file, brm_info); + } + } +} + + +/* Output formatted load latency info pointed to by LL_INFO into the + open file FP. TOOL_HEADER contains additional explanation of + fields. */ + +static void +output_load_latency_line (FILE *fp, const gcov_pmu_ll_info_t *ll_info, + gcov_pmu_tool_header_t *tool_header ATTRIBUTE_UNUSED) +{ + fprintf (fp, "%9s:%5u: ", "PMU_LL", ll_info->line); + fprintf (fp, " %u %.2f%% %.2f%% %.2f%% %.2f%% %.2f%% %.2f%% %.2f%% " + "%.2f%% %.2f%% " HOST_WIDEST_INT_PRINT_HEX "\n", + ll_info->counts, + convert_unsigned_to_pct (ll_info->self), + convert_unsigned_to_pct (ll_info->cum), + convert_unsigned_to_pct (ll_info->lt_10), + convert_unsigned_to_pct (ll_info->lt_32), + convert_unsigned_to_pct (ll_info->lt_64), + convert_unsigned_to_pct (ll_info->lt_256), + convert_unsigned_to_pct (ll_info->lt_1024), + convert_unsigned_to_pct (ll_info->gt_1024), + convert_unsigned_to_pct (ll_info->wself), + ll_info->code_addr); +} + + +/* Output formatted branch mispredict info pointed to by BRM_INFO into + the open file FP. */ + +static void +output_branch_mispredict_line (FILE *fp, + const gcov_pmu_brm_info_t *ll_info) +{ + fprintf (fp, "%9s:%5u: count: %u self: %.2f%% addr: " + HOST_WIDEST_INT_PRINT_HEX "\n", + "PMU BRM", + ll_info->line, + ll_info->counts, + convert_unsigned_to_pct (ll_info->self), + ll_info->code_addr); +} + +/* Read in the PMU profile information from the global PMU profile file. */ + +static void process_pmu_profile (void) +{ + unsigned tag; + unsigned version; + int error = 0; + ll_infos_t *ll_infos = &pmu_global_info.ll_infos; + brm_infos_t *brm_infos = &pmu_global_info.brm_infos; + + /* Construct path for pmuprofile.gcda filename. */ + create_file_names (pmu_profile_filename); + if (!gcov_open (da_file_name, 1)) + { + fnotice (stderr, "%s:cannot open pmu profile file\n", + pmu_profile_filename); + return; + } + 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; + } + 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); + } + /* read stamp */ + tag = gcov_read_unsigned (); + + /* Initialize PMU data fields. */ + ll_infos->ll_count = 0; + ll_infos->alloc_ll_count = 64; + ll_infos->ll_array = XCNEWVEC (gcov_pmu_ll_info_t *, + ll_infos->alloc_ll_count); + brm_infos->brm_count = 0; + brm_infos->alloc_brm_count = 64; + brm_infos->brm_array = XCNEWVEC (gcov_pmu_brm_info_t *, + brm_infos->alloc_brm_count); + + while ((tag = gcov_read_unsigned ())) + { + unsigned length = gcov_read_unsigned (); + unsigned long base = gcov_position (); + + if (tag == GCOV_TAG_PMU_LOAD_LATENCY_INFO) + { + gcov_pmu_ll_info_t *ll_info = XCNEW (gcov_pmu_ll_info_t); + gcov_read_pmu_load_latency_info (ll_info, length); + ll_infos->ll_count++; + if (ll_infos->ll_count >= ll_infos->alloc_ll_count) + { + /* need to realloc */ + ll_infos->ll_array = (gcov_pmu_ll_info_t **) + xrealloc (ll_infos->ll_array, 2 * ll_infos->alloc_ll_count); + if (ll_infos->ll_array == NULL) { + fprintf (stderr, "Cannot allocate memory for load latency.\n"); + goto cleanup; + } + } + ll_infos->ll_array[ll_infos->ll_count - 1] = ll_info; + } + else if (tag == GCOV_TAG_PMU_BRANCH_MISPREDICT_INFO) + { + gcov_pmu_brm_info_t *brm_info = XCNEW (gcov_pmu_brm_info_t); + gcov_read_pmu_branch_mispredict_info (brm_info, length); + brm_infos->brm_count++; + if (brm_infos->brm_count >= brm_infos->alloc_brm_count) + { + /* need to realloc */ + brm_infos->brm_array = (gcov_pmu_brm_info_t **) + xrealloc (brm_infos->brm_array, 2 * brm_infos->alloc_brm_count); + if (brm_infos->brm_array == NULL) { + fprintf (stderr, "Cannot allocate memory for load latency.\n"); + goto cleanup; + } + } + brm_infos->brm_array[brm_infos->brm_count - 1] = brm_info; + } + else if (tag == GCOV_TAG_PMU_TOOL_HEADER) + { + gcov_pmu_tool_header_t *tool_header = XCNEW (gcov_pmu_tool_header_t); + gcov_read_pmu_tool_header (tool_header, length); + ll_infos->pmu_tool_header = tool_header; + brm_infos->pmu_tool_header = tool_header; + } + + 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 (); +} |