From patchwork Thu Aug 16 14:21:37 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Teresa Johnson X-Patchwork-Id: 178003 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) by ozlabs.org (Postfix) with SMTP id 968442C0086 for ; Fri, 17 Aug 2012 00:22:38 +1000 (EST) Comment: DKIM? See http://www.dkim.org DKIM-Signature: v=1; a=rsa-sha1; c=relaxed/relaxed; d=gcc.gnu.org; s=default; x=1345731758; h=Comment: DomainKey-Signature:Received:Received:Received:Received:Received: Received:Received:Received:Received:To:Subject:Message-Id:Date: From:Mailing-List:Precedence:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:Sender:Delivered-To; bh=l5U48eV wfTfk8tO8U1P88W2Jh0k=; b=ZYlbdcK/z//srJWp1GQ+SbzPtsHqpbBVyepVKjJ L60FSlaD7Pje8qCvgvPOEjJjG/mSObTihR17fTViW0B7nOlp9/0DJCHqRHAoOTVa izPdmihMBT6ykThCpVhYrsMpWgT7SfVYW8EmxgHY5Ghc+6DXksxymhq12Ms7vgIy f62Q= Comment: DomainKeys? See http://antispam.yahoo.com/domainkeys DomainKey-Signature: a=rsa-sha1; q=dns; c=nofws; s=default; d=gcc.gnu.org; h=Received:Received:X-SWARE-Spam-Status:X-Spam-Check-By:Received:Received:X-Google-DKIM-Signature:Received:Received:Received:Received:Received:To:Subject:Message-Id:Date:From:X-Gm-Message-State:X-IsSubscribed:Mailing-List:Precedence:List-Id:List-Unsubscribe:List-Archive:List-Post:List-Help:Sender:Delivered-To; b=mu5YLQwUkWO291ghcyxOJzjvwquXRLyQ5p9xHLDbNpPcmpnX8a8Rc8HnvrvCG4 spZcYXKXgQLHeE8y2iPWJzKH1LAjOQ9Je5ArwTg35oqUx2RJhJpUwOtGp10Xv/mt 8a7pJAMkFCTBXUkn5iu+CHJBvPR1fKXKfJ0cQ1cBaE6wE=; Received: (qmail 26305 invoked by alias); 16 Aug 2012 14:22:26 -0000 Received: (qmail 25818 invoked by uid 22791); 16 Aug 2012 14:22:03 -0000 X-SWARE-Spam-Status: No, hits=-4.5 required=5.0 tests=AWL, BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, KHOP_RCVD_TRUST, RCVD_IN_DNSWL_LOW, RCVD_IN_HOSTKARMA_YE, RP_MATCHES_RCVD, TW_CL, TW_CP, URIBL_DBL_REDIR X-Spam-Check-By: sourceware.org Received: from mail-ee0-f73.google.com (HELO mail-ee0-f73.google.com) (74.125.83.73) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Thu, 16 Aug 2012 14:21:40 +0000 Received: by eeke49 with SMTP id e49so133077eek.2 for ; Thu, 16 Aug 2012 07:21:38 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=to:subject:message-id:date:from:x-gm-message-state; bh=axhuSxDHm7XKjuRnSDTrIQmjXyMdoWsQt/kMMk9EMPM=; b=IP+RSSP2tJMoE9dUJxTtDSuNYuhd7kBQiDEeI/j71tDpua4Pu22rHkz7Zm8AuRLIbG MtlA27SvJ06FpbjtCNW+McyceQ0sqtYC84HN/tzxdXlLAYha0XV6gy5VqunLt+3jL+T/ iS9xD+Wml8stFXUnUZmd2VRUf2Ffgz263tne+eARqoH8RtnQjS46D5YoVLpSoEOTBDn4 o9SfkqpVrQXcPutmPYYTgrnf7/wC1QPVmCyqt1FGdlDMxeHnDrFY8foVX1qe2CZRmz4B XwARhai6WL4c9aLS9cZUR0b6Cg3oKmpa0/wELXjNPZNnjsXHwJ9g29tlKSAhPLmY2Hs3 C5vw== Received: by 10.14.173.68 with SMTP id u44mr1187398eel.3.1345126898957; Thu, 16 Aug 2012 07:21:38 -0700 (PDT) Received: by 10.14.173.68 with SMTP id u44mr1187392eel.3.1345126898893; Thu, 16 Aug 2012 07:21:38 -0700 (PDT) Received: from hpza9.eem.corp.google.com ([74.125.121.33]) by gmr-mx.google.com with ESMTPS id d5si6194321eep.0.2012.08.16.07.21.38 (version=TLSv1/SSLv3 cipher=AES128-SHA); Thu, 16 Aug 2012 07:21:38 -0700 (PDT) Received: from tjsboxrox.mtv.corp.google.com (tjsboxrox.mtv.corp.google.com [172.18.110.68]) by hpza9.eem.corp.google.com (Postfix) with ESMTP id 3C55A5C0050; Thu, 16 Aug 2012 07:21:38 -0700 (PDT) Received: by tjsboxrox.mtv.corp.google.com (Postfix, from userid 147431) id 7F5EA61414; Thu, 16 Aug 2012 07:21:37 -0700 (PDT) To: reply@codereview.appspotmail.com, hubicka@ucw.cz, davidxl@google.com, gcc-patches@gcc.gnu.org Subject: [PATCH] Add working-set size and hotness information to fdo summary (issue6465057) Message-Id: <20120816142137.7F5EA61414@tjsboxrox.mtv.corp.google.com> Date: Thu, 16 Aug 2012 07:21:37 -0700 (PDT) From: tejohnson@google.com (Teresa Johnson) X-Gm-Message-State: ALoCoQkHQZC+5H3xJiXDHeDnS+w9JAeNesEAJUMwL25FxW12UXimWheCTtLGVywk8V00XZs4Tg8G9xWAkgyzNEQytKKcFfT/BhfABkB2z1dHKNd33KTFs9F6Ib8lbPaD7tfXkPadCa1wFkbk1KGzuR252DmVFHB6bH4/I0N+WGMT3sTfoTL29YjeIED1tWgjKGcVRlRNBEhv X-IsSubscribed: yes Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org This is a revision of my earlier patch to add working set information to the gcov program summary based on discussions between Honza, David and I and approved with changes by Honza in this earlier thread on the first patch: http://gcc.gnu.org/ml/gcc-patches/2012-07/msg01412.html I plan to commit this in couple days unless there are more revisions requested. Changes from the earlier patch are the removal of the loop unroller changes utilizing the new summary info, and feeding back an array of working set information for different cutoffs, as well as using a histogram to compute the working set information to avoid expensive copying and qsorting. Patch description: Patch to add new summary information on counter working set sizes to gcov profiles. The function summaries now include an array of working set data, where each data point corresponds to a percentage of the total sum_all of the counters and includes the number of hot counters and the minimum counter value included in that working set. By default, there are 128 working set data points included in the summary, but this is controlled at runtime with the GCOV_WORKING_SET_ENTRIES environment variable. The first GCOV_WORKING_SET_ENTRIES - 1 entries correspond to percentages 100.0/GCOV_WORKING_SET_ENTRIES through 100.0/GCOV_WORKING_SET_ENTRIES * (GCOV_WORKING_SET_ENTRIES - 1) of sum_all. The last entry always corresponds to 99.99% of sum_all. A log2-based histogram is used to compute the working set information, to avoid the need to copy all counters into a large array and invoke qsort. Bootstrapped and tested on x86_64-unknown-linux-gnu. 2012-08-16 Teresa Johnson * libgcc/libgcov.c (histo_index): New function. (histogram_insert): Ditto. (gcov_compute_working_set): Ditto. (gcov_exit): Invoke gcov_compute_working_set, and perform merging of new working set summary info. * gcc/gcov.c (read_count_file): Invoke gcov_destroy_summary to clean up allocated working set memory. * gcc/gcov-io.c (gcov_write_summary): Include length of working set summary info in GCOV_TAG_SUMMARY_LENGTH, and write out working set summary. (gcov_read_summary): Read working set summary info. (gcov_destroy_summary): New function. * gcc/gcov-io.h (gcov_type_unsigned): New type. (gcov_destroy_summary): Declare. (GCOV_TAG_SUMMARY_LENGTH): Update to include working set summary. (struct gcov_working_set_info): New structure. (struct gcov_ctr_summary): Include working set summary. * gcc/coverage.c (htab_counts_entry_del): Free working sets. (read_counts_file): Read and merge in working set summary info. * gcc/gcov-dump.c (tag_summary): Dump out working set summary info. --- This patch is available for review at http://codereview.appspot.com/6465057 Index: libgcc/libgcov.c =================================================================== --- libgcc/libgcov.c (revision 189420) +++ libgcc/libgcov.c (working copy) @@ -276,6 +276,252 @@ gcov_version (struct gcov_info *ptr, gcov_unsigned return 1; } +/* Structure used for each bucket of the log2 histogram of counter values. */ + +typedef struct +{ + /* The total number of BBs whose profile count falls within the bucket. */ + gcov_unsigned_t count; + /* The minimum profile count placed in this bucket. */ + gcov_type min_value; + /* This is the cumulative value of the profile counts in this histogram + bucket. */ + gcov_type cum_value; +} bucket_type; + +/* For a log2 scale histogram with each range split into 4 + linear sub-ranges, there will be at most GCOV_TYPE_SIZE - 1 log2 + ranges since the lowest 2 log2 values share the lowest 4 linear + sub-range (values 0 - 3). */ + +#define HISTOGRAM_SIZE (GCOV_TYPE_SIZE - 1) * 4 + +/* Determine the index into histogram for VALUE. */ + +static unsigned +histo_index(gcov_type value) +{ + gcov_type_unsigned v = (gcov_type_unsigned)value; + unsigned r = 0; + unsigned prev2bits = 0; + + /* Find index into log2 scale histogram, where each of the log2 + sized buckets is divided into 4 linear sub-buckets for better + focus in the higher buckets. */ + + /* Find the place of the most-significant bit set. */ + if (v > 0) + r = GCOV_TYPE_SIZE - 1 - __builtin_clzll (v); + + /* If at most the 2 least significant bits are set (value is + 0 - 3) then that value is our index into the lowest set of + four buckets. */ + if (r < 2) + return (unsigned)value; + + gcc_assert (r < 64); + + /* Find the two next most significant bits to determine which + of the four linear sub-buckets to select. */ + prev2bits = (v >> (r - 2)) & 0x3; + /* Finally, compose the final bucket index from the log2 index and + the next 2 bits. The minimum r value at this point is 2 since we + returned above if r was 2 or more, so the minimum bucket at this + point is 4. */ + return (r - 1) * 4 + prev2bits; +} + +/* Insert counter VALUE into HISTOGRAM. */ + +static void +histogram_insert(bucket_type *histogram, gcov_type value) +{ + unsigned i; + + i = histo_index(value); + gcc_assert (i < HISTOGRAM_SIZE); + + histogram[i].count++; + histogram[i].cum_value += value; + if (value < histogram[i].min_value) + histogram[i].min_value = value; +} + +/* Determines the working set statistics to place in the summary SUM. This is + an array of information for a range of percentages of the total execution + count (sum_all), and includes the number of counters required to cover that + working set percentage and the minimum counter value in that working set. */ + +static void +gcov_compute_working_set (struct gcov_summary *sum) +{ + struct gcov_info *gi_ptr; + const struct gcov_fn_info *gfi_ptr; + const struct gcov_ctr_info *ci_ptr; + struct gcov_ctr_summary *cs_ptr; + unsigned t_ix, f_ix, ws_ix, ctr_info_ix, ix; + gcov_unsigned_t c_num, count; + int h_ix; + bucket_type histogram[HISTOGRAM_SIZE]; + gcov_type *working_set_cum_values; + gcov_type ws_cum_hotness_incr; + gcov_type cum, tmp_cum; + char *ws_entries_str; + unsigned num_ws_entries; + + /* This currently only applies to arc counters. */ + t_ix = GCOV_COUNTER_ARCS; + + /* First check if there are any counts recorded for this counter. */ + cs_ptr = &(sum->ctrs[t_ix]); + if (!cs_ptr->num) + return; + + for (h_ix = 0; h_ix < HISTOGRAM_SIZE; h_ix++) + { + histogram[h_ix].count = 0; + histogram[h_ix].min_value = cs_ptr->run_max; + histogram[h_ix].cum_value = 0; + } + +#define MAX_WS_SUMMARY_ENTRIES 128 + ws_entries_str = getenv ("GCOV_WORKING_SET_ENTRIES"); + if (ws_entries_str && strlen (ws_entries_str)) + { + num_ws_entries = atoi (ws_entries_str); + /* Limit the summary information to a reasonable amount. + The maximum value gives statistics for more than every 1% + of the sum_all, which should be sufficient granularity for + optimizations. It also ensures that the last entry of 99.9% + is the largest value. */ + if (num_ws_entries > MAX_WS_SUMMARY_ENTRIES) + num_ws_entries = MAX_WS_SUMMARY_ENTRIES; + } + else + num_ws_entries = MAX_WS_SUMMARY_ENTRIES; + + cs_ptr->working_set_count = num_ws_entries; + + if (! num_ws_entries) + return; + + /* Next fill in an array of the cumulative hotness values corresponding + to each working set summary entry we are going to compute below. */ + working_set_cum_values + = (gcov_type *) malloc (sizeof (gcov_type) * num_ws_entries); + + /* Compute the amount of sum_all that the cumulative hotness grows + by in each successive working set entry, which depends on the + number of working set entries requested. By default num_ws_entries + is a power-of-two so that divide becomes a shift. */ + ws_cum_hotness_incr = cs_ptr->sum_all / num_ws_entries; + + /* Skip 0% statistics, which can be extrapolated from the + rest of the summary data. */ + cum = ws_cum_hotness_incr; + for (ws_ix = 0; ws_ix < num_ws_entries; ws_ix++, cum += ws_cum_hotness_incr) + working_set_cum_values[ws_ix] = cum; + /* The last summary entry is reserved for (roughly) 99.9% of the + working set. Divide by 1024 so it becomes a shift, which gives + almost exactly 99.9%. */ + working_set_cum_values[num_ws_entries-1] + = cs_ptr->sum_all - cs_ptr->sum_all/1024; + + /* Next, walk through all the per-object structures and record each of + the count values in histogram. */ + for (gi_ptr = gcov_list; gi_ptr; gi_ptr = gi_ptr->next) + { + if (!gi_ptr->merge[t_ix]) + continue; + + /* Find the appropriate index into the gcov_ctr_info array + for the counter we are currently working on based on the + existence of the merge function pointer for this object. */ + for (ix = 0, ctr_info_ix = 0; ix < t_ix; ix++) + { + if (gi_ptr->merge[ix]) + ctr_info_ix++; + } + for (f_ix = 0; f_ix != gi_ptr->n_functions; f_ix++) + { + gfi_ptr = gi_ptr->functions[f_ix]; + + if (!gfi_ptr || gfi_ptr->key != gi_ptr) + continue; + + ci_ptr = &gfi_ptr->ctrs[ctr_info_ix]; + for (ix = 0; ix < ci_ptr->num; ix++) + histogram_insert(histogram, ci_ptr->values[ix]); + } + } + + /* Next, walk through the histogram in decending order of hotness + and compute the statistics for the working set summary array. + As histogram entries are accumulated, we check to see which + working set entries have had their expected cum_value reached + and fill them in, walking the working set entries in increasing + size of cum_value. */ + cs_ptr->working_sets = (gcov_ws_info_t *) malloc ( + num_ws_entries * sizeof (gcov_ws_info_t)); + ws_ix = 0; /* The current entry into the working set array. */ + cum = 0; /* The current accumulated counter sum. */ + count = 0; /* The current accumulated count of block counters. */ + for (h_ix = HISTOGRAM_SIZE - 1; h_ix > 0 && ws_ix < num_ws_entries; h_ix--) + { + /* If we haven't reached the required cumulative counter value for + the current working set percentage, simply accumulate this histogram + entry into the running sums and continue to the next histogram + entry. */ + if (cum + histogram[h_ix].cum_value < working_set_cum_values[ws_ix]) + { + cum += histogram[h_ix].cum_value; + count += histogram[h_ix].count; + continue; + } + + /* If adding the current histogram entry's cumulative counter value + causes us to exceed the current working set size, then estimate + how many of this histogram entry's counter values are required to + reach the working set size, and fill in working set entries + as we reach their expected cumulative value. */ + for (c_num = 0, tmp_cum = cum; + c_num < histogram[h_ix].count && ws_ix < num_ws_entries; + c_num++) + { + count++; + /* If we haven't reached the last histogram entry counter, add + in the minimum value again. This will underestimate the + cumulative sum so far, because many of the counter values in this + entry may have been larger than the minimum. We could add in the + average value every time, but that would require an expensive + divide operation. */ + if (c_num + 1 < histogram[h_ix].count) + tmp_cum += histogram[h_ix].min_value; + /* If we have reached the last histogram entry counter, then add + in the entire cumulative value. */ + else + tmp_cum = cum + histogram[h_ix].cum_value; + + /* Next walk through successive working set entries and fill in + the statistics for any whose size we have reached by accumulating + this histogram counter. */ + while (tmp_cum >= working_set_cum_values[ws_ix] + && ws_ix < num_ws_entries) + { + cs_ptr->working_sets[ws_ix].num_counters = count; + cs_ptr->working_sets[ws_ix].min_bb_counter + = histogram[h_ix].min_value; + ws_ix++; + } + } + /* Finally, update the running cumulative value since we were + using a temporary above. */ + cum += histogram[h_ix].cum_value; + } + gcc_assert (ws_ix == num_ws_entries); + free (working_set_cum_values); +} + /* Dump the coverage counts. 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 @@ -347,6 +593,7 @@ gcov_exit (void) } } } + gcov_compute_working_set (&this_prg); { /* Check if the level of dirs to strip off specified. */ @@ -389,7 +636,7 @@ gcov_exit (void) /* Now merge each file. */ for (gi_ptr = gcov_list; gi_ptr; gi_ptr = gi_ptr->next) { - unsigned n_counts; + unsigned n_counts, ws_cnt; struct gcov_summary prg; /* summary for this object over all program. */ struct gcov_ctr_summary *cs_prg, *cs_tprg, *cs_all; @@ -482,8 +729,6 @@ gcov_exit (void) f_ix--; length = gcov_read_unsigned (); - if (length != GCOV_TAG_SUMMARY_LENGTH) - goto read_mismatch; gcov_read_summary (&tmp); if ((error = gcov_is_error ())) goto read_error; @@ -597,8 +842,35 @@ gcov_exit (void) if (gi_ptr->merge[t_ix]) { + ws_cnt = cs_tprg->working_set_count; if (!cs_prg->runs++) - cs_prg->num = cs_tprg->num; + { + cs_prg->num = cs_tprg->num; + /* Allocate the working set array for the merged summary. */ + if (ws_cnt) + { + cs_prg->working_set_count = ws_cnt; + cs_prg->working_sets = (gcov_ws_info_t *) malloc ( + ws_cnt * sizeof (gcov_ws_info_t)); + } + } + else if (cs_prg->num != cs_tprg->num + || ws_cnt != cs_prg->working_set_count) + goto read_mismatch; + /* Copy over this run's working set information if either this is + the first run, the total size of the profile (sum_all) is much + (50%) larger for this run (need to check this before updating + cs_prg->sum_all below), or this run has a larger working + set in the largest (99.99% of sum_all) bucket. */ + if (ws_cnt + && (cs_prg->runs == 1 + || (cs_tprg->sum_all + > (cs_prg->sum_all + cs_prg->sum_all / 2)) + || (cs_tprg->working_sets[ws_cnt - 1].num_counters + > cs_prg->working_sets[ws_cnt - 1].num_counters))) + memcpy (cs_prg->working_sets, + cs_tprg->working_sets, + ws_cnt * sizeof (gcov_ws_info_t)); cs_prg->sum_all += cs_tprg->sum_all; if (cs_prg->run_max < cs_tprg->run_max) cs_prg->run_max = cs_tprg->run_max; @@ -696,7 +968,11 @@ gcov_exit (void) "profiling:%s:Overflow writing\n" : "profiling:%s:Error writing\n", gi_filename); + + gcov_destroy_summary (&prg); } + + gcov_destroy_summary (&this_prg); } /* Reset all counters to zero. */ Index: gcc/gcov.c =================================================================== --- gcc/gcov.c (revision 189924) +++ gcc/gcov.c (working copy) @@ -1264,6 +1264,7 @@ read_count_file (function_t *fns) struct gcov_summary summary; gcov_read_summary (&summary); object_runs += summary.ctrs[GCOV_COUNTER_ARCS].runs; + gcov_destroy_summary (&summary); program_count++; } else if (tag == GCOV_TAG_FUNCTION && !length) Index: gcc/gcov-io.c =================================================================== --- gcc/gcov-io.c (revision 189924) +++ gcc/gcov-io.c (working copy) @@ -368,10 +368,13 @@ gcov_write_tag_length (gcov_unsigned_t tag, gcov_u GCOV_LINKAGE void gcov_write_summary (gcov_unsigned_t tag, const struct gcov_summary *summary) { - unsigned ix; + unsigned ix, ws_ix, ws_cnt; const struct gcov_ctr_summary *csum; - gcov_write_tag_length (tag, GCOV_TAG_SUMMARY_LENGTH); + for (csum = summary->ctrs, ix = GCOV_COUNTERS_SUMMABLE, ws_cnt = 0; + ix--; csum++) + ws_cnt += csum->working_set_count; + gcov_write_tag_length (tag, GCOV_TAG_SUMMARY_LENGTH(ws_cnt)); gcov_write_unsigned (summary->checksum); for (csum = summary->ctrs, ix = GCOV_COUNTERS_SUMMABLE; ix--; csum++) { @@ -380,6 +383,12 @@ gcov_write_summary (gcov_unsigned_t tag, const str gcov_write_counter (csum->sum_all); gcov_write_counter (csum->run_max); gcov_write_counter (csum->sum_max); + gcov_write_unsigned (csum->working_set_count); + for (ws_ix = 0; ws_ix < csum->working_set_count; ws_ix++) + { + gcov_write_unsigned (csum->working_sets[ws_ix].num_counters); + gcov_write_counter (csum->working_sets[ws_ix].min_bb_counter); + } } } #endif /* IN_LIBGCOV */ @@ -488,7 +497,7 @@ gcov_read_string (void) GCOV_LINKAGE void gcov_read_summary (struct gcov_summary *summary) { - unsigned ix; + unsigned ix, ws_ix; struct gcov_ctr_summary *csum; summary->checksum = gcov_read_unsigned (); @@ -499,9 +508,35 @@ gcov_read_summary (struct gcov_summary *summary) csum->sum_all = gcov_read_counter (); csum->run_max = gcov_read_counter (); csum->sum_max = gcov_read_counter (); + csum->working_set_count = gcov_read_unsigned (); + csum->working_sets +#if IN_LIBGCOV + = (gcov_ws_info_t *) malloc (csum->working_set_count * +#else + = (gcov_ws_info_t *) xmalloc (csum->working_set_count * +#endif + sizeof (gcov_ws_info_t)); + for (ws_ix = 0; ws_ix < csum->working_set_count; ws_ix++) + { + csum->working_sets[ws_ix].num_counters = gcov_read_unsigned (); + csum->working_sets[ws_ix].min_bb_counter = gcov_read_counter (); + } } } +GCOV_LINKAGE void +gcov_destroy_summary (struct gcov_summary *summary) +{ + unsigned ix; + struct gcov_ctr_summary *csum; + + for (csum = summary->ctrs, ix = GCOV_COUNTERS_SUMMABLE; ix--; csum++) + { + if (csum->working_set_count) + free (csum->working_sets); + } +} + #if !IN_LIBGCOV /* Reset to a known position. BASE should have been obtained from gcov_position, LENGTH should be a record length. */ Index: gcc/gcov-io.h =================================================================== --- gcc/gcov-io.h (revision 189924) +++ gcc/gcov-io.h (working copy) @@ -139,7 +139,9 @@ see the files COPYING3 and COPYING.RUNTIME respect counts: header int64:count* summary: int32:checksum {count-summary}GCOV_COUNTERS_SUMMABLE count-summary: int32:num int32:runs int64:sum - int64:max int64:sum_max + int64:max int64:sum_max working-set-summary + working-set-summary: int32:count working-sets* + working-sets: int32:num int64:min The ANNOUNCE_FUNCTION record is the same as that in the note file, but without the source location. The COUNTS gives the @@ -171,8 +173,10 @@ typedef unsigned gcov_unsigned_t __attribute__ ((m typedef unsigned gcov_position_t __attribute__ ((mode (SI))); #if LONG_LONG_TYPE_SIZE > 32 typedef signed gcov_type __attribute__ ((mode (DI))); +typedef unsigned gcov_type_unsigned __attribute__ ((mode (DI))); #else typedef signed gcov_type __attribute__ ((mode (SI))); +typedef unsigned gcov_type_unsigned __attribute__ ((mode (SI))); #endif #else #if BITS_PER_UNIT == 16 @@ -180,16 +184,20 @@ typedef unsigned gcov_unsigned_t __attribute__ ((m typedef unsigned gcov_position_t __attribute__ ((mode (HI))); #if LONG_LONG_TYPE_SIZE > 32 typedef signed gcov_type __attribute__ ((mode (SI))); +typedef unsigned gcov_type_unsigned __attribute__ ((mode (SI))); #else typedef signed gcov_type __attribute__ ((mode (HI))); +typedef unsigned gcov_type_unsigned __attribute__ ((mode (HI))); #endif #else typedef unsigned gcov_unsigned_t __attribute__ ((mode (QI))); typedef unsigned gcov_position_t __attribute__ ((mode (QI))); #if LONG_LONG_TYPE_SIZE > 32 typedef signed gcov_type __attribute__ ((mode (HI))); +typedef unsigned gcov_type_unsigned __attribute__ ((mode (HI))); #else typedef signed gcov_type __attribute__ ((mode (QI))); +typedef unsigned gcov_type_unsigned __attribute__ ((mode (QI))); #endif #endif #endif @@ -213,8 +221,6 @@ typedef HOST_WIDEST_INT gcov_type; #if IN_GCOV > 0 #include #endif -#else /*!IN_GCOV */ -#define GCOV_TYPE_SIZE (LONG_LONG_TYPE_SIZE > 32 ? 64 : 32) #endif #if defined (HOST_HAS_F_SETLKW) @@ -225,6 +231,8 @@ typedef HOST_WIDEST_INT gcov_type; #endif /* !IN_LIBGCOV */ +#define GCOV_TYPE_SIZE (LONG_LONG_TYPE_SIZE > 32 ? 64 : 32) + /* In gcov we want function linkage to be static. In the compiler we want it extern, so that they can be accessed from elsewhere. In libgcov we need these functions to be extern, so prefix them with __gcov. In @@ -248,6 +256,7 @@ typedef HOST_WIDEST_INT gcov_type; #define gcov_read_unsigned __gcov_read_unsigned #define gcov_read_counter __gcov_read_counter #define gcov_read_summary __gcov_read_summary +#define gcov_destroy_summary __gcov_destroy_summary /* Poison these, so they don't accidentally slip in. */ #pragma GCC poison gcov_write_string gcov_write_tag gcov_write_length @@ -309,9 +318,10 @@ typedef HOST_WIDEST_INT gcov_type; #define GCOV_TAG_COUNTER_NUM(LENGTH) ((LENGTH) / 2) #define GCOV_TAG_OBJECT_SUMMARY ((gcov_unsigned_t)0xa1000000) /* Obsolete */ #define GCOV_TAG_PROGRAM_SUMMARY ((gcov_unsigned_t)0xa3000000) -#define GCOV_TAG_SUMMARY_LENGTH \ - (1 + GCOV_COUNTERS_SUMMABLE * (2 + 3 * 2)) +#define GCOV_TAG_SUMMARY_LENGTH(NUM) \ + (1 + GCOV_COUNTERS_SUMMABLE * (3 + 3 * 2) + (NUM) * 3) + /* Counters that are collected. */ #define GCOV_COUNTER_ARCS 0 /* Arc transitions. */ #define GCOV_COUNTERS_SUMMABLE 1 /* Counters which can be @@ -389,6 +399,16 @@ typedef HOST_WIDEST_INT gcov_type; /* Structured records. */ +/* Working set size statistics for a given percentage of the entire + profile (sum_all). */ +typedef struct gcov_working_set_info +{ + /* Number of hot counters included in this working set. */ + gcov_unsigned_t num_counters; + /* Smallest counter included in this working set. */ + gcov_type min_bb_counter; +} gcov_ws_info_t; + /* Cumulative counter data. */ struct gcov_ctr_summary { @@ -397,6 +417,8 @@ struct gcov_ctr_summary gcov_type sum_all; /* sum of all counters accumulated. */ gcov_type run_max; /* maximum value on a single run. */ gcov_type sum_max; /* sum of individual run max values. */ + gcov_unsigned_t working_set_count; /* number of working set infos. */ + gcov_ws_info_t *working_sets; /* array of working set info. */ }; /* Object & program summary record. */ @@ -552,6 +574,7 @@ static int gcov_is_error (void); GCOV_LINKAGE gcov_unsigned_t gcov_read_unsigned (void) ATTRIBUTE_HIDDEN; GCOV_LINKAGE gcov_type gcov_read_counter (void) ATTRIBUTE_HIDDEN; GCOV_LINKAGE void gcov_read_summary (struct gcov_summary *) ATTRIBUTE_HIDDEN; +GCOV_LINKAGE void gcov_destroy_summary (struct gcov_summary *) ATTRIBUTE_HIDDEN; #if IN_LIBGCOV /* Available only in libgcov */ Index: gcc/coverage.c =================================================================== --- gcc/coverage.c (revision 189924) +++ gcc/coverage.c (working copy) @@ -172,6 +172,8 @@ htab_counts_entry_del (void *of) counts_entry_t *const entry = (counts_entry_t *) of; free (entry->counts); + if (entry->summary.working_sets) + free (entry->summary.working_sets); free (entry); } @@ -247,12 +249,40 @@ read_counts_file (void) gcov_read_summary (&sum); for (ix = 0; ix != GCOV_COUNTERS_SUMMABLE; ix++) { + gcov_unsigned_t ws_cnt = sum.ctrs[ix].working_set_count; + /* Allocate the working set array for a new summary. */ + if (ws_cnt && new_summary) + { + summary.ctrs[ix].working_set_count = ws_cnt; + summary.ctrs[ix].working_sets + = (gcov_ws_info_t *) xmalloc (ws_cnt + * sizeof (gcov_ws_info_t)); + } + /* Copy over this summary's working set information if either + summary is new, the total size of the profile (sum_all) is much + (50%) larger for this summary (need to check this before + updating sum_all below), or this summary has a larger working + set in the largest (99.99% of sum_all) bucket. */ + if (ws_cnt + && ws_cnt == summary.ctrs[ix].working_set_count + && (new_summary + || (sum.ctrs[ix].sum_all + > (summary.ctrs[ix].sum_all + + summary.ctrs[ix].sum_all / 2)) + || (sum.ctrs[ix].working_sets[ws_cnt - 1].num_counters + > summary.ctrs[ix].working_sets[ws_cnt - 1].num_counters))) + { + memcpy (summary.ctrs[ix].working_sets, + sum.ctrs[ix].working_sets, + ws_cnt * sizeof (gcov_ws_info_t)); + } summary.ctrs[ix].runs += sum.ctrs[ix].runs; summary.ctrs[ix].sum_all += sum.ctrs[ix].sum_all; if (summary.ctrs[ix].run_max < sum.ctrs[ix].run_max) summary.ctrs[ix].run_max = sum.ctrs[ix].run_max; summary.ctrs[ix].sum_max += sum.ctrs[ix].sum_max; } + gcov_destroy_summary (&sum); new_summary = 0; } else if (GCOV_TAG_IS_COUNTER (tag) && fn_ident) Index: gcc/gcov-dump.c =================================================================== --- gcc/gcov-dump.c (revision 189924) +++ gcc/gcov-dump.c (working copy) @@ -447,7 +447,8 @@ tag_summary (const char *filename ATTRIBUTE_UNUSED unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED) { struct gcov_summary summary; - unsigned ix; + unsigned ix, jx, pctinc, pct; + gcov_ws_info_t *ws_info; gcov_read_summary (&summary); printf (" checksum=0x%08x", summary.checksum); @@ -465,5 +466,27 @@ tag_summary (const char *filename ATTRIBUTE_UNUSED (HOST_WIDEST_INT)summary.ctrs[ix].run_max); printf (", sum_max=" HOST_WIDEST_INT_PRINT_DEC, (HOST_WIDEST_INT)summary.ctrs[ix].sum_max); + if (! summary.ctrs[ix].working_set_count) + continue; + printf ("\n"); + print_prefix (filename, 0, 0); + printf ("\t\tworking set statistics:"); + /* Multiply the percentage by 100 to avoid float. */ + pctinc = 100 * 100 / summary.ctrs[ix].working_set_count; + for (jx = 0, pct = pctinc; jx < summary.ctrs[ix].working_set_count; + jx++, pct += pctinc) + { + if (jx == summary.ctrs[ix].working_set_count - 1) + pct = 9999; + ws_info = &summary.ctrs[ix].working_sets[jx]; + printf ("\n"); + print_prefix (filename, 0, 0); + /* Print out the percentage using int arithmatic to avoid float. */ + printf ("\t\t%u.%u%%: num counts=%u, min counter=" + HOST_WIDEST_INT_PRINT_DEC, + pct / 100, pct - pct / 100, + ws_info->num_counters, (HOST_WIDEST_INT)ws_info->min_bb_counter); + } } + gcov_destroy_summary (&summary); }