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 <sys/types.h>
 #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);
 }
