X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=libgcc%2Flibgcov-driver.c;h=e53e4dc392a0cd6c4d0d90ff79385c1d5bb9086f;hb=1e9f339d946b8296e3785bec77e03d71b487d689;hp=cdebb7473262665e4c589f70b3db0e0d08b1acd7;hpb=512cc0151207de4c7ff3a84f040f730fe0d52458;p=gcc.git diff --git a/libgcc/libgcov-driver.c b/libgcc/libgcov-driver.c index cdebb747326..e53e4dc392a 100644 --- a/libgcc/libgcov-driver.c +++ b/libgcc/libgcov-driver.c @@ -1,6 +1,6 @@ /* Routines required for instrumenting a program. */ /* Compile this one with gcc. */ -/* Copyright (C) 1989-2018 Free Software Foundation, Inc. +/* Copyright (C) 1989-2020 Free Software Foundation, Inc. This file is part of GCC. @@ -53,6 +53,8 @@ static void gcov_error_exit (void); #include "gcov-io.c" +#define GCOV_PROF_PREFIX "libgcov profiling error:%s:" + struct gcov_fn_buffer { struct gcov_fn_buffer *next; @@ -151,12 +153,33 @@ buffer_fn_data (const char *filename, const struct gcov_info *gi_ptr, return &fn_buffer->next; fail: - gcov_error ("profiling:%s:Function %u %s %u \n", filename, fn_ix, + gcov_error (GCOV_PROF_PREFIX "Function %u %s %u \n", filename, fn_ix, len ? "cannot allocate" : "counter mismatch", len ? len : ix); return (struct gcov_fn_buffer **)free_fn_data (gi_ptr, fn_buffer, ix); } +/* Convert VERSION into a string description and return the it. + BUFFER is used for storage of the string. The code should be + aligned wit gcov-iov.c. */ + +static char * +gcov_version_string (char *buffer, char version[4]) +{ + if (version[0] < 'A' || version[0] > 'Z' + || version[1] < '0' || version[1] > '9' + || version[2] < '0' || version[2] > '9') + sprintf (buffer, "(unknown)"); + else + { + unsigned major = 10 * (version[0] - 'A') + (version[1] - '0'); + unsigned minor = version[2] - '0'; + sprintf (buffer, "%u.%u (%s)", major, minor, + version[3] == '*' ? "release" : "experimental"); + } + return buffer; +} + /* Check if VERSION of the info block PTR matches libgcov one. Return 1 on success, or zero in case of versions mismatch. If FILENAME is not NULL, its value used for reporting purposes @@ -169,12 +192,16 @@ gcov_version (struct gcov_info *ptr, gcov_unsigned_t version, if (version != GCOV_VERSION) { char v[4], e[4]; + char version_string[128], expected_string[128]; GCOV_UNSIGNED2STRING (v, version); GCOV_UNSIGNED2STRING (e, GCOV_VERSION); - gcov_error ("profiling:%s:Version mismatch - expected %.4s got %.4s\n", - filename? filename : ptr->filename, e, v); + gcov_error (GCOV_PROF_PREFIX "Version mismatch - expected %s (%.4s) " + "got %s (%.4s)\n", + filename? filename : ptr->filename, + gcov_version_string (expected_string, e), e, + gcov_version_string (version_string, v), v); return 0; } return 1; @@ -209,7 +236,7 @@ merge_one_data (const char *filename, if (length != gi_ptr->stamp) { /* Read from a different compilation. Overwrite the file. */ - gcov_error ("profiling:%s:overwriting an existing profile data " + gcov_error (GCOV_PROF_PREFIX "overwriting an existing profile data " "with a different timestamp\n", filename); return 0; } @@ -274,22 +301,27 @@ merge_one_data (const char *filename, if (!merge) continue; - tag = gcov_read_unsigned (); - length = gcov_read_unsigned (); - if (tag != GCOV_TAG_FOR_COUNTER (t_ix) - || length != GCOV_TAG_COUNTER_LENGTH (ci_ptr->num)) - goto read_mismatch; - (*merge) (ci_ptr->values, ci_ptr->num); - ci_ptr++; - } + tag = gcov_read_unsigned (); + int read_length = (int)gcov_read_unsigned (); + length = abs (read_length); + if (tag != GCOV_TAG_FOR_COUNTER (t_ix) + || (length != GCOV_TAG_COUNTER_LENGTH (ci_ptr->num) + && t_ix != GCOV_COUNTER_V_TOPN + && t_ix != GCOV_COUNTER_V_INDIR)) + goto read_mismatch; + /* Merging with all zero counters does not make sense. */ + if (read_length > 0) + (*merge) (ci_ptr->values, ci_ptr->num); + ci_ptr++; + } if ((error = gcov_is_error ())) - goto read_error; + goto read_error; } if (tag) { read_mismatch:; - gcov_error ("profiling:%s:Merge mismatch for %s %u\n", + gcov_error (GCOV_PROF_PREFIX "Merge mismatch for %s %u\n", filename, f_ix >= 0 ? "function" : "summary", f_ix < 0 ? -1 - f_ix : f_ix); return -1; @@ -297,11 +329,42 @@ merge_one_data (const char *filename, return 0; read_error: - gcov_error ("profiling:%s:%s merging\n", filename, + gcov_error (GCOV_PROF_PREFIX "%s merging\n", filename, error < 0 ? "Overflow": "Error"); return -1; } +/* Store all TOP N counters where each has a dynamic length. */ + +static void +write_top_counters (const struct gcov_ctr_info *ci_ptr, + unsigned t_ix, + gcov_unsigned_t n_counts) +{ + unsigned counters = n_counts / GCOV_TOPN_MEM_COUNTERS; + gcc_assert (n_counts % GCOV_TOPN_MEM_COUNTERS == 0); + unsigned pair_total = 0; + for (unsigned i = 0; i < counters; i++) + pair_total += ci_ptr->values[GCOV_TOPN_MEM_COUNTERS * i + 1]; + unsigned disk_size = GCOV_TOPN_DISK_COUNTERS * counters + 2 * pair_total; + gcov_write_tag_length (GCOV_TAG_FOR_COUNTER (t_ix), + GCOV_TAG_COUNTER_LENGTH (disk_size)); + + for (unsigned i = 0; i < counters; i++) + { + gcov_type pair_count = ci_ptr->values[GCOV_TOPN_MEM_COUNTERS * i + 1]; + gcov_write_counter (ci_ptr->values[GCOV_TOPN_MEM_COUNTERS * i]); + gcov_write_counter (pair_count); + gcov_type start = ci_ptr->values[GCOV_TOPN_MEM_COUNTERS * i + 2]; + for (struct gcov_kvp *node = (struct gcov_kvp *)(intptr_t)start; + node != NULL; node = node->next) + { + gcov_write_counter (node->value); + gcov_write_counter (node->count); + } + } +} + /* Write counters in GI_PTR and the summary in PRG to a gcda file. In the case of appending to an existing file, SUMMARY_POS will be non-zero. We will write the file starting from SUMMAY_POS. */ @@ -354,20 +417,40 @@ write_one_data (const struct gcov_info *gi_ptr, ci_ptr = gfi_ptr->ctrs; for (t_ix = 0; t_ix < GCOV_COUNTERS; t_ix++) { - gcov_unsigned_t n_counts; - gcov_type *c_ptr; + gcov_position_t n_counts; - if (!gi_ptr->merge[t_ix]) - continue; + if (!gi_ptr->merge[t_ix]) + continue; - n_counts = ci_ptr->num; - gcov_write_tag_length (GCOV_TAG_FOR_COUNTER (t_ix), - GCOV_TAG_COUNTER_LENGTH (n_counts)); - c_ptr = ci_ptr->values; - while (n_counts--) - gcov_write_counter (*c_ptr++); - ci_ptr++; - } + n_counts = ci_ptr->num; + + if (t_ix == GCOV_COUNTER_V_TOPN || t_ix == GCOV_COUNTER_V_INDIR) + write_top_counters (ci_ptr, t_ix, n_counts); + else + { + /* Do not stream when all counters are zero. */ + int all_zeros = 1; + for (unsigned i = 0; i < n_counts; i++) + if (ci_ptr->values[i] != 0) + { + all_zeros = 0; + break; + } + + if (all_zeros) + gcov_write_tag_length (GCOV_TAG_FOR_COUNTER (t_ix), + GCOV_TAG_COUNTER_LENGTH (-n_counts)); + else + { + gcov_write_tag_length (GCOV_TAG_FOR_COUNTER (t_ix), + GCOV_TAG_COUNTER_LENGTH (n_counts)); + for (unsigned i = 0; i < n_counts; i++) + gcov_write_counter (ci_ptr->values[i]); + } + } + + ci_ptr++; + } if (buffered) fn_buffer = free_fn_data (gi_ptr, fn_buffer, GCOV_COUNTERS); } @@ -375,97 +458,6 @@ write_one_data (const struct gcov_info *gi_ptr, gcov_write_unsigned (0); } -/* Helper function for merging summary. */ - -static void -merge_summary (int run_counted, struct gcov_summary *summary, - gcov_type run_max) -{ - if (!run_counted) - { - summary->runs++; - summary->sum_max += run_max; - } -} - -/* Sort N entries in VALUE_ARRAY in descending order. - Each entry in VALUE_ARRAY has two values. The sorting - is based on the second value. */ - -GCOV_LINKAGE void -gcov_sort_n_vals (gcov_type *value_array, int n) -{ - int j, k; - - for (j = 2; j < n; j += 2) - { - gcov_type cur_ent[2]; - - cur_ent[0] = value_array[j]; - cur_ent[1] = value_array[j + 1]; - k = j - 2; - while (k >= 0 && value_array[k + 1] < cur_ent[1]) - { - value_array[k + 2] = value_array[k]; - value_array[k + 3] = value_array[k+1]; - k -= 2; - } - value_array[k + 2] = cur_ent[0]; - value_array[k + 3] = cur_ent[1]; - } -} - -/* Sort the profile counters for all indirect call sites. Counters - for each call site are allocated in array COUNTERS. */ - -static void -gcov_sort_icall_topn_counter (const struct gcov_ctr_info *counters) -{ - int i; - gcov_type *values; - int n = counters->num; - - gcc_assert (!(n % GCOV_ICALL_TOPN_NCOUNTS)); - values = counters->values; - - for (i = 0; i < n; i += GCOV_ICALL_TOPN_NCOUNTS) - { - gcov_type *value_array = &values[i + 1]; - gcov_sort_n_vals (value_array, GCOV_ICALL_TOPN_NCOUNTS - 1); - } -} - -/* Sort topn indirect_call profile counters in GI_PTR. */ - -static void -gcov_sort_topn_counter_arrays (const struct gcov_info *gi_ptr) -{ - unsigned int i; - int f_ix; - const struct gcov_fn_info *gfi_ptr; - const struct gcov_ctr_info *ci_ptr; - - if (!gi_ptr->merge[GCOV_COUNTER_ICALL_TOPNV]) - return; - - for (f_ix = 0; (unsigned)f_ix != gi_ptr->n_functions; f_ix++) - { - gfi_ptr = gi_ptr->functions[f_ix]; - ci_ptr = gfi_ptr->ctrs; - for (i = 0; i < GCOV_COUNTERS; i++) - { - if (!gi_ptr->merge[i]) - continue; - if (i == GCOV_COUNTER_ICALL_TOPNV) - { - gcov_sort_icall_topn_counter (ci_ptr); - break; - } - ci_ptr++; - } - } -} - /* Dump the coverage counts for one gcov_info object. We merge with existing counts when possible, to avoid growing the .da files ad infinitum. We use this program's checksum to make sure we only accumulate whole program @@ -475,16 +467,14 @@ gcov_sort_topn_counter_arrays (const struct gcov_info *gi_ptr) static void dump_one_gcov (struct gcov_info *gi_ptr, struct gcov_filename *gf, - unsigned run_counted, gcov_type run_max) + unsigned run_counted ATTRIBUTE_UNUSED, + gcov_type run_max ATTRIBUTE_UNUSED) { struct gcov_summary summary = {}; int error; gcov_unsigned_t tag; - fn_buffer = 0; - gcov_sort_topn_counter_arrays (gi_ptr); - error = gcov_exit_open_gcda_file (gi_ptr, gf); if (error == -1) return; @@ -495,7 +485,8 @@ dump_one_gcov (struct gcov_info *gi_ptr, struct gcov_filename *gf, /* Merge data from file. */ if (tag != GCOV_DATA_MAGIC) { - gcov_error ("profiling:%s:Not a gcov data file\n", gf->filename); + gcov_error (GCOV_PROF_PREFIX "Not a gcov data file\n", + gf->filename); goto read_fatal; } error = merge_one_data (gf->filename, gi_ptr, &summary); @@ -505,7 +496,15 @@ dump_one_gcov (struct gcov_info *gi_ptr, struct gcov_filename *gf, gcov_rewrite (); - merge_summary (run_counted, &summary, run_max); +#if !IN_GCOV_TOOL + if (!run_counted) + { + summary.runs++; + summary.sum_max += run_max; + } +#else + summary = gi_ptr->summary; +#endif write_one_data (gi_ptr, &summary); /* fall through */ @@ -516,8 +515,8 @@ read_fatal:; if ((error = gcov_close ())) gcov_error (error < 0 ? - "profiling:%s:Overflow writing\n" : - "profiling:%s:Error writing\n", + GCOV_PROF_PREFIX "Overflow writing\n" : + GCOV_PROF_PREFIX "Error writing\n", gf->filename); } @@ -589,6 +588,12 @@ struct gcov_root __gcov_root; struct gcov_master __gcov_master = {GCOV_VERSION, 0}; +/* Pool of pre-allocated gcov_kvp strutures. */ +struct gcov_kvp __gcov_kvp_pool[GCOV_PREALLOCATED_KVP]; + +/* Index to first free gcov_kvp in the pool. */ +unsigned __gcov_kvp_pool_index; + void __gcov_exit (void) {