Function entry/exit profiling instrumentation:
authorKen Raeburn <raeburn@cygnus.com>
Thu, 30 Jul 1998 10:38:22 +0000 (10:38 +0000)
committerKen Raeburn <raeburn@gcc.gnu.org>
Thu, 30 Jul 1998 10:38:22 +0000 (10:38 +0000)
* 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

15 files changed:
gcc/ChangeLog
gcc/c-common.c
gcc/c-decl.c
gcc/expr.h
gcc/extend.texi
gcc/flags.h
gcc/function.c
gcc/function.h
gcc/invoke.texi
gcc/optabs.c
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.c-torture/special/eeprof-1.c [new file with mode: 0644]
gcc/testsuite/gcc.c-torture/special/special.exp
gcc/toplev.c
gcc/tree.h

index 8ae4b2a1fd5e26792f17e50bacb041e2a3d4d8bd..88fb4937ce591c821030110e310ae4d66e09151a 100644 (file)
@@ -1,3 +1,31 @@
+Thu Jul 30 13:08:07 1998  Ken Raeburn  <raeburn@cygnus.com>
+
+       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
index 245fedb97be344908cf20188f8d18c6bd3a29403..8405a41a2c911a6aa7fe8f9c6c81e8e095b7bc4a 100644 (file)
@@ -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);
 }
 \f
 /* 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;
        }
     }
 }
index 3af985d732d1807da2169c14692d4a5ee8ff3ce0..a4b87860e66d44a63091b8c07116204fa84c9e9b 100644 (file)
@@ -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 ();
index a53a036a15a34bad0f9da66d86ca46c5aae86a0a..9e43741f26ecc95b226311a520e1fcd079fabc06 100644 (file)
@@ -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;
 \f
 typedef rtx (*rtxfun) PROTO ((rtx));
 
index 736f4220c5a242e8d7530d69dadd0ff9e0c0b176..0fb01f444f55ef53957053efb632f6ee8c3328fa 100644 (file)
@@ -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.
index 37c1bd96a91bbb2aab435303b91b707bb6a69b38..cfb4ee628c968f52b7ac253b1b1700c3f9a85a67 100644 (file)
@@ -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;
 \f
 /* Other basic status info about current function.  */
 
index cf9542bfe9bc7ae6e87ea2e480c1428a8d044512..5bd012fc1356c8aa8f5f51ccfe583dbcfa590684 100644 (file)
@@ -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.  */
index f9b9a9a924b7ca485674f22a41219a99437356e0..06e90dc88aa34b759d7a097090b52922059e1c6f 100644 (file)
@@ -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;
index 3b3ad4313db6bce854571df52238772cf5866468..67c7392258fdb8fe0cc3c4ff4e5a4ad1f53ce0e4 100644 (file)
@@ -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
index 4b31fe0d106433960ea3f0e098e20e7d40218026..95c963ee040a9658d3a32d9f1c1fe9000c52fb88 100644 (file)
@@ -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
index 4b718f2a8fa70676c0a8d8d8d0a13d3a7d241dc3..aced868b959ccf9798701932937fbaf0b46a11c3 100644 (file)
@@ -1,3 +1,9 @@
+1998-07-30  Ken Raeburn  <raeburn@cygnus.com>
+
+       * 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 (file)
index 0000000..6dad7ec
--- /dev/null
@@ -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;
+}
index 374fe40c7653d198b1e021f7c1b01443fa3b3326..ac215a39f5bfb765c0563a3c62769019e401966f 100644 (file)
@@ -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"
index 266a209f9bf0297426c66481048bcfbd60f21a11..9ab6435ab308312040033a9c4cd49dbc1b9dbd24 100644 (file)
@@ -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]))
index 703c94e7aef747da50d1aa0662dad690e0df0252..401e5ee4b924c1f67207125141912cd7d42e3f62 100644 (file)
@@ -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.