From 38fe12e377baa6c8dfe5845687e7ed3d1daf0769 Mon Sep 17 00:00:00 2001 From: marxin Date: Mon, 11 Nov 2013 23:21:02 +0000 Subject: [PATCH] Time profiler introduced. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@204690 138bc75d-0d04-0410-961f-82ee72b054a4 --- gcc/ChangeLog | 28 +++++++++++++ gcc/cgraph.c | 1 + gcc/cgraph.h | 2 + gcc/cgraphclones.c | 1 + gcc/gcov-io.h | 13 ++++-- gcc/lto-cgraph.c | 5 +++ gcc/lto/lto-symtab.c | 7 ++++ gcc/profile.c | 31 +++++++++++++-- gcc/testsuite/ChangeLog | 5 +++ gcc/testsuite/gcc.dg/tree-prof/time-profiler-1.c | 22 +++++++++++ gcc/testsuite/gcc.dg/tree-prof/time-profiler-2.c | 50 ++++++++++++++++++++++++ gcc/tree-profile.c | 37 +++++++++++++++++- gcc/value-prof.c | 23 ++++++++++- gcc/value-prof.h | 4 ++ libgcc/Makefile.in | 2 +- libgcc/libgcov.c | 38 ++++++++++++++++++ 16 files changed, 257 insertions(+), 12 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/tree-prof/time-profiler-1.c create mode 100644 gcc/testsuite/gcc.dg/tree-prof/time-profiler-2.c diff --git a/gcc/ChangeLog b/gcc/ChangeLog index bc418fea7ab..92bb7a11240 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,31 @@ +2013-11-11 Martin Liska + Jan Hubicka + + * cgraph.c (dump_cgraph_node): Profile dump added. + * cgraph.h (struct cgraph_node): New time profile variable added. + * cgraphclones.c (cgraph_clone_node): Time profile is cloned. + * gcov-io.h (gcov_type): New profiler type introduced. + * ipa-profile.c (lto_output_node): Streaming for time profile added. + (input_node): Time profiler is read from LTO stream. + * predict.c (maybe_hot_count_p): Hot prediction changed. + * profile.c (instrument_values): New case for time profiler added. + (compute_value_histograms): Read of time profile. + * tree-pretty-print.c (dump_function_header): Time profiler is dumped. + * tree-profile.c (init_ic_make_global_vars): Time profiler function added. + (gimple_init_edge_profiler): TP function instrumentation. + (gimple_gen_time_profiler): New. + * value-prof.c (gimple_add_histogram_value): Support for time profiler + added. + (dump_histogram_value): TP type added to dumps. + (visit_hist): More sensitive check that takes TP into account. + (gimple_find_values_to_profile): TP instrumentation. + * value-prof.h (hist_type): New histogram type added. + (struct histogram_value_t): Pointer to struct function added. + * libgcc/Makefile.in: New GCOV merge function for TP added. + * libgcov.c: function_counter variable introduced. + (_gcov_merge_time_profile): New. + (_gcov_time_profiler): New. + 2013-11-11 Marc Glisse Jeff Law diff --git a/gcc/cgraph.c b/gcc/cgraph.c index f3666fab6dd..385b11da797 100644 --- a/gcc/cgraph.c +++ b/gcc/cgraph.c @@ -1890,6 +1890,7 @@ dump_cgraph_node (FILE *f, struct cgraph_node *node) if (node->profile_id) fprintf (f, " Profile id: %i\n", node->profile_id); + fprintf (f, " First run: %i\n", node->tp_first_run); fprintf (f, " Function flags:"); if (node->count) fprintf (f, " executed "HOST_WIDEST_INT_PRINT_DEC"x", diff --git a/gcc/cgraph.h b/gcc/cgraph.h index 4f93713b889..dd99dc830b6 100644 --- a/gcc/cgraph.h +++ b/gcc/cgraph.h @@ -298,6 +298,8 @@ public: int uid; /* ID assigned by the profiling. */ unsigned int profile_id; + /* Time profiler: first run of function. */ + int tp_first_run; /* Set when decl is an abstract function pointed to by the ABSTRACT_DECL_ORIGIN of a reachable function. */ diff --git a/gcc/cgraphclones.c b/gcc/cgraphclones.c index 9bb5656fb07..f91fcfc6fd4 100644 --- a/gcc/cgraphclones.c +++ b/gcc/cgraphclones.c @@ -208,6 +208,7 @@ cgraph_clone_node (struct cgraph_node *n, tree decl, gcov_type count, int freq, new_node->frequency = n->frequency; new_node->clone = n->clone; new_node->clone.tree_map = NULL; + new_node->tp_first_run = n->tp_first_run; if (n->count) { if (new_node->count > n->count) diff --git a/gcc/gcov-io.h b/gcc/gcov-io.h index ea8d9a7b769..73a52798f0d 100644 --- a/gcc/gcov-io.h +++ b/gcc/gcov-io.h @@ -342,9 +342,10 @@ typedef unsigned HOST_WIDEST_INT gcov_type_unsigned; counter. */ #define GCOV_COUNTER_IOR 7 /* IOR of the all values passed to counter. */ -#define GCOV_LAST_VALUE_COUNTER 7 /* The last of counters used for value +#define GCOV_TIME_PROFILER 8 /* Time profile collecting first run of a function */ +#define GCOV_LAST_VALUE_COUNTER 8 /* The last of counters used for value profiling. */ -#define GCOV_COUNTERS 8 +#define GCOV_COUNTERS 9 /* Number of counters used for value profiling. */ #define GCOV_N_VALUE_COUNTERS \ @@ -352,7 +353,7 @@ typedef unsigned HOST_WIDEST_INT gcov_type_unsigned; /* A list of human readable names of the counters */ #define GCOV_COUNTER_NAMES {"arcs", "interval", "pow2", "single", \ - "delta", "indirect_call", "average", "ior"} + "delta", "indirect_call", "average", "ior", "time_profiler"} /* Names of merge functions for counters. */ #define GCOV_MERGE_FUNCTIONS {"__gcov_merge_add", \ @@ -362,7 +363,8 @@ typedef unsigned HOST_WIDEST_INT gcov_type_unsigned; "__gcov_merge_delta", \ "__gcov_merge_single", \ "__gcov_merge_add", \ - "__gcov_merge_ior"} + "__gcov_merge_ior", \ + "__gcov_merge_time_profile" } /* Convert a counter index to a tag. */ #define GCOV_TAG_FOR_COUNTER(COUNT) \ @@ -511,6 +513,8 @@ 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; +extern void __gcov_merge_time_profile (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); @@ -518,6 +522,7 @@ extern void __gcov_one_value_profiler (gcov_type *, gcov_type); extern void __gcov_indirect_call_profiler_v2 (gcov_type, void *); extern void __gcov_average_profiler (gcov_type *, gcov_type); extern void __gcov_ior_profiler (gcov_type *, gcov_type); +extern void __gcov_time_profiler (gcov_type *); #ifndef inhibit_libc /* The wrappers around some library functions.. */ diff --git a/gcc/lto-cgraph.c b/gcc/lto-cgraph.c index 6a52da8a662..99dbf96b7a5 100644 --- a/gcc/lto-cgraph.c +++ b/gcc/lto-cgraph.c @@ -482,6 +482,8 @@ lto_output_node (struct lto_simple_output_block *ob, struct cgraph_node *node, ref = LCC_NOT_FOUND; streamer_write_hwi_stream (ob->main_stream, ref); + streamer_write_hwi_stream (ob->main_stream, node->tp_first_run); + bp = bitpack_create (ob->main_stream); bp_pack_value (&bp, node->local.local, 1); bp_pack_value (&bp, node->externally_visible, 1); @@ -1077,7 +1079,10 @@ input_node (struct lto_file_decl_data *file_data, internal_error ("bytecode stream: found multiple instances of cgraph " "node with uid %d", node->uid); + node->tp_first_run = streamer_read_uhwi (ib); + bp = streamer_read_bitpack (ib); + input_overwrite_node (file_data, node, tag, &bp); /* Store a reference for now, and fix up later to be a pointer. */ diff --git a/gcc/lto/lto-symtab.c b/gcc/lto/lto-symtab.c index ced6cf97908..2ebc07d4fb4 100644 --- a/gcc/lto/lto-symtab.c +++ b/gcc/lto/lto-symtab.c @@ -29,6 +29,7 @@ along with GCC; see the file COPYING3. If not see #include "plugin-api.h" #include "lto-streamer.h" #include "ipa-utils.h" +#include "ipa-inline.h" /* Replace the cgraph node NODE with PREVAILING_NODE in the cgraph, merging all edges and removing the old node. */ @@ -84,6 +85,12 @@ lto_cgraph_replace_node (struct cgraph_node *node, if (node->decl != prevailing_node->decl) cgraph_release_function_body (node); + /* Time profile merging */ + if (node->tp_first_run) + prevailing_node->tp_first_run = prevailing_node->tp_first_run ? + MIN (prevailing_node->tp_first_run, node->tp_first_run) : + node->tp_first_run; + /* Finally remove the replaced node. */ cgraph_remove_node (node); } diff --git a/gcc/profile.c b/gcc/profile.c index 7118ac8ac29..9e50560830e 100644 --- a/gcc/profile.c +++ b/gcc/profile.c @@ -65,6 +65,7 @@ along with GCC; see the file COPYING3. If not see #include "tree-cfg.h" #include "cfgloop.h" #include "dumpfile.h" +#include "cgraph.h" #include "profile.h" @@ -188,6 +189,15 @@ instrument_values (histogram_values values) gimple_gen_ior_profiler (hist, t, 0); break; + case HIST_TYPE_TIME_PROFILE: + { + basic_block bb = split_edge (single_succ_edge (ENTRY_BLOCK_PTR)); + gimple_stmt_iterator gsi = gsi_start_bb (bb); + + gimple_gen_time_profiler (t, 0, gsi); + break; + } + default: gcc_unreachable (); } @@ -850,6 +860,7 @@ compute_value_histograms (histogram_values values, unsigned cfg_checksum, gcov_type *histogram_counts[GCOV_N_VALUE_COUNTERS]; gcov_type *act_count[GCOV_N_VALUE_COUNTERS]; gcov_type *aact_count; + struct cgraph_node *node; for (t = 0; t < GCOV_N_VALUE_COUNTERS; t++) n_histogram_counters[t] = 0; @@ -888,6 +899,7 @@ compute_value_histograms (histogram_values values, unsigned cfg_checksum, t = (int) hist->type; aact_count = act_count[t]; + if (act_count[t]) act_count[t] += hist->n_counters; @@ -895,9 +907,22 @@ compute_value_histograms (histogram_values values, unsigned cfg_checksum, hist->hvalue.counters = XNEWVEC (gcov_type, hist->n_counters); for (j = 0; j < hist->n_counters; j++) if (aact_count) - hist->hvalue.counters[j] = aact_count[j]; - else - hist->hvalue.counters[j] = 0; + hist->hvalue.counters[j] = aact_count[j]; + else + hist->hvalue.counters[j] = 0; + + /* Time profiler counter is not related to any statement, + so that we have to read the counter and set the value to + the corresponding call graph node. */ + if (hist->type == HIST_TYPE_TIME_PROFILE) + { + node = cgraph_get_node (hist->fun->decl); + + node->tp_first_run = hist->hvalue.counters[0]; + + if (dump_file) + fprintf (dump_file, "Read tp_first_run: %d\n", node->tp_first_run); + } } for (t = 0; t < GCOV_N_VALUE_COUNTERS; t++) diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 9a527e02050..849597c0d0c 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2013-11-11 Martin Liska + + * gcc.dg/time-profiler-1.c: New test. + * gcc.dg/time-profiler-2.c: Ditto. + 2013-11-11 Marc Glisse Jeff Law diff --git a/gcc/testsuite/gcc.dg/tree-prof/time-profiler-1.c b/gcc/testsuite/gcc.dg/tree-prof/time-profiler-1.c new file mode 100644 index 00000000000..c61b534a250 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-prof/time-profiler-1.c @@ -0,0 +1,22 @@ +/* { dg-options "-O2 -fdump-ipa-profile" } */ + +__attribute__ ((noinline)) +int foo() +{ + return 0; +} + +__attribute__ ((noinline)) +int bar() +{ + return 1; +} + +int main () +{ + return foo (); +} +/* { dg-final-use { scan-ipa-dump-times "Read tp_first_run: 0" 1 "profile"} } */ +/* { dg-final-use { scan-ipa-dump-times "Read tp_first_run: 1" 1 "profile"} } */ +/* { dg-final-use { scan-ipa-dump-times "Read tp_first_run: 2" 1 "profile"} } */ +/* { dg-final-use { cleanup-ipa-dump "profile" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-prof/time-profiler-2.c b/gcc/testsuite/gcc.dg/tree-prof/time-profiler-2.c new file mode 100644 index 00000000000..04113419753 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-prof/time-profiler-2.c @@ -0,0 +1,50 @@ +/* { dg-options "-O2 -fdump-ipa-profile" } */ + +#include + +__attribute__ ((noinline)) +int foo() +{ + return 1; +} + +__attribute__ ((noinline)) +int bar() +{ + return 1; +} + +__attribute__ ((noinline)) +int baz() +{ + return 1; +} + +__attribute__ ((noinline)) +int baz1() +{ + return 1; +} + +int main () +{ + int f = fork(); + int r = 0; + + foo (); + + if (f < 0) + return 1; /* Fork failed. */ + + if(f == 0) /* Child process. */ + r = bar() - foo(); + else /* Parent process. */ + r = foo() - foo(); + + return r; +} +/* { dg-final-use { scan-ipa-dump-times "Read tp_first_run: 0" 2 "profile"} } */ +/* { dg-final-use { scan-ipa-dump-times "Read tp_first_run: 1" 1 "profile"} } */ +/* { dg-final-use { scan-ipa-dump-times "Read tp_first_run: 2" 1 "profile"} } */ +/* { dg-final-use { scan-ipa-dump-times "Read tp_first_run: 3" 1 "profile"} } */ +/* { dg-final-use { cleanup-ipa-dump "profile" } } */ diff --git a/gcc/tree-profile.c b/gcc/tree-profile.c index e98ea686fbc..43d5b923e1f 100644 --- a/gcc/tree-profile.c +++ b/gcc/tree-profile.c @@ -51,9 +51,10 @@ static GTY(()) tree tree_interval_profiler_fn; static GTY(()) tree tree_pow2_profiler_fn; static GTY(()) tree tree_one_value_profiler_fn; static GTY(()) tree tree_indirect_call_profiler_fn; +static GTY(()) tree tree_time_profiler_fn; static GTY(()) tree tree_average_profiler_fn; static GTY(()) tree tree_ior_profiler_fn; - + static GTY(()) tree ic_void_ptr_var; static GTY(()) tree ic_gcov_type_ptr_var; @@ -63,7 +64,8 @@ static GTY(()) tree ptr_void; /* Add code: __thread gcov* __gcov_indirect_call_counters; // pointer to actual counter - __thread void* __gcov_indirect_call_callee; // actual callee address + __thread void* __gcov_indirect_call_callee; // actual callee address + __thread int __gcov_function_counter; // time profiler function counter */ static void init_ic_make_global_vars (void) @@ -145,6 +147,7 @@ gimple_init_edge_profiler (void) tree gcov_type_ptr; tree ic_profiler_fn_type; tree average_profiler_fn_type; + tree time_profiler_fn_type; if (!gcov_type_node) { @@ -222,6 +225,18 @@ gimple_init_edge_profiler (void) = tree_cons (get_identifier ("leaf"), NULL, DECL_ATTRIBUTES (tree_indirect_call_profiler_fn)); + /* void (*) (gcov_type *, gcov_type, void *) */ + time_profiler_fn_type + = build_function_type_list (void_type_node, + gcov_type_ptr, NULL_TREE); + tree_time_profiler_fn + = build_fn_decl ("__gcov_time_profiler", + time_profiler_fn_type); + TREE_NOTHROW (tree_time_profiler_fn) = 1; + DECL_ATTRIBUTES (tree_time_profiler_fn) + = tree_cons (get_identifier ("leaf"), NULL, + DECL_ATTRIBUTES (tree_time_profiler_fn)); + /* void (*) (gcov_type *, gcov_type) */ average_profiler_fn_type = build_function_type_list (void_type_node, @@ -247,6 +262,7 @@ gimple_init_edge_profiler (void) DECL_ASSEMBLER_NAME (tree_pow2_profiler_fn); DECL_ASSEMBLER_NAME (tree_one_value_profiler_fn); DECL_ASSEMBLER_NAME (tree_indirect_call_profiler_fn); + DECL_ASSEMBLER_NAME (tree_time_profiler_fn); DECL_ASSEMBLER_NAME (tree_average_profiler_fn); DECL_ASSEMBLER_NAME (tree_ior_profiler_fn); } @@ -455,6 +471,23 @@ gimple_gen_ic_func_profiler (void) gsi_insert_before (&gsi, stmt2, GSI_SAME_STMT); } +/* Output instructions as GIMPLE tree at the beginning for each function. + TAG is the tag of the section for counters, BASE is offset of the + counter position and GSI is the iterator we place the counter. */ + +void +gimple_gen_time_profiler (unsigned tag, unsigned base, + gimple_stmt_iterator &gsi) +{ + tree ref_ptr = tree_coverage_counter_addr (tag, base); + gimple call; + + ref_ptr = force_gimple_operand_gsi (&gsi, ref_ptr, + true, NULL_TREE, true, GSI_SAME_STMT); + call = gimple_build_call (tree_time_profiler_fn, 1, ref_ptr); + gsi_insert_before (&gsi, call, GSI_NEW_STMT); +} + /* Output instructions as GIMPLE trees for code to find the most common value of a difference between two evaluations of an expression. VALUE is the expression whose value is profiled. TAG is the tag of the diff --git a/gcc/value-prof.c b/gcc/value-prof.c index 0a9388285bb..40e72ab8c89 100644 --- a/gcc/value-prof.c +++ b/gcc/value-prof.c @@ -196,6 +196,7 @@ gimple_add_histogram_value (struct function *fun, gimple stmt, { hist->hvalue.next = gimple_histogram_value (fun, stmt); set_histogram_value (fun, stmt, hist); + hist->fun = fun; } @@ -338,6 +339,15 @@ dump_histogram_value (FILE *dump_file, histogram_value hist) } fprintf (dump_file, ".\n"); break; + case HIST_TYPE_TIME_PROFILE: + fprintf (dump_file, "Time profile "); + if (hist->hvalue.counters) + { + fprintf (dump_file, "time:"HOST_WIDEST_INT_PRINT_DEC, + (HOST_WIDEST_INT) hist->hvalue.counters[0]); + } + fprintf (dump_file, ".\n"); + break; case HIST_TYPE_MAX: gcc_unreachable (); } @@ -411,6 +421,7 @@ stream_in_histogram_value (struct lto_input_block *ib, gimple stmt) break; case HIST_TYPE_IOR: + case HIST_TYPE_TIME_PROFILE: ncounters = 1; break; case HIST_TYPE_MAX: @@ -496,7 +507,9 @@ visit_hist (void **slot, void *data) { struct pointer_set_t *visited = (struct pointer_set_t *) data; histogram_value hist = *(histogram_value *) slot; - if (!pointer_set_contains (visited, hist)) + + if (!pointer_set_contains (visited, hist) + && hist->type != HIST_TYPE_TIME_PROFILE) { error ("dead histogram"); dump_histogram_value (stderr, hist); @@ -1919,12 +1932,14 @@ gimple_find_values_to_profile (histogram_values *values) gimple_stmt_iterator gsi; unsigned i; histogram_value hist = NULL; - values->create (0); + FOR_EACH_BB (bb) for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi)) gimple_values_to_profile (gsi_stmt (gsi), values); + values->safe_push (gimple_alloc_histogram_value (cfun, HIST_TYPE_TIME_PROFILE, 0, 0)); + FOR_EACH_VEC_ELT (*values, i, hist) { switch (hist->type) @@ -1949,6 +1964,10 @@ gimple_find_values_to_profile (histogram_values *values) hist->n_counters = 3; break; + case HIST_TYPE_TIME_PROFILE: + hist->n_counters = 1; + break; + case HIST_TYPE_AVERAGE: hist->n_counters = 2; break; diff --git a/gcc/value-prof.h b/gcc/value-prof.h index 57f249d56ef..ef77af4395e 100644 --- a/gcc/value-prof.h +++ b/gcc/value-prof.h @@ -34,6 +34,7 @@ enum hist_type called in indirect call */ HIST_TYPE_AVERAGE, /* Compute average value (sum of all values). */ HIST_TYPE_IOR, /* Used to compute expected alignment. */ + HIST_TYPE_TIME_PROFILE, /* Used for time profile */ HIST_TYPE_MAX }; @@ -54,6 +55,7 @@ struct histogram_value_t } hvalue; enum hist_type type; /* Type of information to measure. */ unsigned n_counters; /* Number of required counters. */ + struct function *fun; union { struct @@ -97,6 +99,8 @@ extern void gimple_gen_pow2_profiler (histogram_value, unsigned, unsigned); extern void gimple_gen_one_value_profiler (histogram_value, unsigned, unsigned); extern void gimple_gen_ic_profiler (histogram_value, unsigned, unsigned); extern void gimple_gen_ic_func_profiler (void); +extern void gimple_gen_time_profiler (unsigned, unsigned, + gimple_stmt_iterator &); extern void gimple_gen_const_delta_profiler (histogram_value, unsigned, unsigned); extern void gimple_gen_average_profiler (histogram_value, unsigned, unsigned); diff --git a/libgcc/Makefile.in b/libgcc/Makefile.in index 354fb72d984..0d91cfc2d91 100644 --- a/libgcc/Makefile.in +++ b/libgcc/Makefile.in @@ -858,7 +858,7 @@ LIBGCOV = _gcov _gcov_merge_add _gcov_merge_single _gcov_merge_delta \ _gcov_execv _gcov_execvp _gcov_execve _gcov_reset _gcov_dump \ _gcov_interval_profiler _gcov_pow2_profiler _gcov_one_value_profiler \ _gcov_indirect_call_profiler _gcov_average_profiler _gcov_ior_profiler \ - _gcov_merge_ior _gcov_indirect_call_profiler_v2 + _gcov_merge_ior _gcov_time_profiler _gcov_indirect_call_profiler_v2 _gcov_merge_time_profile libgcov-objects = $(patsubst %,%$(objext),$(LIBGCOV)) diff --git a/libgcc/libgcov.c b/libgcc/libgcov.c index 3c39331e6ba..6450fd76548 100644 --- a/libgcc/libgcov.c +++ b/libgcc/libgcov.c @@ -80,6 +80,7 @@ void __gcov_merge_delta (gcov_type *counters __attribute__ ((unused)), #include #endif +extern gcov_type function_counter ATTRIBUTE_HIDDEN; extern void gcov_clear (void) ATTRIBUTE_HIDDEN; extern void gcov_exit (void) ATTRIBUTE_HIDDEN; extern int gcov_dump_complete ATTRIBUTE_HIDDEN; @@ -350,6 +351,10 @@ gcov_compute_histogram (struct gcov_summary *sum) } } + +/* Counter for first visit of each function. */ +gcov_type function_counter; + /* 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 @@ -974,6 +979,27 @@ __gcov_merge_ior (gcov_type *counters, unsigned n_counters) } #endif +/* Time profiles are merged so that minimum from all valid (greater than zero) + * is stored. There could be a fork that creates new counters. To have + * the profile stable, we chosen to pick the smallest function visit time. */ + +#ifdef L_gcov_merge_time_profile +void +__gcov_merge_time_profile (gcov_type *counters, unsigned n_counters) +{ + unsigned int i; + gcov_type value; + + for (i = 0; i < n_counters; i++) + { + value = gcov_read_counter (); + + if (value && (!counters[i] || value < counters[i])) + counters[i] = value; + } +} +#endif /* L_gcov_merge_time_profile */ + #ifdef L_gcov_merge_single /* The profile merging function for choosing the most common value. It is given an array COUNTERS of N_COUNTERS old counters and it @@ -1202,6 +1228,18 @@ __gcov_indirect_call_profiler_v2 (gcov_type value, void* cur_func) } #endif +#ifdef L_gcov_time_profiler + +/* Sets corresponding COUNTERS if there is no value. */ + +void +__gcov_time_profiler (gcov_type* counters) +{ + if (!counters[0]) + counters[0] = ++function_counter; +} +#endif + #ifdef L_gcov_average_profiler /* Increase corresponding COUNTER by VALUE. FIXME: Perhaps we want to saturate up. */ -- 2.11.4.GIT