Makefile.in: Fix dependence.

2014-10-06  Rong Xu  <xur@google.com>

	* gcc/Makefile.in: Fix dependence.
	* gcc/gcov-counter.def (GCOV_COUNTER_ICALL_TOPNV): Add
        indirect call topn profiler.
	* gcc/gcov-io.h: Ditto.
	* libgcc/Makefile.in: Ditto.
	* libgcc/libgcov-driver.c (gcov_sort_n_vals): New utility function.
	(gcov_sort_icall_topn_counter): Ditto.
	(gcov_sort_topn_counter_arrays): Ditto.
	(dump_one_gcov): Sort indirect_call topn counters.
	* libgcc/libgcov-merge.c (__gcov_merge_icall_topn): New merge
        function.
	* libgcc/libgcov-profiler.c (__gcov_topn_value_profiler_body): New
        utility function.
	(__gcov_indirect_call_topn_profiler): New profiler function.
	* libgcc/libgcov-util.c (__gcov_icall_topn_counter_op): New.
	* libgcc/libgcov.h: New decls.

From-SVN: r215962
This commit is contained in:
Rong Xu 2014-10-07 04:02:31 +00:00
parent c5b0abd3ef
commit afe0c5ee91
9 changed files with 322 additions and 4 deletions

View File

@ -2574,7 +2574,7 @@ gcov-dump$(exeext): $(GCOV_DUMP_OBJS) $(LIBDEPS)
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 $@ $<

View File

@ -52,3 +52,6 @@ DEF_GCOV_COUNTER(GCOV_COUNTER_IOR, "ior", _ior)
/* 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)

View File

@ -270,6 +270,12 @@ GCOV_COUNTERS
#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))

View File

@ -855,11 +855,12 @@ include $(iterator)
# 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

View File

@ -665,6 +665,85 @@ merge_summary (const char *filename, int run_counted,
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
@ -687,6 +766,8 @@ dump_one_gcov (struct gcov_info *gi_ptr, struct gcov_filename *gf,
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;

View File

@ -166,4 +166,67 @@ __gcov_merge_delta (gcov_type *counters, unsigned n_counters)
}
}
#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 */

View File

@ -93,6 +93,144 @@ __gcov_one_value_profiler (gcov_type *counters, gcov_type value)
}
#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. */
@ -118,8 +256,8 @@ __gcov_indirect_call_profiler (gcov_type* counter, gcov_type value,
&& *(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

View File

@ -746,6 +746,25 @@ __gcov_single_counter_op (gcov_type *counters, unsigned n_counters,
}
}
/* 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

View File

@ -100,6 +100,7 @@ typedef unsigned gcov_type_unsigned __attribute__ ((mode (QI)));
#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. */
@ -128,6 +129,7 @@ typedef unsigned gcov_position_t;
#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 ();
@ -261,6 +263,9 @@ extern void __gcov_merge_delta (gcov_type *, unsigned) ATTRIBUTE_HIDDEN;
/* 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);
@ -271,6 +276,8 @@ extern void __gcov_indirect_call_profiler_v2 (gcov_type, void *);
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.. */