GCOV_TOOL_DEP_FILES = $(srcdir)/../libgcc/libgcov-util.c gcov-io.c $(GCOV_IO_H) \
$(srcdir)/../libgcc/libgcov-driver.c $(srcdir)/../libgcc/libgcov-driver-system.c \
- $(srcdir)/../libgcc/libgcov-merge.c \
+ $(srcdir)/../libgcc/libgcov-merge.c $(srcdir)/../libgcc/libgcov.h \
$(SYSTEM_H) coretypes.h $(TM_H) $(CONFIG_H) version.h intl.h $(DIAGNOSTIC_H)
libgcov-util.o: $(srcdir)/../libgcc/libgcov-util.c $(GCOV_TOOL_DEP_FILES)
+$(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) -o $@ $<
/* Time profile collecting first run of a function */
DEF_GCOV_COUNTER(GCOV_TIME_PROFILER, "time_profiler", _time_profile)
+
+/* Top N value tracking for indirect calls. */
+DEF_GCOV_COUNTER(GCOV_COUNTER_ICALL_TOPNV, "indirect_call_topn", _icall_topn)
#define GCOV_N_VALUE_COUNTERS \
(GCOV_LAST_VALUE_COUNTER - GCOV_FIRST_VALUE_COUNTER + 1)
+/* The number of hottest callees to be tracked. */
+#define GCOV_ICALL_TOPN_VAL 2
+
+/* The number of counter entries per icall callsite. */
+#define GCOV_ICALL_TOPN_NCOUNTS (1 + GCOV_ICALL_TOPN_VAL * 4)
+
/* Convert a counter index to a tag. */
#define GCOV_TAG_FOR_COUNTER(COUNT) \
(GCOV_TAG_COUNTER_BASE + ((gcov_unsigned_t)(COUNT) << 17))
# Build libgcov components.
LIBGCOV_MERGE = _gcov_merge_add _gcov_merge_single _gcov_merge_delta \
- _gcov_merge_ior _gcov_merge_time_profile
+ _gcov_merge_ior _gcov_merge_time_profile _gcov_merge_icall_topn
LIBGCOV_PROFILER = _gcov_interval_profiler _gcov_pow2_profiler \
_gcov_one_value_profiler _gcov_indirect_call_profiler \
_gcov_average_profiler _gcov_ior_profiler \
- _gcov_indirect_call_profiler_v2 _gcov_time_profiler
+ _gcov_indirect_call_profiler_v2 _gcov_time_profiler \
+ _gcov_indirect_call_topn_profiler
LIBGCOV_INTERFACE = _gcov_dump _gcov_flush _gcov_fork \
_gcov_execl _gcov_execlp \
_gcov_execle _gcov_execv _gcov_execvp _gcov_execve _gcov_reset
return 0;
}
+
+/* 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
fn_buffer = 0;
sum_buffer = 0;
+ gcov_sort_topn_counter_arrays (gi_ptr);
+
error = gcov_exit_open_gcda_file (gi_ptr, gf);
if (error == -1)
return;
}
}
#endif /* L_gcov_merge_delta */
+
+#ifdef L_gcov_merge_icall_topn
+/* The profile merging function used for merging indirect call counts
+ This function is given array COUNTERS of N_COUNTERS old counters and it
+ reads the same number of counters from the gcov file. */
+
+void
+__gcov_merge_icall_topn (gcov_type *counters, unsigned n_counters)
+{
+ unsigned i, j, k, m;
+
+ gcc_assert (!(n_counters % GCOV_ICALL_TOPN_NCOUNTS));
+ for (i = 0; i < n_counters; i += GCOV_ICALL_TOPN_NCOUNTS)
+ {
+ gcov_type *value_array = &counters[i + 1];
+ unsigned tmp_size = 2 * (GCOV_ICALL_TOPN_NCOUNTS - 1);
+ gcov_type *tmp_array
+ = (gcov_type *) alloca (tmp_size * sizeof (gcov_type));
+
+ for (j = 0; j < tmp_size; j++)
+ tmp_array[j] = 0;
+
+ for (j = 0; j < GCOV_ICALL_TOPN_NCOUNTS - 1; j += 2)
+ {
+ tmp_array[j] = value_array[j];
+ tmp_array[j + 1] = value_array [j + 1];
+ }
+
+ /* Skip the number_of_eviction entry. */
+ gcov_get_counter ();
+ for (k = 0; k < GCOV_ICALL_TOPN_NCOUNTS - 1; k += 2)
+ {
+ int found = 0;
+ gcov_type global_id = gcov_get_counter_target ();
+ gcov_type call_count = gcov_get_counter ();
+ for (m = 0; m < j; m += 2)
+ {
+ if (tmp_array[m] == global_id)
+ {
+ found = 1;
+ tmp_array[m + 1] += call_count;
+ break;
+ }
+ }
+ if (!found)
+ {
+ tmp_array[j] = global_id;
+ tmp_array[j + 1] = call_count;
+ j += 2;
+ }
+ }
+ /* Now sort the temp array */
+ gcov_sort_n_vals (tmp_array, j);
+
+ /* Now copy back the top half of the temp array */
+ for (k = 0; k < GCOV_ICALL_TOPN_NCOUNTS - 1; k += 2)
+ {
+ value_array[k] = tmp_array[k];
+ value_array[k + 1] = tmp_array[k + 1];
+ }
+ }
+}
+#endif /* L_gcov_merge_icall_topn */
#endif /* inhibit_libc */
}
#endif
+#ifdef L_gcov_indirect_call_topn_profiler
+/* Tries to keep track the most frequent N values in the counters where
+ N is specified by parameter TOPN_VAL. To track top N values, 2*N counter
+ entries are used.
+ counter[0] --- the accumative count of the number of times one entry in
+ in the counters gets evicted/replaced due to limited capacity.
+ When this value reaches a threshold, the bottom N values are
+ cleared.
+ counter[1] through counter[2*N] records the top 2*N values collected so far.
+ Each value is represented by two entries: count[2*i+1] is the ith value, and
+ count[2*i+2] is the number of times the value is seen. */
+
+static void
+__gcov_topn_value_profiler_body (gcov_type *counters, gcov_type value)
+{
+ unsigned i, found = 0, have_zero_count = 0;
+ gcov_type *entry;
+ gcov_type *lfu_entry = &counters[1];
+ gcov_type *value_array = &counters[1];
+ gcov_type *num_eviction = &counters[0];
+ gcov_unsigned_t topn_val = GCOV_ICALL_TOPN_VAL;
+
+ /* There are 2*topn_val values tracked, each value takes two slots in the
+ counter array. */
+ for (i = 0; i < (topn_val << 2); i += 2)
+ {
+ entry = &value_array[i];
+ if (entry[0] == value)
+ {
+ entry[1]++ ;
+ found = 1;
+ break;
+ }
+ else if (entry[1] == 0)
+ {
+ lfu_entry = entry;
+ have_zero_count = 1;
+ }
+ else if (entry[1] < lfu_entry[1])
+ lfu_entry = entry;
+ }
+
+ if (found)
+ return;
+
+ /* lfu_entry is either an empty entry or an entry
+ with lowest count, which will be evicted. */
+ lfu_entry[0] = value;
+ lfu_entry[1] = 1;
+
+#define GCOV_ICALL_COUNTER_CLEAR_THRESHOLD 3000
+
+ /* Too many evictions -- time to clear bottom entries to
+ avoid hot values bumping each other out. */
+ if (!have_zero_count
+ && ++*num_eviction >= GCOV_ICALL_COUNTER_CLEAR_THRESHOLD)
+ {
+ unsigned i, j;
+ gcov_type *p, minv;
+ gcov_type* tmp_cnts
+ = (gcov_type *)alloca (topn_val * sizeof (gcov_type));
+
+ *num_eviction = 0;
+
+ for (i = 0; i < topn_val; i++)
+ tmp_cnts[i] = 0;
+
+ /* Find the largest topn_val values from the group of
+ 2*topn_val values and put them into tmp_cnts. */
+
+ for (i = 0; i < 2 * topn_val; i += 2)
+ {
+ p = 0;
+ for (j = 0; j < topn_val; j++)
+ {
+ if (!p || tmp_cnts[j] < *p)
+ p = &tmp_cnts[j];
+ }
+ if (value_array[i + 1] > *p)
+ *p = value_array[i + 1];
+ }
+
+ minv = tmp_cnts[0];
+ for (j = 1; j < topn_val; j++)
+ {
+ if (tmp_cnts[j] < minv)
+ minv = tmp_cnts[j];
+ }
+ /* Zero out low value entries. */
+ for (i = 0; i < 2 * topn_val; i += 2)
+ {
+ if (value_array[i + 1] < minv)
+ {
+ value_array[i] = 0;
+ value_array[i + 1] = 0;
+ }
+ }
+ }
+}
+
+/* These two variables are used to actually track caller and callee. Keep
+ them in TLS memory so races are not common (they are written to often).
+ The variables are set directly by GCC instrumented code, so declaration
+ here must match one in tree-profile.c. */
+
+#if defined(HAVE_CC_TLS) && !defined (USE_EMUTLS)
+__thread
+#endif
+gcov_type *__gcov_indirect_call_topn_counters ATTRIBUTE_HIDDEN;
+
+#if defined(HAVE_CC_TLS) && !defined (USE_EMUTLS)
+__thread
+#endif
+void *__gcov_indirect_call_topn_callee ATTRIBUTE_HIDDEN;
+
+#ifdef TARGET_VTABLE_USES_DESCRIPTORS
+#define VTABLE_USES_DESCRIPTORS 1
+#else
+#define VTABLE_USES_DESCRIPTORS 0
+#endif
+
+/* This fucntion is instrumented at function entry to track topn indirect
+ calls to CUR_FUNC. */
+
+void
+__gcov_indirect_call_topn_profiler (gcov_type value, void* cur_func)
+{
+ void *callee_func = __gcov_indirect_call_topn_callee;
+ /* If the C++ virtual tables contain function descriptors then one
+ function may have multiple descriptors and we need to dereference
+ the descriptors to see if they point to the same function. */
+ if (cur_func == callee_func
+ || (VTABLE_USES_DESCRIPTORS && callee_func
+ && *(void **) cur_func == *(void **) callee_func))
+ __gcov_topn_value_profiler_body (__gcov_indirect_call_topn_counters, value);
+}
+#endif
+
#ifdef L_gcov_indirect_call_profiler
/* This function exist only for workaround of binutils bug 14342.
Once this compatibility hack is obsolette, it can be removed. */
&& *(void **) cur_func == *(void **) callee_func))
__gcov_one_value_profiler_body (counter, value);
}
-
#endif
+
#ifdef L_gcov_indirect_call_profiler_v2
/* These two variables are used to actually track caller and callee. Keep
}
}
+/* Performing FN upon indirect-call profile counters. */
+
+static void
+__gcov_icall_topn_counter_op (gcov_type *counters, unsigned n_counters,
+ counter_op_fn fn, void *data1, void *data2)
+{
+ unsigned i;
+
+ gcc_assert (!(n_counters % GCOV_ICALL_TOPN_NCOUNTS));
+ for (i = 0; i < n_counters; i += GCOV_ICALL_TOPN_NCOUNTS)
+ {
+ unsigned j;
+ gcov_type *value_array = &counters[i + 1];
+
+ for (j = 0; j < GCOV_ICALL_TOPN_NCOUNTS - 1; j += 2)
+ value_array[j + 1] = fn (value_array[j + 1], data1, data2);
+ }
+}
+
/* Scaling the counter value V by multiplying *(float*) DATA1. */
static 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_sort_n_vals __gcov_sort_n_vals
#else /* IN_GCOV_TOOL */
/* About the host. */
#define L_gcov_merge_delta 1
#define L_gcov_merge_ior 1
#define L_gcov_merge_time_profile 1
+#define L_gcov_merge_icall_topn 1
extern gcov_type gcov_read_counter_mem ();
extern unsigned gcov_get_merge_weight ();
/* The merge function that just ors the counters together. */
extern void __gcov_merge_ior (gcov_type *, unsigned) ATTRIBUTE_HIDDEN;
+/* The merge function is used for topn indirect call counters. */
+extern void __gcov_merge_icall_topn (gcov_type *, unsigned) ATTRIBUTE_HIDDEN;
+
/* The profiler functions. */
extern void __gcov_interval_profiler (gcov_type *, gcov_type, int, unsigned);
extern void __gcov_pow2_profiler (gcov_type *, gcov_type);
extern void __gcov_time_profiler (gcov_type *);
extern void __gcov_average_profiler (gcov_type *, gcov_type);
extern void __gcov_ior_profiler (gcov_type *, gcov_type);
+extern void __gcov_indirect_call_topn_profiler (gcov_type, void *);
+extern void gcov_sort_n_vals (gcov_type *, int);
#ifndef inhibit_libc
/* The wrappers around some library functions.. */