From 07417085a14349cde788c5cc10663815da40c26f Mon Sep 17 00:00:00 2001 From: Ken Raeburn Date: Thu, 30 Jul 1998 10:38:22 +0000 Subject: [PATCH] Function entry/exit profiling instrumentation: * expr.h (profile_function_entry_libfunc, profile_function_exit_libfunc): Declare new variables. * optabs.c: Define them here. (init_optabs): Initialize them. * tree.h (struct tree_decl): New flag no_instrument_function_entry_exit. (DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT): New accessor macro. * c-decl.c (duplicate_decls): Merge it. * c-common.c (enum attrs): New value A_NO_INSTRUMENT_FUNCTION. (init_attributes): Use it for "no_instrument_function". (decl_attributes): Handle it, for functions that have not yet been compiled. Set decl flag. * flags.h (flag_instrument_function_entry_exit): Declare new variable. * toplev.c (flag_instrument_function_entry_exit): Define it here. (f_options): New option "instrument-functions". * function.h (struct function): New field instrument_entry_exit. * function.c (current_function_instrument_entry_exit): New variable. (push_function_context_to, pop_function_context_from): Save and restore. (expand_function_start): Set current_ variable, maybe emit return label and entry profile call. (expand_function_end): Maybe emit exit profile call. Testsuite: * gcc.c-torture/special/eeprof-1.c: New test, for -finstrument-functions. * gcc.c-torture/special/special.exp: Run it. From-SVN: r21495 --- gcc/ChangeLog | 28 ++++++++ gcc/c-common.c | 19 ++++++ gcc/c-decl.c | 3 + gcc/expr.h | 4 ++ gcc/extend.texi | 11 ++- gcc/flags.h | 3 + gcc/function.c | 43 +++++++++++- gcc/function.h | 1 + gcc/invoke.texi | 35 ++++++++++ gcc/optabs.c | 9 +++ gcc/testsuite/ChangeLog | 6 ++ .../gcc.c-torture/special/eeprof-1.c | 67 +++++++++++++++++++ .../gcc.c-torture/special/special.exp | 2 + gcc/toplev.c | 7 +- gcc/tree.h | 6 +- 15 files changed, 239 insertions(+), 5 deletions(-) create mode 100644 gcc/testsuite/gcc.c-torture/special/eeprof-1.c diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 8ae4b2a1fd5..88fb4937ce5 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,31 @@ +Thu Jul 30 13:08:07 1998 Ken Raeburn + + Function entry/exit profiling instrumentation: + * expr.h (profile_function_entry_libfunc, + profile_function_exit_libfunc): Declare new variables. + * optabs.c: Define them here. + (init_optabs): Initialize them. + * tree.h (struct tree_decl): New flag + no_instrument_function_entry_exit. + (DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT): New accessor macro. + * c-decl.c (duplicate_decls): Merge it. + * c-common.c (enum attrs): New value A_NO_INSTRUMENT_FUNCTION. + (init_attributes): Use it for "no_instrument_function". + (decl_attributes): Handle it, for functions that have not yet been + compiled. Set decl flag. + * flags.h (flag_instrument_function_entry_exit): Declare new + variable. + * toplev.c (flag_instrument_function_entry_exit): Define it here. + (f_options): New option "instrument-functions". + * function.h (struct function): New field instrument_entry_exit. + * function.c (current_function_instrument_entry_exit): New + variable. + (push_function_context_to, pop_function_context_from): Save and + restore. + (expand_function_start): Set current_ variable, maybe emit return + label and entry profile call. + (expand_function_end): Maybe emit exit profile call. + Thu Jul 30 00:58:34 1998 Jeffrey A Law (law@cygnus.com) * i386.md (movqi): When optimizing a load of (const_int 1) into a diff --git a/gcc/c-common.c b/gcc/c-common.c index 245fedb97be..8405a41a2c9 100644 --- a/gcc/c-common.c +++ b/gcc/c-common.c @@ -50,6 +50,7 @@ extern struct obstack permanent_obstack; int skip_evaluation; enum attrs {A_PACKED, A_NOCOMMON, A_COMMON, A_NORETURN, A_CONST, A_T_UNION, + A_NO_INSTRUMENT_FUNCTION, A_CONSTRUCTOR, A_DESTRUCTOR, A_MODE, A_SECTION, A_ALIGNED, A_UNUSED, A_FORMAT, A_FORMAT_ARG, A_WEAK, A_ALIAS}; @@ -382,6 +383,7 @@ init_attributes () add_attribute (A_FORMAT_ARG, "format_arg", 1, 1, 1); add_attribute (A_WEAK, "weak", 0, 0, 1); add_attribute (A_ALIAS, "alias", 1, 1, 1); + add_attribute (A_NO_INSTRUMENT_FUNCTION, "no_instrument_function", 0, 0, 1); } /* Process the attributes listed in ATTRIBUTES and PREFIX_ATTRIBUTES @@ -856,6 +858,23 @@ decl_attributes (node, attributes, prefix_attributes) else warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name)); break; + + case A_NO_INSTRUMENT_FUNCTION: + if (TREE_CODE (decl) != FUNCTION_DECL) + { + error_with_decl (decl, + "`%s' attribute applies only to functions", + IDENTIFIER_POINTER (name)); + } + else if (DECL_INITIAL (decl)) + { + error_with_decl (decl, + "can't set `%s' attribute after definition", + IDENTIFIER_POINTER (name)); + } + else + DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (decl) = 1; + break; } } } diff --git a/gcc/c-decl.c b/gcc/c-decl.c index 3af985d732d..a4b87860e66 100644 --- a/gcc/c-decl.c +++ b/gcc/c-decl.c @@ -1931,6 +1931,9 @@ duplicate_decls (newdecl, olddecl, different_binding_level) { DECL_STATIC_CONSTRUCTOR(newdecl) |= DECL_STATIC_CONSTRUCTOR(olddecl); DECL_STATIC_DESTRUCTOR (newdecl) |= DECL_STATIC_DESTRUCTOR (olddecl); + + DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (newdecl) + |= DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (olddecl); } pop_obstacks (); diff --git a/gcc/expr.h b/gcc/expr.h index a53a036a15a..9e43741f26e 100644 --- a/gcc/expr.h +++ b/gcc/expr.h @@ -532,6 +532,10 @@ extern rtx chkr_set_right_libfunc; extern rtx chkr_copy_bitmap_libfunc; extern rtx chkr_check_exec_libfunc; extern rtx chkr_check_str_libfunc; + +/* For instrument-functions. */ +extern rtx profile_function_entry_libfunc; +extern rtx profile_function_exit_libfunc; typedef rtx (*rtxfun) PROTO ((rtx)); diff --git a/gcc/extend.texi b/gcc/extend.texi index 736f4220c5a..0fb01f444f5 100644 --- a/gcc/extend.texi +++ b/gcc/extend.texi @@ -1286,8 +1286,9 @@ carefully. The keyword @code{__attribute__} allows you to specify special attributes when making a declaration. This keyword is followed by an -attribute specification inside double parentheses. Eight attributes, -@code{noreturn}, @code{const}, @code{format}, @code{section}, +attribute specification inside double parentheses. Nine attributes, +@code{noreturn}, @code{const}, @code{format}, +@code{no_instrument_function}, @code{section}, @code{constructor}, @code{destructor}, @code{unused} and @code{weak} are currently defined for functions. Other attributes, including @code{section} are supported for variables declarations (@pxref{Variable @@ -1447,6 +1448,12 @@ operands are a call to one of your own function. The compiler always treats @code{gettext}, @code{dgettext}, and @code{dcgettext} in this manner. +@item no_instrument_function +@cindex @code{no_instrument_function} function attribute +If @samp{-finstrument-functions} is given, profiling function calls will +be generated at entry and exit of most user-compiled functions. +Functions with this attribute will not be so instrumented. + @item section ("section-name") @cindex @code{section} function attribute Normally, the compiler places the code it generates in the @code{text} section. diff --git a/gcc/flags.h b/gcc/flags.h index 37c1bd96a91..cfb4ee628c9 100644 --- a/gcc/flags.h +++ b/gcc/flags.h @@ -444,6 +444,9 @@ extern int flag_stack_check; /* Do the full regmove optimization pass. */ extern int flag_regmove; + +/* Instrument functions with calls at entry and exit, for profiling. */ +extern int flag_instrument_function_entry_exit; /* Other basic status info about current function. */ diff --git a/gcc/function.c b/gcc/function.c index cf9542bfe9b..5bd012fc135 100644 --- a/gcc/function.c +++ b/gcc/function.c @@ -217,6 +217,10 @@ rtx current_function_internal_arg_pointer; /* Language-specific reason why the current function cannot be made inline. */ char *current_function_cannot_inline; +/* Nonzero if instrumentation calls for function entry and exit should be + generated. */ +int current_function_instrument_entry_exit; + /* The FUNCTION_DECL for an inline function currently being expanded. */ tree inline_function_decl; @@ -539,6 +543,7 @@ push_function_context_to (context) p->fixup_var_refs_queue = 0; p->epilogue_delay_list = current_function_epilogue_delay_list; p->args_info = current_function_args_info; + p->instrument_entry_exit = current_function_instrument_entry_exit; save_tree_status (p, context); save_storage_status (p); @@ -621,6 +626,7 @@ pop_function_context_from (context) current_function_epilogue_delay_list = p->epilogue_delay_list; reg_renumber = 0; current_function_args_info = p->args_info; + current_function_instrument_entry_exit = p->instrument_entry_exit; restore_tree_status (p, context); restore_storage_status (p); @@ -5458,6 +5464,10 @@ expand_function_start (subr, parms_have_cleanups) valid operands of arithmetic insns. */ init_recog_no_volatile (); + current_function_instrument_entry_exit + = (flag_instrument_function_entry_exit + && ! DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (subr)); + /* If function gets a static chain arg, store it in the stack frame. Do this first, so it gets the first stack slot offset. */ if (current_function_needs_context) @@ -5484,6 +5494,7 @@ expand_function_start (subr, parms_have_cleanups) or if it returns a structure, or if it has parm cleanups. */ #ifdef HAVE_return if (cleanup_label == 0 && HAVE_return + && ! current_function_instrument_entry_exit && ! current_function_returns_pcc_struct && ! (current_function_returns_struct && ! optimize)) return_label = 0; @@ -5532,7 +5543,7 @@ expand_function_start (subr, parms_have_cleanups) else if (DECL_MODE (DECL_RESULT (subr)) == VOIDmode) /* If return mode is void, this decl rtl should not be used. */ DECL_RTL (DECL_RESULT (subr)) = 0; - else if (parms_have_cleanups) + else if (parms_have_cleanups || current_function_instrument_entry_exit) { /* If function will end with cleanup code for parms, compute the return values into a pseudo reg, @@ -5650,6 +5661,21 @@ expand_function_start (subr, parms_have_cleanups) } } + if (current_function_instrument_entry_exit) + { + rtx fun = DECL_RTL (current_function_decl); + if (GET_CODE (fun) == MEM) + fun = XEXP (fun, 0); + else + abort (); + emit_library_call (profile_function_entry_libfunc, 0, VOIDmode, 2, + fun, Pmode, + expand_builtin_return_addr (BUILT_IN_RETURN_ADDRESS, + 0, + hard_frame_pointer_rtx), + Pmode); + } + /* After the display initializations is where the tail-recursion label should go, if we end up needing one. Ensure we have a NOTE here since some things (like trampolines) get placed before this. */ @@ -5863,6 +5889,21 @@ expand_function_end (filename, line, end_bindings) } } + if (current_function_instrument_entry_exit) + { + rtx fun = DECL_RTL (current_function_decl); + if (GET_CODE (fun) == MEM) + fun = XEXP (fun, 0); + else + abort (); + emit_library_call (profile_function_exit_libfunc, 0, VOIDmode, 2, + fun, Pmode, + expand_builtin_return_addr (BUILT_IN_RETURN_ADDRESS, + 0, + hard_frame_pointer_rtx), + Pmode); + } + /* If we had calls to alloca, and this machine needs an accurate stack pointer to exit the function, insert some code to save and restore the stack pointer. */ diff --git a/gcc/function.h b/gcc/function.h index f9b9a9a924b..06e90dc88aa 100644 --- a/gcc/function.h +++ b/gcc/function.h @@ -113,6 +113,7 @@ struct function int temp_slot_level; int target_temp_slot_level; int var_temp_slot_level; + int instrument_entry_exit; /* This slot is initialized as 0 and is added to during the nested function. */ struct var_refs_queue *fixup_var_refs_queue; diff --git a/gcc/invoke.texi b/gcc/invoke.texi index 3b3ad4313db..67c7392258f 100644 --- a/gcc/invoke.texi +++ b/gcc/invoke.texi @@ -5907,6 +5907,41 @@ prefix_foo (int a) @end example This option is designed to be used with @samp{-fcheck-memory-usage}. +@item -finstrument-functions +Generate instrumentation calls for entry and exit to functions. Just +after function entry and just before function exit, the following +profiling functions will be called with the address of the current +function and its call site. (On some platforms, +@code{__builtin_return_address} does not work beyond the current +function, so the call site information may not be available to the +profiling functions otherwise.) + +@example +void __cyg_profile_func_enter (void *this_fn, void *call_site); +void __cyg_profile_func_exit (void *this_fn, void *call_site); +@end example + +The first argument is the address of the start of the current function, +which may be looked up exactly in the symbol table. + +This instrumentation is also done for functions expanded inline in other +functions. The profiling calls will indicate where, conceptually, the +inline function is entered and exited. This means that addressable +versions of such functions must be available. If all your uses of a +function are expanded inline, this may mean an additional expansion of +code size. If you use @samp{extern inline} in your C code, an +addressable version of such functions must be provided. (This is +normally the case anyways, but if you get lucky and the optimizer always +expands the functions inline, you might have gotten away without +providing static copies.) + +A function may be given the attribute @code{no_instrument_function}, in +which case this instrumentation will not be done. This can be used, for +example, for the profiling functions listed above, high-priority +interrupt routines, and any functions from which the profiling functions +cannot safely be called (perhaps signal handlers, if the profiling +routines generate output or allocate memory). + @item -fstack-check Generate code to verify that you do not go beyond the boundary of the stack. You should specify this flag if you are running in an diff --git a/gcc/optabs.c b/gcc/optabs.c index 4b31fe0d106..95c963ee040 100644 --- a/gcc/optabs.c +++ b/gcc/optabs.c @@ -214,6 +214,9 @@ rtx chkr_copy_bitmap_libfunc; rtx chkr_check_exec_libfunc; rtx chkr_check_str_libfunc; +rtx profile_function_entry_libfunc; +rtx profile_function_exit_libfunc; + /* Indexed by the rtx-code for a conditional (eg. EQ, LT,...) gives the gen_function to make a branch to test that condition. */ @@ -4391,6 +4394,12 @@ init_optabs () chkr_check_exec_libfunc = gen_rtx_SYMBOL_REF (VOIDmode, "chkr_check_exec"); chkr_check_str_libfunc = gen_rtx_SYMBOL_REF (VOIDmode, "chkr_check_str"); + /* For function entry/exit instrumentation. */ + profile_function_entry_libfunc + = gen_rtx_SYMBOL_REF (VOIDmode, "__cyg_profile_func_enter"); + profile_function_exit_libfunc + = gen_rtx_SYMBOL_REF (VOIDmode, "__cyg_profile_func_exit"); + #ifdef HAVE_conditional_trap init_traps (); #endif diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 4b718f2a8fa..aced868b959 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,9 @@ +1998-07-30 Ken Raeburn + + * gcc.c-torture/special/eeprof-1.c: New test, for + -finstrument-functions. + * gcc.c-torture/special/special.exp: Run it. + Wed Jul 29 00:17:18 1998 Jeffrey A Law (law@cygnus.com) * gcc.c-torture/compile/980729-1.c: New test. diff --git a/gcc/testsuite/gcc.c-torture/special/eeprof-1.c b/gcc/testsuite/gcc.c-torture/special/eeprof-1.c new file mode 100644 index 00000000000..6dad7ec5a73 --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/special/eeprof-1.c @@ -0,0 +1,67 @@ +#define ASSERT(X) if (!(X)) abort (); +#define NOCHK __attribute__ ((no_instrument_function)) + +int entry_calls, exit_calls; +void (*last_fn_entered)(); +void (*last_fn_exited)(); + +int main () NOCHK; + +void foo () +{ + ASSERT (last_fn_entered == foo); +} + +static void foo2 () +{ + ASSERT (entry_calls == 1 && exit_calls == 0); + ASSERT (last_fn_entered == foo2); + foo (); + ASSERT (entry_calls == 2 && exit_calls == 1); + ASSERT (last_fn_entered == foo); + ASSERT (last_fn_exited == foo); +} + +void nfoo (void) NOCHK; +void nfoo () +{ + ASSERT (entry_calls == 2 && exit_calls == 2); + ASSERT (last_fn_entered == foo); + ASSERT (last_fn_exited == foo2); + foo (); + ASSERT (entry_calls == 3 && exit_calls == 3); + ASSERT (last_fn_entered == foo); + ASSERT (last_fn_exited == foo); +} + +int main () +{ + ASSERT (entry_calls == 0 && exit_calls == 0); + + foo2 (); + + ASSERT (entry_calls == 2 && exit_calls == 2); + ASSERT (last_fn_entered == foo); + ASSERT (last_fn_exited == foo2); + + nfoo (); + + ASSERT (entry_calls == 3 && exit_calls == 3); + ASSERT (last_fn_entered == foo); + + return 0; +} + +void __cyg_profile_func_enter (void (*fn)(), void (*parent)()) NOCHK; +void __cyg_profile_func_exit (void (*fn)(), void (*parent)()) NOCHK; + +void __cyg_profile_func_enter (void (*fn)(), void (*parent)()) +{ + entry_calls++; + last_fn_entered = fn; +} +void __cyg_profile_func_exit (void (*fn)(), void (*parent)()) +{ + exit_calls++; + last_fn_exited = fn; +} diff --git a/gcc/testsuite/gcc.c-torture/special/special.exp b/gcc/testsuite/gcc.c-torture/special/special.exp index 374fe40c765..ac215a39f5b 100644 --- a/gcc/testsuite/gcc.c-torture/special/special.exp +++ b/gcc/testsuite/gcc.c-torture/special/special.exp @@ -98,3 +98,5 @@ c-torture 960224-1.c "-E -ansi -pedantic-errors" # 960224-2 #c-torture 960224-2.c "-E -ansi -pedantic-errors" + +c-torture-execute $srcdir/$subdir/eeprof-1.c "-finstrument-functions" diff --git a/gcc/toplev.c b/gcc/toplev.c index 266a209f9bf..9ab6435ab30 100644 --- a/gcc/toplev.c +++ b/gcc/toplev.c @@ -723,6 +723,9 @@ int flag_strict_aliasing = 0; extern int flag_dump_unnumbered; +/* Instrument functions with calls at entry and exit, for profiling. */ +int flag_instrument_function_entry_exit = 0; + /* Table of supported debugging formats. */ static struct @@ -908,7 +911,9 @@ lang_independent_options f_options[] = "Generate code to check every memory access" }, {"prefix-function-name", &flag_prefix_function_name, 1, "Add a prefix to all function names" }, - {"dump-unnumbered", &flag_dump_unnumbered, 1} + {"dump-unnumbered", &flag_dump_unnumbered, 1}, + {"instrument-functions", &flag_instrument_function_entry_exit, 1, + "Instrument function entry/exit with profiling calls"}, }; #define NUM_ELEM(a) (sizeof (a) / sizeof ((a)[0])) diff --git a/gcc/tree.h b/gcc/tree.h index 703c94e7aef..401e5ee4b92 100644 --- a/gcc/tree.h +++ b/gcc/tree.h @@ -1201,6 +1201,10 @@ struct tree_type multiple translation units should be merged. */ #define DECL_ONE_ONLY(NODE) (DECL_CHECK (NODE)->decl.transparent_union) +/* Used in FUNCTION_DECLs to indicate that function entry and exit should + be instrumented with calls to support routines. */ +#define DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT(NODE) ((NODE)->decl.no_instrument_function_entry_exit) + /* Additional flags for language-specific uses. */ #define DECL_LANG_FLAG_0(NODE) (DECL_CHECK (NODE)->decl.lang_flag_0) #define DECL_LANG_FLAG_1(NODE) (DECL_CHECK (NODE)->decl.lang_flag_1) @@ -1245,7 +1249,6 @@ struct tree_decl unsigned static_dtor_flag : 1; unsigned artificial_flag : 1; unsigned weak_flag : 1; - /* room for no more */ unsigned lang_flag_0 : 1; unsigned lang_flag_1 : 1; @@ -1257,6 +1260,7 @@ struct tree_decl unsigned lang_flag_7 : 1; unsigned non_addr_const_p : 1; + unsigned no_instrument_function_entry_exit : 1; /* For a FUNCTION_DECL, if inline, this is the size of frame needed. If built-in, this is the code for which built-in function. -- 2.30.2