diff mbox

Caller instrumentation with -finstrument-calls

Message ID 4555973.9Ur2WRMufi@atv-pwoegere-l3.atv.mentorg.com
State New
Headers show

Commit Message

Paul_Woegerer@mentor.com June 28, 2013, 12:51 p.m. UTC
Hi,

The patch below provides caller instrumentation for GCC.

The following new options have been added:

    -finstrument-calls
    -finstrument-calls-exclude-function-list=SYM,SYM,...
    -finstrument-calls-exclude-file-list=FILE,FILE,...

These new options behave and appear similar to the existing function
instrumentation options (-finstrument-functions*). I have also added
attribute no_instrument_calls to specify which functions should be
excluded from within the source code. Calls to functions that have
attribute no_instrument_function are also excluded.

The effect of the instrumentation causes all calls inside a function
to get instrumented using the following hooks:

    void __cyg_profile_call_before (void *fn);
    void __cyg_profile_call_after  (void *fn);

So, any occurrence of:

    ..
    call foo(...)
    ..

gets transformed to:

    ..
    call __cyg_profile_call_before(&foo)
    call foo(...)
    call __cyg_profile_call_after(&foo)
    ..

-finstrument-calls can be very useful if you want to trace what is
going on _inside_ a function. To get a similar result with the existing
-finstrument-functions option you would need to recompile the sources
of all the functions that could potentially get called inside the
function of interest. Most of the time this is not practical.


Thanks,
Paul


From edbee74bca9719f1d3b27591823e475be597c042 Mon Sep 17 00:00:00 2001
From: Paul Woegerer <paul_woegerer@mentor.com>
Date: Fri, 28 Jun 2013 14:09:30 +0200
Subject: [PATCH] Caller instrumentation with -finstrument-calls

	Caller instrumentation with -finstrument-calls.
	* gcc/builtins.def: Add call-hooks __cyg_profile_call_before and
	__cyg_profile_call_after.
	* gcc/libfuncs.h (enum libfunc_index): Likewise.
	* gcc/optabs.c (init_optabs): Likewise.
	* gcc/c-family/c-common.c (no_instrument_calls): Add attribute.
	(handle_no_instrument_calls_attribute): New.
	* gcc/common.opt (finstrument-calls): New option.
	(finstrument-calls-exclude-function-list): Likewise.
	(finstrument-calls-exclude-file-list): Likewise.
	* gcc/opts.c (common_handle_option): Handle new options.
	* gcc/tree.h (tree_function_decl): Add field tree_function_decl.
	* gcc/c/c-decl.c (merge_decls): Handle tree_function_decl field.
	* gcc/cp/decl.c (duplicate_decls): Likewise.
	* gcc/function.c (expand_function_start): Likewise.
	* gcc/ipa.c: Likewise.
	* gcc/java/jcf-parse.c: Likewise.
	* gcc/tree-streamer-in.c: Likewise.
	* gcc/tree-streamer-out.c: Likewise.
	(finstrument-calls-exclude-function-list): Likewise.
	(finstrument-calls-exclude-file-list): Likewise.
	* gcc/gimplify.c (flag_instrument_calls_exclude_p): New.
	(addr_expr_for_call_instrumentation): New.
	(maybe_add_profile_call): New.
	(gimplify_call_expr): Add call-hooks insertion.
	(gimplify_modify_expr): Likewise.
	* gcc/doc/invoke.texi: Added documentation for
	-finstrument-calls-exclude-function-list and
	-finstrument-calls-exclude-file-list and
	-finstrument-calls.
	* gcc/testsuite/g++.dg/other/instrument_calls-1.C  Added
	 regression test for -finstrument-calls.
	* gcc/testsuite/g++.dg/other/instrument_calls-2.C: Likewise.
	* gcc/testsuite/g++.dg/other/instrument_calls-3.C: Likewise.
	* gcc/testsuite/gcc.dg/instrument_calls-1.c: Likewise.
	* gcc/testsuite/gcc.dg/instrument_calls-2.c: Likewise.
	* gcc/testsuite/gcc.dg/instrument_calls-3.c: Likewise.
	* gcc/testsuite/gcc.dg/instrument_calls-4.c: Likewise.
	* gcc/testsuite/gcc.dg/instrument_calls-5.c: Likewise.
	* gcc/testsuite/gcc.dg/instrument_calls-6.c: Likewise.
	* gcc/testsuite/gcc.dg/instrument_calls-7.c: Likewise.
	* gcc/testsuite/gcc.dg/instrument_calls-8.c: Likewise.
	* gcc/testsuite/gcc.dg/instrument_calls-9.c: Likewise.

Comments

Andrew Pinski June 28, 2013, 4:50 p.m. UTC | #1
On Fri, Jun 28, 2013 at 5:51 AM,  <Paul_Woegerer@mentor.com> wrote:
> Hi,
>
> The patch below provides caller instrumentation for GCC.
>
> The following new options have been added:
>
>     -finstrument-calls
>     -finstrument-calls-exclude-function-list=SYM,SYM,...
>     -finstrument-calls-exclude-file-list=FILE,FILE,...
>
> These new options behave and appear similar to the existing function
> instrumentation options (-finstrument-functions*). I have also added
> attribute no_instrument_calls to specify which functions should be
> excluded from within the source code. Calls to functions that have
> attribute no_instrument_function are also excluded.
>
> The effect of the instrumentation causes all calls inside a function
> to get instrumented using the following hooks:
>
>     void __cyg_profile_call_before (void *fn);
>     void __cyg_profile_call_after  (void *fn);


Can you not use cyg as the prefix rather use gcc or gnu.  cyg prefix
for -finstrument-functions is a historical accident as it stands for
cygnus but that company no longer exists and we should not be using a
company specific prefix inside of GCC anymore.

Thanks,
Andrew

>
> So, any occurrence of:
>
>     ..
>     call foo(...)
>     ..
>
> gets transformed to:
>
>     ..
>     call __cyg_profile_call_before(&foo)
>     call foo(...)
>     call __cyg_profile_call_after(&foo)
>     ..
>
> -finstrument-calls can be very useful if you want to trace what is
> going on _inside_ a function. To get a similar result with the existing
> -finstrument-functions option you would need to recompile the sources
> of all the functions that could potentially get called inside the
> function of interest. Most of the time this is not practical.
>
>
> Thanks,
> Paul
>
>
> From edbee74bca9719f1d3b27591823e475be597c042 Mon Sep 17 00:00:00 2001
> From: Paul Woegerer <paul_woegerer@mentor.com>
> Date: Fri, 28 Jun 2013 14:09:30 +0200
> Subject: [PATCH] Caller instrumentation with -finstrument-calls
>
>         Caller instrumentation with -finstrument-calls.
>         * gcc/builtins.def: Add call-hooks __cyg_profile_call_before and
>         __cyg_profile_call_after.
>         * gcc/libfuncs.h (enum libfunc_index): Likewise.
>         * gcc/optabs.c (init_optabs): Likewise.
>         * gcc/c-family/c-common.c (no_instrument_calls): Add attribute.
>         (handle_no_instrument_calls_attribute): New.
>         * gcc/common.opt (finstrument-calls): New option.
>         (finstrument-calls-exclude-function-list): Likewise.
>         (finstrument-calls-exclude-file-list): Likewise.
>         * gcc/opts.c (common_handle_option): Handle new options.
>         * gcc/tree.h (tree_function_decl): Add field tree_function_decl.
>         * gcc/c/c-decl.c (merge_decls): Handle tree_function_decl field.
>         * gcc/cp/decl.c (duplicate_decls): Likewise.
>         * gcc/function.c (expand_function_start): Likewise.
>         * gcc/ipa.c: Likewise.
>         * gcc/java/jcf-parse.c: Likewise.
>         * gcc/tree-streamer-in.c: Likewise.
>         * gcc/tree-streamer-out.c: Likewise.
>         (finstrument-calls-exclude-function-list): Likewise.
>         (finstrument-calls-exclude-file-list): Likewise.
>         * gcc/gimplify.c (flag_instrument_calls_exclude_p): New.
>         (addr_expr_for_call_instrumentation): New.
>         (maybe_add_profile_call): New.
>         (gimplify_call_expr): Add call-hooks insertion.
>         (gimplify_modify_expr): Likewise.
>         * gcc/doc/invoke.texi: Added documentation for
>         -finstrument-calls-exclude-function-list and
>         -finstrument-calls-exclude-file-list and
>         -finstrument-calls.
>         * gcc/testsuite/g++.dg/other/instrument_calls-1.C  Added
>          regression test for -finstrument-calls.
>         * gcc/testsuite/g++.dg/other/instrument_calls-2.C: Likewise.
>         * gcc/testsuite/g++.dg/other/instrument_calls-3.C: Likewise.
>         * gcc/testsuite/gcc.dg/instrument_calls-1.c: Likewise.
>         * gcc/testsuite/gcc.dg/instrument_calls-2.c: Likewise.
>         * gcc/testsuite/gcc.dg/instrument_calls-3.c: Likewise.
>         * gcc/testsuite/gcc.dg/instrument_calls-4.c: Likewise.
>         * gcc/testsuite/gcc.dg/instrument_calls-5.c: Likewise.
>         * gcc/testsuite/gcc.dg/instrument_calls-6.c: Likewise.
>         * gcc/testsuite/gcc.dg/instrument_calls-7.c: Likewise.
>         * gcc/testsuite/gcc.dg/instrument_calls-8.c: Likewise.
>         * gcc/testsuite/gcc.dg/instrument_calls-9.c: Likewise.
>
> diff --git a/gcc/builtins.def b/gcc/builtins.def
> index 91879a6..d175692 100644
> --- a/gcc/builtins.def
> +++ b/gcc/builtins.def
> @@ -795,6 +795,11 @@ DEF_BUILTIN (BUILT_IN_PROFILE_FUNC_ENTER, "__cyg_profile_func_enter", BUILT_IN_N
>  DEF_BUILTIN (BUILT_IN_PROFILE_FUNC_EXIT, "__cyg_profile_func_exit", BUILT_IN_NORMAL, BT_FN_VOID_PTR_PTR, BT_LAST,
>              false, false, false, ATTR_NULL, true, true)
>
> +DEF_BUILTIN (BUILT_IN_PROFILE_CALL_BEFORE, "__cyg_profile_call_before", BUILT_IN_NORMAL, BT_FN_VOID_PTR, BT_LAST,
> +            false, false, false, ATTR_NULL, true, true)
> +DEF_BUILTIN (BUILT_IN_PROFILE_CALL_AFTER, "__cyg_profile_call_after", BUILT_IN_NORMAL, BT_FN_VOID_PTR, BT_LAST,
> +            false, false, false, ATTR_NULL, true, true)
> +
>  /* TLS thread pointer related builtins.  */
>  DEF_BUILTIN (BUILT_IN_THREAD_POINTER, "__builtin_thread_pointer",
>              BUILT_IN_NORMAL, BT_FN_PTR, BT_LAST,
> diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
> index 8f7f5e5..f3ad003 100644
> --- a/gcc/c-family/c-common.c
> +++ b/gcc/c-family/c-common.c
> @@ -343,6 +343,8 @@ static tree handle_tls_model_attribute (tree *, tree, tree, int,
>                                         bool *);
>  static tree handle_no_instrument_function_attribute (tree *, tree,
>                                                      tree, int, bool *);
> +static tree handle_no_instrument_calls_attribute (tree *, tree,
> +                                                    tree, int, bool *);
>  static tree handle_malloc_attribute (tree *, tree, tree, int, bool *);
>  static tree handle_returns_twice_attribute (tree *, tree, tree, int, bool *);
>  static tree handle_no_limit_stack_attribute (tree *, tree, tree, int,
> @@ -658,6 +660,9 @@ const struct attribute_spec c_common_attribute_table[] =
>    { "no_instrument_function", 0, 0, true,  false, false,
>                               handle_no_instrument_function_attribute,
>                               false },
> +  { "no_instrument_calls", 0, 0, true,  false, false,
> +                             handle_no_instrument_calls_attribute,
> +                             false },
>    { "malloc",                 0, 0, true,  false, false,
>                               handle_malloc_attribute, false },
>    { "returns_twice",          0, 0, true,  false, false,
> @@ -7891,6 +7896,35 @@ handle_no_instrument_function_attribute (tree *node, tree name,
>    return NULL_TREE;
>  }
>
> +/* Handle a "no_instrument_calls" attribute; arguments as in
> +   struct attribute_spec.handler.  */
> +
> +static tree
> +handle_no_instrument_calls_attribute (tree *node, tree name,
> +                                        tree ARG_UNUSED (args),
> +                                        int ARG_UNUSED (flags),
> +                                        bool *no_add_attrs)
> +{
> +  tree decl = *node;
> +
> +  if (TREE_CODE (decl) != FUNCTION_DECL)
> +    {
> +      error_at (DECL_SOURCE_LOCATION (decl),
> +               "%qE attribute applies only to functions", name);
> +      *no_add_attrs = true;
> +    }
> +  else if (DECL_INITIAL (decl))
> +    {
> +      error_at (DECL_SOURCE_LOCATION (decl),
> +               "can%'t set %qE attribute after definition", name);
> +      *no_add_attrs = true;
> +    }
> +  else
> +    DECL_NO_INSTRUMENT_CALLS_BEFORE_AFTER (decl) = 1;
> +
> +  return NULL_TREE;
> +}
> +
>  /* Handle a "malloc" attribute; arguments as in
>     struct attribute_spec.handler.  */
>
> diff --git a/gcc/c/c-decl.c b/gcc/c/c-decl.c
> index 8170a80..4657e48 100644
> --- a/gcc/c/c-decl.c
> +++ b/gcc/c/c-decl.c
> @@ -2287,6 +2287,8 @@ merge_decls (tree newdecl, tree olddecl, tree newtype, tree oldtype)
>           DECL_NO_LIMIT_STACK (newdecl) |= DECL_NO_LIMIT_STACK (olddecl);
>           DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (newdecl)
>             |= DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (olddecl);
> +         DECL_NO_INSTRUMENT_CALLS_BEFORE_AFTER (newdecl)
> +           |= DECL_NO_INSTRUMENT_CALLS_BEFORE_AFTER (olddecl);
>           TREE_THIS_VOLATILE (newdecl) |= TREE_THIS_VOLATILE (olddecl);
>           DECL_IS_MALLOC (newdecl) |= DECL_IS_MALLOC (olddecl);
>           DECL_IS_OPERATOR_NEW (newdecl) |= DECL_IS_OPERATOR_NEW (olddecl);
> diff --git a/gcc/common.opt b/gcc/common.opt
> index 4c7933e..90cb06d 100644
> --- a/gcc/common.opt
> +++ b/gcc/common.opt
> @@ -93,7 +93,7 @@ int flag_gen_aux_info = 0
>  Variable
>  int flag_shlib
>
> -; These two are really VEC(char_p,heap) *.
> +; These four are really VEC(char_p,heap) *.
>
>  Variable
>  void *flag_instrument_functions_exclude_functions
> @@ -101,6 +101,12 @@ void *flag_instrument_functions_exclude_functions
>  Variable
>  void *flag_instrument_functions_exclude_files
>
> +Variable
> +void *flag_instrument_calls_exclude_functions
> +
> +Variable
> +void *flag_instrument_calls_exclude_files
> +
>  ; Generic structs (e.g. templates not explicitly specialized)
>  ; may not have a compilation unit associated with them, and so
>  ; may need to be treated differently from ordinary structs.
> @@ -1364,6 +1370,18 @@ finstrument-functions-exclude-file-list=
>  Common RejectNegative Joined
>  -finstrument-functions-exclude-file-list=filename,...  Do not instrument functions listed in files
>
> +finstrument-calls
> +Common Report Var(flag_instrument_calls_before_after)
> +Instrument call entry and exit with profiling calls
> +
> +finstrument-calls-exclude-function-list=
> +Common RejectNegative Joined
> +-finstrument-calls-exclude-function-list=name,...  Do not instrument calls from listed functions
> +
> +finstrument-calls-exclude-file-list=
> +Common RejectNegative Joined
> +-finstrument-calls-exclude-file-list=filename,...  Do not instrument calls from functions listed in files
> +
>  fipa-cp
>  Common Report Var(flag_ipa_cp) Optimization
>  Perform Interprocedural constant propagation
> diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
> index 047fd77..9a02012 100644
> --- a/gcc/cp/decl.c
> +++ b/gcc/cp/decl.c
> @@ -1971,6 +1971,8 @@ duplicate_decls (tree newdecl, tree olddecl, bool newdecl_is_friend)
>         {
>           DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (newdecl)
>             |= DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (olddecl);
> +         DECL_NO_INSTRUMENT_CALLS_BEFORE_AFTER (newdecl)
> +           |= DECL_NO_INSTRUMENT_CALLS_BEFORE_AFTER (olddecl);
>           DECL_NO_LIMIT_STACK (newdecl) |= DECL_NO_LIMIT_STACK (olddecl);
>           TREE_THIS_VOLATILE (newdecl) |= TREE_THIS_VOLATILE (olddecl);
>           TREE_NOTHROW (newdecl) |= TREE_NOTHROW (olddecl);
> diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
> index 1496d30..a1a3579 100644
> --- a/gcc/doc/invoke.texi
> +++ b/gcc/doc/invoke.texi
> @@ -1015,6 +1015,9 @@ See S/390 and zSeries Options.
>  -finhibit-size-directive  -finstrument-functions @gol
>  -finstrument-functions-exclude-function-list=@var{sym},@var{sym},@dots{} @gol
>  -finstrument-functions-exclude-file-list=@var{file},@var{file},@dots{} @gol
> +-finstrument-calls @gol
> +-finstrument-calls-exclude-function-list=@var{sym},@var{sym},@dots{} @gol
> +-finstrument-calls-exclude-file-list=@var{file},@var{file},@dots{} @gol
>  -fno-common  -fno-ident @gol
>  -fpcc-struct-return  -fpic  -fPIC -fpie -fPIE @gol
>  -fno-jump-tables @gol
> @@ -20706,6 +20709,45 @@ of the function name, it is considered to be a match.  For C99 and C++
>  extended identifiers, the function name must be given in UTF-8, not
>  using universal character names.
>
> +@item -finstrument-calls
> +@opindex finstrument-calls
> +Generate instrumentation calls immediately before and after each
> +function call. The following profiling functions will be called with
> +the address of the function that is called between them. Use
> +@code{__builtin_return_address(0)} inside the profiling functions to
> +get the addresses from where they are called.
> +
> +@smallexample
> +void __cyg_profile_call_before (void *fn);
> +void __cyg_profile_call_after  (void *fn);
> +@end smallexample
> +
> +A function may be given attribute @code{no_instrument_calls}, in which
> +case the instrumentation is omitted (no calls within that function will
> +be instrumented).
> +
> +In addition, calls to functions which have been given attribute
> +@code{no_instrument_function} (or selected via
> +@code{-finstrument-functions-exclude} options) are also excluded from
> +instrumentation.
> +
> +@item -finstrument-calls-exclude-file-list=@var{file},@var{file},@dots{}
> +@opindex finstrument-calls-exclude-file-list
> +
> +Set the list of functions that are excluded from instrumentation (see
> +the description of @code{-finstrument-calls}).  If the file that
> +contains a function definition matches with one of @var{file}, then
> +the calls in that function are not instrumented.  The match is done on
> +substrings as for @code{-finstrument-functions-exclude-file-list}.
> +
> +@item -finstrument-calls-exclude-function-list=@var{sym},@var{sym},@dots{}
> +@opindex finstrument-calls-exclude-function-list
> +
> +This is similar to @code{-finstrument-calls-exclude-file-list},
> +but this option sets the list of function names to be excluded from
> +instrumentation.  The function name to be matched in the same way as for
> +@code{-finstrument-functions-exclude-function-list}
> +
>  @item -fstack-check
>  @opindex fstack-check
>  Generate code to verify that you do not go beyond the boundary of the
> diff --git a/gcc/function.c b/gcc/function.c
> index 3e33fc7..7290d76 100644
> --- a/gcc/function.c
> +++ b/gcc/function.c
> @@ -4728,7 +4728,8 @@ expand_function_start (tree subr)
>
>    crtl->profile
>      = (profile_flag
> -       && ! DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (subr));
> +       && ! DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (subr)
> +       && ! DECL_NO_INSTRUMENT_CALLS_BEFORE_AFTER (subr));
>
>    crtl->limit_stack
>      = (stack_limit_rtx != NULL_RTX && ! DECL_NO_LIMIT_STACK (subr));
> diff --git a/gcc/gimplify.c b/gcc/gimplify.c
> index e2ae893..8401278 100644
> --- a/gcc/gimplify.c
> +++ b/gcc/gimplify.c
> @@ -89,6 +89,8 @@ static struct gimplify_omp_ctx *gimplify_omp_ctxp;
>
>  /* Forward declaration.  */
>  static enum gimplify_status gimplify_compound_expr (tree *, gimple_seq *, bool);
> +static bool flag_instrument_calls_exclude_p (tree fndecl);
> +static bool flag_instrument_functions_exclude_p (tree fndecl);
>
>  /* Mark X addressable.  Unlike the langhook we expect X to be in gimple
>     form and we don't do any syntax checking.  */
> @@ -1153,6 +1155,63 @@ build_stack_save_restore (gimple *save, gimple *restore)
>                          1, tmp_var);
>  }
>
> +/* Returns the function decl that corresponds the function called in
> +   CALL_EXPR if call instrumentation is enabled.  */
> +
> +static tree
> +addr_expr_for_call_instrumentation (tree call_expr)
> +{
> +  tree addr_expr = NULL_TREE;
> +
> +  if (!gimplify_ctxp->into_ssa && flag_instrument_calls_before_after
> +      && !DECL_NO_INSTRUMENT_CALLS_BEFORE_AFTER (current_function_decl)
> +      && !flag_instrument_calls_exclude_p (current_function_decl))
> +    {
> +      tree fndecl = get_callee_fndecl (call_expr);
> +      if (fndecl)
> +        {
> +         if (!DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (fndecl)
> +             && !flag_instrument_functions_exclude_p (fndecl))
> +           {
> +             if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL
> +                     && DECL_FUNCTION_CODE (fndecl) == BUILT_IN_APPLY
> +                     && call_expr_nargs (call_expr) > 0)
> +                 addr_expr = CALL_EXPR_ARG (call_expr, 0);
> +             else if (!DECL_IS_BUILTIN (fndecl))
> +                 addr_expr = build_fold_addr_expr (fndecl);
> +           }
> +        }
> +      else
> +         addr_expr = CALL_EXPR_FN (call_expr);
> +    }
> +
> +  if (addr_expr)
> +    {
> +      if (TREE_CODE (addr_expr) == OBJ_TYPE_REF)
> +       addr_expr = OBJ_TYPE_REF_EXPR (addr_expr);
> +      else if (!is_gimple_val (addr_expr))
> +       addr_expr = NULL_TREE;
> +    }
> +
> +  return addr_expr;
> +}
> +
> +/* Prepare call to PROFILE_CALL_* builtin (specified by CODE) for
> +   function with decl FNDECL and add it to the sequence of GIMPLE
> +   statements in PRE_P.  */
> +
> +static void
> +maybe_add_profile_call (tree addr_expr, enum built_in_function code,
> +                       gimple_seq *pre_p)
> +{
> +  if (addr_expr)
> +    {
> +      tree x = builtin_decl_implicit (code);
> +      gimple call = gimple_build_call (x, 1, addr_expr);
> +      gimplify_seq_add_stmt (pre_p, call);
> +    }
> +}
> +
>  /* Gimplify a BIND_EXPR.  Just voidify and recurse.  */
>
>  static enum gimplify_status
> @@ -2684,15 +2743,22 @@ gimplify_call_expr (tree *expr_p, gimple_seq *pre_p, bool want_value)
>       gimplify_modify_expr.  */
>    if (!want_value)
>      {
> +      tree addr_expr = addr_expr_for_call_instrumentation (*expr_p);
> +      gimple_stmt_iterator gsi;
> +
> +      maybe_add_profile_call (addr_expr, BUILT_IN_PROFILE_CALL_BEFORE, pre_p);
> +
>        /* The CALL_EXPR in *EXPR_P is already in GIMPLE form, so all we
>          have to do is replicate it as a GIMPLE_CALL tuple.  */
> -      gimple_stmt_iterator gsi;
>        call = gimple_build_call_from_tree (*expr_p);
>        gimple_call_set_fntype (call, TREE_TYPE (fnptrtype));
>        notice_special_calls (call);
>        gimplify_seq_add_stmt (pre_p, call);
>        gsi = gsi_last (*pre_p);
>        fold_stmt (&gsi);
> +
> +      maybe_add_profile_call (addr_expr, BUILT_IN_PROFILE_CALL_AFTER, pre_p);
> +
>        *expr_p = NULL_TREE;
>      }
>    else
> @@ -4793,6 +4859,7 @@ gimplify_modify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p,
>    gimple assign;
>    location_t loc = EXPR_LOCATION (*expr_p);
>    gimple_stmt_iterator gsi;
> +  tree addr_expr = NULL_TREE;
>
>    gcc_assert (TREE_CODE (*expr_p) == MODIFY_EXPR
>               || TREE_CODE (*expr_p) == INIT_EXPR);
> @@ -4922,9 +4989,13 @@ gimplify_modify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p,
>
>    if (TREE_CODE (*from_p) == CALL_EXPR)
>      {
> +      tree fnptrtype;
> +
> +      addr_expr = addr_expr_for_call_instrumentation (*from_p);
> +
>        /* Since the RHS is a CALL_EXPR, we need to create a GIMPLE_CALL
>          instead of a GIMPLE_ASSIGN.  */
> -      tree fnptrtype = TREE_TYPE (CALL_EXPR_FN (*from_p));
> +      fnptrtype = TREE_TYPE (CALL_EXPR_FN (*from_p));
>        CALL_EXPR_FN (*from_p) = TREE_OPERAND (CALL_EXPR_FN (*from_p), 0);
>        STRIP_USELESS_TYPE_CONVERSION (CALL_EXPR_FN (*from_p));
>        assign = gimple_build_call_from_tree (*from_p);
> @@ -4945,7 +5016,9 @@ gimplify_modify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p,
>        gcc_assert (TREE_CODE (*to_p) == SSA_NAME);
>      }
>
> +  maybe_add_profile_call (addr_expr, BUILT_IN_PROFILE_CALL_BEFORE, pre_p);
>    gimplify_seq_add_stmt (pre_p, assign);
> +  maybe_add_profile_call (addr_expr, BUILT_IN_PROFILE_CALL_AFTER, pre_p);
>    gsi = gsi_last (*pre_p);
>    fold_stmt (&gsi);
>
> @@ -8284,6 +8357,42 @@ flag_instrument_functions_exclude_p (tree fndecl)
>    return false;
>  }
>
> +/* Return whether we should exclude FNDECL from call instrumentation.  */
> +
> +static bool
> +flag_instrument_calls_exclude_p (tree fndecl)
> +{
> +  vec<char_p> *v;
> +
> +  v = (vec<char_p> *) flag_instrument_calls_exclude_functions;
> +  if (v && v->length () > 0)
> +    {
> +      const char *name;
> +      int i;
> +      char *s;
> +
> +      name = lang_hooks.decl_printable_name (fndecl, 0);
> +      FOR_EACH_VEC_ELT (*v, i, s)
> +       if (strstr (name, s) != NULL)
> +         return true;
> +    }
> +
> +  v = (vec<char_p> *) flag_instrument_calls_exclude_files;
> +  if (v && v->length () > 0)
> +    {
> +      const char *name;
> +      int i;
> +      char *s;
> +
> +      name = DECL_SOURCE_FILE (fndecl);
> +      FOR_EACH_VEC_ELT (*v, i, s)
> +       if (strstr (name, s) != NULL)
> +         return true;
> +    }
> +
> +  return false;
> +}
> +
>  /* Entry point to the gimplification pass.  FNDECL is the FUNCTION_DECL
>     node for the function we want to gimplify.
>
> diff --git a/gcc/ipa.c b/gcc/ipa.c
> index 7c0d495..b62b301 100644
> --- a/gcc/ipa.c
> +++ b/gcc/ipa.c
> @@ -1429,6 +1429,7 @@ cgraph_build_static_cdtor_1 (char which, tree body, int priority, bool final)
>    TREE_USED (decl) = 1;
>    DECL_ARTIFICIAL (decl) = 1;
>    DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (decl) = 1;
> +  DECL_NO_INSTRUMENT_CALLS_BEFORE_AFTER (decl) = 1;
>    DECL_SAVED_TREE (decl) = body;
>    if (!targetm.have_ctors_dtors && final)
>      {
> diff --git a/gcc/java/jcf-parse.c b/gcc/java/jcf-parse.c
> index fbd4e00..5ead4a6 100644
> --- a/gcc/java/jcf-parse.c
> +++ b/gcc/java/jcf-parse.c
> @@ -1715,6 +1715,7 @@ java_emit_static_constructor (void)
>        TREE_USED (decl) = 1;
>        DECL_ARTIFICIAL (decl) = 1;
>        DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (decl) = 1;
> +      DECL_NO_INSTRUMENT_CALLS_BEFORE_AFTER (decl) = 1;
>        DECL_SAVED_TREE (decl) = body;
>        DECL_UNINLINABLE (decl) = 1;
>
> diff --git a/gcc/libfuncs.h b/gcc/libfuncs.h
> index 04a4dc2..4c7faa4 100644
> --- a/gcc/libfuncs.h
> +++ b/gcc/libfuncs.h
> @@ -40,6 +40,9 @@ enum libfunc_index
>    LTI_profile_function_entry,
>    LTI_profile_function_exit,
>
> +  LTI_profile_call_before,
> +  LTI_profile_call_after,
> +
>    LTI_synchronize,
>
>    LTI_gcov_flush,
> @@ -98,6 +101,9 @@ extern struct target_libfuncs *this_target_libfuncs;
>  #define profile_function_entry_libfunc (libfunc_table[LTI_profile_function_entry])
>  #define profile_function_exit_libfunc  (libfunc_table[LTI_profile_function_exit])
>
> +#define profile_call_before_libfunc    (libfunc_table[LTI_profile_call_before])
> +#define profile_call_after_libfunc     (libfunc_table[LTI_profile_call_after])
> +
>  #define synchronize_libfunc    (libfunc_table[LTI_synchronize])
>
>  #define gcov_flush_libfunc     (libfunc_table[LTI_gcov_flush])
> diff --git a/gcc/optabs.c b/gcc/optabs.c
> index a3051ad..9b93900 100644
> --- a/gcc/optabs.c
> +++ b/gcc/optabs.c
> @@ -6202,6 +6202,12 @@ init_optabs (void)
>    profile_function_exit_libfunc
>      = init_one_libfunc ("__cyg_profile_func_exit");
>
> +  /* For call before/after instrumentation.  */
> +  profile_call_before_libfunc
> +    = init_one_libfunc ("__cyg_profile_call_before");
> +  profile_call_after_libfunc
> +    = init_one_libfunc ("__cyg_profile_call_after");
> +
>    gcov_flush_libfunc = init_one_libfunc ("__gcov_flush");
>
>    /* Allow the target to add more libcalls or rename some, etc.  */
> diff --git a/gcc/opts.c b/gcc/opts.c
> index 6856c3c..14bb78d 100644
> --- a/gcc/opts.c
> +++ b/gcc/opts.c
> @@ -1540,6 +1540,16 @@ common_handle_option (struct gcc_options *opts,
>         (&opts->x_flag_instrument_functions_exclude_files, arg);
>        break;
>
> +    case OPT_finstrument_calls_exclude_function_list_:
> +      add_comma_separated_to_vector
> +       (&opts->x_flag_instrument_calls_exclude_functions, arg);
> +      break;
> +
> +    case OPT_finstrument_calls_exclude_file_list_:
> +      add_comma_separated_to_vector
> +       (&opts->x_flag_instrument_calls_exclude_files, arg);
> +      break;
> +
>      case OPT_fmessage_length_:
>        pp_set_line_maximum_length (dc->printer, value);
>        diagnostic_set_caret_max_width (dc, value);
> diff --git a/gcc/testsuite/g++.dg/other/instrument_calls-1.C b/gcc/testsuite/g++.dg/other/instrument_calls-1.C
> new file mode 100644
> index 0000000..e1bf365
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/other/instrument_calls-1.C
> @@ -0,0 +1,14 @@
> +/* { dg-do compile } */
> +/* { dg-options "-finstrument-calls" } */
> +
> +class Base
> +{
> +public:
> +       virtual void foo();
> +};
> +
> +void fn_caller( Base* b ) { b->foo(); }
> +
> +/* { dg-final { scan-assembler "__cyg_profile_call_before" } } */
> +/* { dg-final { scan-assembler "__cyg_profile_call_after" } } */
> +
> diff --git a/gcc/testsuite/g++.dg/other/instrument_calls-2.C b/gcc/testsuite/g++.dg/other/instrument_calls-2.C
> new file mode 100644
> index 0000000..02b113c
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/other/instrument_calls-2.C
> @@ -0,0 +1,20 @@
> +/* { dg-do compile } */
> +/* { dg-options "-finstrument-calls" } */
> +
> +#define CALL_MEMBER_FN(object,ptrToMember)  ((object).*(ptrToMember))
> +
> +class Base
> +{
> +public:
> +       virtual void foo();
> +       virtual void bar();
> +       virtual void foobar(int i);
> +       virtual void barfoo(int i);
> +};
> +
> +typedef void (Base::*BaseMemFn)(int i);
> +void fn_caller( Base& obj, BaseMemFn memfnptr ) { CALL_MEMBER_FN(obj, memfnptr)(42); }
> +
> +/* { dg-final { scan-assembler "__cyg_profile_call_before" } } */
> +/* { dg-final { scan-assembler "__cyg_profile_call_after" } } */
> +
> diff --git a/gcc/testsuite/g++.dg/other/instrument_calls-3.C b/gcc/testsuite/g++.dg/other/instrument_calls-3.C
> new file mode 100644
> index 0000000..eccf7c1
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/other/instrument_calls-3.C
> @@ -0,0 +1,17 @@
> +/* { dg-do compile } */
> +/* { dg-options "-finstrument-calls" } */
> +
> +class Base
> +{
> +public:
> +       int bar();
> +};
> +
> +int fn_caller( Base& b )
> +{
> +   b.bar();
> +}
> +
> +/* { dg-final { scan-assembler "__cyg_profile_call_before" } } */
> +/* { dg-final { scan-assembler "__cyg_profile_call_after" } } */
> +
> diff --git a/gcc/testsuite/gcc.dg/instrument_calls-1.c b/gcc/testsuite/gcc.dg/instrument_calls-1.c
> new file mode 100644
> index 0000000..eac08f4
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/instrument_calls-1.c
> @@ -0,0 +1,8 @@
> +/* { dg-do compile } */
> +/* { dg-options "-finstrument-calls" } */
> +
> +void fn () { }
> +void fn_caller () { fn (); }
> +
> +/* { dg-final { scan-assembler "__cyg_profile_call_before" } } */
> +/* { dg-final { scan-assembler "__cyg_profile_call_after" } } */
> diff --git a/gcc/testsuite/gcc.dg/instrument_calls-2.c b/gcc/testsuite/gcc.dg/instrument_calls-2.c
> new file mode 100644
> index 0000000..b897d98
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/instrument_calls-2.c
> @@ -0,0 +1,8 @@
> +/* { dg-do compile } */
> +/* { dg-options "-finstrument-calls -finstrument-calls-exclude-function-list=fn_caller" } */
> +
> +void fn () { }
> +void fn_caller () { fn (); }
> +
> +/* { dg-final { scan-assembler-not "__cyg_profile_call_before" } } */
> +/* { dg-final { scan-assembler-not "__cyg_profile_call_after" } } */
> diff --git a/gcc/testsuite/gcc.dg/instrument_calls-3.c b/gcc/testsuite/gcc.dg/instrument_calls-3.c
> new file mode 100644
> index 0000000..875de8d
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/instrument_calls-3.c
> @@ -0,0 +1,8 @@
> +/* { dg-do compile } */
> +/* { dg-options "-finstrument-calls -finstrument-calls-exclude-file-list=instrument_calls-3" } */
> +
> +void fn () { }
> +void fn_caller () { fn (); }
> +
> +/* { dg-final { scan-assembler-not "__cyg_profile_call_before" } } */
> +/* { dg-final { scan-assembler-not "__cyg_profile_call_after" } } */
> diff --git a/gcc/testsuite/gcc.dg/instrument_calls-4.c b/gcc/testsuite/gcc.dg/instrument_calls-4.c
> new file mode 100644
> index 0000000..e2f896f
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/instrument_calls-4.c
> @@ -0,0 +1,8 @@
> +/* { dg-do compile } */
> +/* { dg-options "-finstrument-calls" } */
> +
> +#include <stdio.h>
> +void fn_caller () { puts (""); }
> +
> +/* { dg-final { scan-assembler "__cyg_profile_call_before" } } */
> +/* { dg-final { scan-assembler "__cyg_profile_call_after" } } */
> diff --git a/gcc/testsuite/gcc.dg/instrument_calls-5.c b/gcc/testsuite/gcc.dg/instrument_calls-5.c
> new file mode 100644
> index 0000000..84b078d
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/instrument_calls-5.c
> @@ -0,0 +1,11 @@
> +/* { dg-do compile } */
> +/* { dg-options "-finstrument-calls" } */
> +
> +int fn (int i) { }
> +int fn_caller (int i)
> +{
> +    return fn (i);
> +}
> +
> +/* { dg-final { scan-assembler "__cyg_profile_call_before" } } */
> +/* { dg-final { scan-assembler "__cyg_profile_call_after" } } */
> diff --git a/gcc/testsuite/gcc.dg/instrument_calls-6.c b/gcc/testsuite/gcc.dg/instrument_calls-6.c
> new file mode 100644
> index 0000000..8514cc8
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/instrument_calls-6.c
> @@ -0,0 +1,11 @@
> +/* { dg-do compile } */
> +/* { dg-options "-finstrument-calls" } */
> +
> +void fn_caller ()
> +{
> +    void fn () { }
> +    fn ();
> +}
> +
> +/* { dg-final { scan-assembler "__cyg_profile_call_before" } } */
> +/* { dg-final { scan-assembler "__cyg_profile_call_after" } } */
> diff --git a/gcc/testsuite/gcc.dg/instrument_calls-7.c b/gcc/testsuite/gcc.dg/instrument_calls-7.c
> new file mode 100644
> index 0000000..86bcc52
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/instrument_calls-7.c
> @@ -0,0 +1,13 @@
> +/* { dg-do compile } */
> +/* { dg-options "-finstrument-calls" } */
> +
> +void *p_fn[3];
> +int fn_caller (int i, char *fmt, ...)
> +{
> +    void *arg = __builtin_apply_args();
> +    void *ret = __builtin_apply(p_fn[i], arg, 0xff);
> +    __builtin_return(ret);
> +}
> +
> +/* { dg-final { scan-assembler "__cyg_profile_call_before" } } */
> +/* { dg-final { scan-assembler "__cyg_profile_call_after" } } */
> diff --git a/gcc/testsuite/gcc.dg/instrument_calls-8.c b/gcc/testsuite/gcc.dg/instrument_calls-8.c
> new file mode 100644
> index 0000000..e38b44c
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/instrument_calls-8.c
> @@ -0,0 +1,7 @@
> +/* { dg-do compile } */
> +/* { dg-options "-finstrument-calls" } */
> +
> +void fn_caller ( void (*p_fn)() ) { p_fn (); }
> +
> +/* { dg-final { scan-assembler "__cyg_profile_call_before" } } */
> +/* { dg-final { scan-assembler "__cyg_profile_call_after" } } */
> diff --git a/gcc/testsuite/gcc.dg/instrument_calls-9.c b/gcc/testsuite/gcc.dg/instrument_calls-9.c
> new file mode 100644
> index 0000000..88cf700
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/instrument_calls-9.c
> @@ -0,0 +1,12 @@
> +/* { dg-do compile } */
> +/* { dg-options "-finstrument-calls" } */
> +
> +__attribute__((no_instrument_function)) void fn ();
> +
> +void fn_caller ()
> +{
> +    fn ();
> +}
> +
> +/* { dg-final { scan-assembler-not "__cyg_profile_call_before" } } */
> +/* { dg-final { scan-assembler-not "__cyg_profile_call_after" } } */
> diff --git a/gcc/tree-streamer-in.c b/gcc/tree-streamer-in.c
> index 00f78a1..967b1e7 100644
> --- a/gcc/tree-streamer-in.c
> +++ b/gcc/tree-streamer-in.c
> @@ -305,6 +305,8 @@ unpack_ts_function_decl_value_fields (struct bitpack_d *bp, tree expr)
>    DECL_NO_INLINE_WARNING_P (expr) = (unsigned) bp_unpack_value (bp, 1);
>    DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (expr)
>                         = (unsigned) bp_unpack_value (bp, 1);
> +  DECL_NO_INSTRUMENT_CALLS_BEFORE_AFTER (expr)
> +                       = (unsigned) bp_unpack_value (bp, 1);
>    DECL_NO_LIMIT_STACK (expr) = (unsigned) bp_unpack_value (bp, 1);
>    DECL_DISREGARD_INLINE_LIMITS (expr) = (unsigned) bp_unpack_value (bp, 1);
>    DECL_PURE_P (expr) = (unsigned) bp_unpack_value (bp, 1);
> diff --git a/gcc/tree-streamer-out.c b/gcc/tree-streamer-out.c
> index fa50ef5..f71fea6 100644
> --- a/gcc/tree-streamer-out.c
> +++ b/gcc/tree-streamer-out.c
> @@ -271,6 +271,7 @@ pack_ts_function_decl_value_fields (struct bitpack_d *bp, tree expr)
>    bp_pack_value (bp, DECL_STATIC_CHAIN (expr), 1);
>    bp_pack_value (bp, DECL_NO_INLINE_WARNING_P (expr), 1);
>    bp_pack_value (bp, DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (expr), 1);
> +  bp_pack_value (bp, DECL_NO_INSTRUMENT_CALLS_BEFORE_AFTER (expr), 1);
>    bp_pack_value (bp, DECL_NO_LIMIT_STACK (expr), 1);
>    bp_pack_value (bp, DECL_DISREGARD_INLINE_LIMITS (expr), 1);
>    bp_pack_value (bp, DECL_PURE_P (expr), 1);
> diff --git a/gcc/tree.h b/gcc/tree.h
> index b444517..456e41e 100644
> --- a/gcc/tree.h
> +++ b/gcc/tree.h
> @@ -3375,6 +3375,11 @@ struct GTY(())
>  #define DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT(NODE) \
>    (FUNCTION_DECL_CHECK (NODE)->function_decl.no_instrument_function_entry_exit)
>
> +/* Used in FUNCTION_DECLs to indicate that function calls in that function should
> +   be instrumented with calls to support routines before and after each function call.  */
> +#define DECL_NO_INSTRUMENT_CALLS_BEFORE_AFTER(NODE) \
> +  (FUNCTION_DECL_CHECK (NODE)->function_decl.no_instrument_calls_before_after)
> +
>  /* Used in FUNCTION_DECLs to indicate that limit-stack-* should be
>     disabled in this function.  */
>  #define DECL_NO_LIMIT_STACK(NODE) \
> @@ -3512,6 +3517,7 @@ struct GTY(()) tree_function_decl {
>    unsigned no_inline_warning_flag : 1;
>
>    unsigned no_instrument_function_entry_exit : 1;
> +  unsigned no_instrument_calls_before_after : 1;
>    unsigned no_limit_stack : 1;
>    unsigned disregard_inline_limits : 1;
>    unsigned pure_flag : 1;
> @@ -3519,7 +3525,7 @@ struct GTY(()) tree_function_decl {
>    unsigned has_debug_args_flag : 1;
>    unsigned tm_clone_flag : 1;
>    unsigned versioned_function : 1;
> -  /* No bits left.  */
> +  /* -1 bit left */
>  };
>
>  /* The source language of the translation-unit.  */
> --
> 1.8.1.4
>
>
diff mbox

Patch

diff --git a/gcc/builtins.def b/gcc/builtins.def
index 91879a6..d175692 100644
--- a/gcc/builtins.def
+++ b/gcc/builtins.def
@@ -795,6 +795,11 @@  DEF_BUILTIN (BUILT_IN_PROFILE_FUNC_ENTER, "__cyg_profile_func_enter", BUILT_IN_N
 DEF_BUILTIN (BUILT_IN_PROFILE_FUNC_EXIT, "__cyg_profile_func_exit", BUILT_IN_NORMAL, BT_FN_VOID_PTR_PTR, BT_LAST,
 	     false, false, false, ATTR_NULL, true, true)
 
+DEF_BUILTIN (BUILT_IN_PROFILE_CALL_BEFORE, "__cyg_profile_call_before", BUILT_IN_NORMAL, BT_FN_VOID_PTR, BT_LAST,
+	     false, false, false, ATTR_NULL, true, true)
+DEF_BUILTIN (BUILT_IN_PROFILE_CALL_AFTER, "__cyg_profile_call_after", BUILT_IN_NORMAL, BT_FN_VOID_PTR, BT_LAST,
+	     false, false, false, ATTR_NULL, true, true)
+
 /* TLS thread pointer related builtins.  */
 DEF_BUILTIN (BUILT_IN_THREAD_POINTER, "__builtin_thread_pointer",
 	     BUILT_IN_NORMAL, BT_FN_PTR, BT_LAST,
diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
index 8f7f5e5..f3ad003 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -343,6 +343,8 @@  static tree handle_tls_model_attribute (tree *, tree, tree, int,
 					bool *);
 static tree handle_no_instrument_function_attribute (tree *, tree,
 						     tree, int, bool *);
+static tree handle_no_instrument_calls_attribute (tree *, tree,
+						     tree, int, bool *);
 static tree handle_malloc_attribute (tree *, tree, tree, int, bool *);
 static tree handle_returns_twice_attribute (tree *, tree, tree, int, bool *);
 static tree handle_no_limit_stack_attribute (tree *, tree, tree, int,
@@ -658,6 +660,9 @@  const struct attribute_spec c_common_attribute_table[] =
   { "no_instrument_function", 0, 0, true,  false, false,
 			      handle_no_instrument_function_attribute,
 			      false },
+  { "no_instrument_calls", 0, 0, true,  false, false,
+			      handle_no_instrument_calls_attribute,
+			      false },
   { "malloc",                 0, 0, true,  false, false,
 			      handle_malloc_attribute, false },
   { "returns_twice",          0, 0, true,  false, false,
@@ -7891,6 +7896,35 @@  handle_no_instrument_function_attribute (tree *node, tree name,
   return NULL_TREE;
 }
 
+/* Handle a "no_instrument_calls" attribute; arguments as in
+   struct attribute_spec.handler.  */
+
+static tree
+handle_no_instrument_calls_attribute (tree *node, tree name,
+					 tree ARG_UNUSED (args),
+					 int ARG_UNUSED (flags),
+					 bool *no_add_attrs)
+{
+  tree decl = *node;
+
+  if (TREE_CODE (decl) != FUNCTION_DECL)
+    {
+      error_at (DECL_SOURCE_LOCATION (decl),
+		"%qE attribute applies only to functions", name);
+      *no_add_attrs = true;
+    }
+  else if (DECL_INITIAL (decl))
+    {
+      error_at (DECL_SOURCE_LOCATION (decl),
+		"can%'t set %qE attribute after definition", name);
+      *no_add_attrs = true;
+    }
+  else
+    DECL_NO_INSTRUMENT_CALLS_BEFORE_AFTER (decl) = 1;
+
+  return NULL_TREE;
+}
+
 /* Handle a "malloc" attribute; arguments as in
    struct attribute_spec.handler.  */
 
diff --git a/gcc/c/c-decl.c b/gcc/c/c-decl.c
index 8170a80..4657e48 100644
--- a/gcc/c/c-decl.c
+++ b/gcc/c/c-decl.c
@@ -2287,6 +2287,8 @@  merge_decls (tree newdecl, tree olddecl, tree newtype, tree oldtype)
 	  DECL_NO_LIMIT_STACK (newdecl) |= DECL_NO_LIMIT_STACK (olddecl);
 	  DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (newdecl)
 	    |= DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (olddecl);
+	  DECL_NO_INSTRUMENT_CALLS_BEFORE_AFTER (newdecl)
+	    |= DECL_NO_INSTRUMENT_CALLS_BEFORE_AFTER (olddecl);
 	  TREE_THIS_VOLATILE (newdecl) |= TREE_THIS_VOLATILE (olddecl);
 	  DECL_IS_MALLOC (newdecl) |= DECL_IS_MALLOC (olddecl);
 	  DECL_IS_OPERATOR_NEW (newdecl) |= DECL_IS_OPERATOR_NEW (olddecl);
diff --git a/gcc/common.opt b/gcc/common.opt
index 4c7933e..90cb06d 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -93,7 +93,7 @@  int flag_gen_aux_info = 0
 Variable
 int flag_shlib
 
-; These two are really VEC(char_p,heap) *.
+; These four are really VEC(char_p,heap) *.
 
 Variable
 void *flag_instrument_functions_exclude_functions
@@ -101,6 +101,12 @@  void *flag_instrument_functions_exclude_functions
 Variable
 void *flag_instrument_functions_exclude_files
 
+Variable
+void *flag_instrument_calls_exclude_functions
+
+Variable
+void *flag_instrument_calls_exclude_files
+
 ; Generic structs (e.g. templates not explicitly specialized)
 ; may not have a compilation unit associated with them, and so
 ; may need to be treated differently from ordinary structs.
@@ -1364,6 +1370,18 @@  finstrument-functions-exclude-file-list=
 Common RejectNegative Joined
 -finstrument-functions-exclude-file-list=filename,...  Do not instrument functions listed in files
 
+finstrument-calls
+Common Report Var(flag_instrument_calls_before_after)
+Instrument call entry and exit with profiling calls
+
+finstrument-calls-exclude-function-list=
+Common RejectNegative Joined
+-finstrument-calls-exclude-function-list=name,...  Do not instrument calls from listed functions
+
+finstrument-calls-exclude-file-list=
+Common RejectNegative Joined
+-finstrument-calls-exclude-file-list=filename,...  Do not instrument calls from functions listed in files
+
 fipa-cp
 Common Report Var(flag_ipa_cp) Optimization
 Perform Interprocedural constant propagation
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index 047fd77..9a02012 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -1971,6 +1971,8 @@  duplicate_decls (tree newdecl, tree olddecl, bool newdecl_is_friend)
 	{
 	  DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (newdecl)
 	    |= DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (olddecl);
+	  DECL_NO_INSTRUMENT_CALLS_BEFORE_AFTER (newdecl)
+	    |= DECL_NO_INSTRUMENT_CALLS_BEFORE_AFTER (olddecl);
 	  DECL_NO_LIMIT_STACK (newdecl) |= DECL_NO_LIMIT_STACK (olddecl);
 	  TREE_THIS_VOLATILE (newdecl) |= TREE_THIS_VOLATILE (olddecl);
 	  TREE_NOTHROW (newdecl) |= TREE_NOTHROW (olddecl);
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 1496d30..a1a3579 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -1015,6 +1015,9 @@  See S/390 and zSeries Options.
 -finhibit-size-directive  -finstrument-functions @gol
 -finstrument-functions-exclude-function-list=@var{sym},@var{sym},@dots{} @gol
 -finstrument-functions-exclude-file-list=@var{file},@var{file},@dots{} @gol
+-finstrument-calls @gol
+-finstrument-calls-exclude-function-list=@var{sym},@var{sym},@dots{} @gol
+-finstrument-calls-exclude-file-list=@var{file},@var{file},@dots{} @gol
 -fno-common  -fno-ident @gol
 -fpcc-struct-return  -fpic  -fPIC -fpie -fPIE @gol
 -fno-jump-tables @gol
@@ -20706,6 +20709,45 @@  of the function name, it is considered to be a match.  For C99 and C++
 extended identifiers, the function name must be given in UTF-8, not
 using universal character names.
 
+@item -finstrument-calls
+@opindex finstrument-calls
+Generate instrumentation calls immediately before and after each
+function call. The following profiling functions will be called with
+the address of the function that is called between them. Use
+@code{__builtin_return_address(0)} inside the profiling functions to
+get the addresses from where they are called.
+
+@smallexample
+void __cyg_profile_call_before (void *fn);
+void __cyg_profile_call_after  (void *fn);
+@end smallexample
+
+A function may be given attribute @code{no_instrument_calls}, in which
+case the instrumentation is omitted (no calls within that function will
+be instrumented). 
+
+In addition, calls to functions which have been given attribute
+@code{no_instrument_function} (or selected via
+@code{-finstrument-functions-exclude} options) are also excluded from
+instrumentation.
+
+@item -finstrument-calls-exclude-file-list=@var{file},@var{file},@dots{}
+@opindex finstrument-calls-exclude-file-list
+
+Set the list of functions that are excluded from instrumentation (see
+the description of @code{-finstrument-calls}).  If the file that
+contains a function definition matches with one of @var{file}, then
+the calls in that function are not instrumented.  The match is done on
+substrings as for @code{-finstrument-functions-exclude-file-list}.
+
+@item -finstrument-calls-exclude-function-list=@var{sym},@var{sym},@dots{}
+@opindex finstrument-calls-exclude-function-list
+
+This is similar to @code{-finstrument-calls-exclude-file-list},
+but this option sets the list of function names to be excluded from
+instrumentation.  The function name to be matched in the same way as for
+@code{-finstrument-functions-exclude-function-list}
+
 @item -fstack-check
 @opindex fstack-check
 Generate code to verify that you do not go beyond the boundary of the
diff --git a/gcc/function.c b/gcc/function.c
index 3e33fc7..7290d76 100644
--- a/gcc/function.c
+++ b/gcc/function.c
@@ -4728,7 +4728,8 @@  expand_function_start (tree subr)
 
   crtl->profile
     = (profile_flag
-       && ! DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (subr));
+       && ! DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (subr)
+       && ! DECL_NO_INSTRUMENT_CALLS_BEFORE_AFTER (subr));
 
   crtl->limit_stack
     = (stack_limit_rtx != NULL_RTX && ! DECL_NO_LIMIT_STACK (subr));
diff --git a/gcc/gimplify.c b/gcc/gimplify.c
index e2ae893..8401278 100644
--- a/gcc/gimplify.c
+++ b/gcc/gimplify.c
@@ -89,6 +89,8 @@  static struct gimplify_omp_ctx *gimplify_omp_ctxp;
 
 /* Forward declaration.  */
 static enum gimplify_status gimplify_compound_expr (tree *, gimple_seq *, bool);
+static bool flag_instrument_calls_exclude_p (tree fndecl);
+static bool flag_instrument_functions_exclude_p (tree fndecl);
 
 /* Mark X addressable.  Unlike the langhook we expect X to be in gimple
    form and we don't do any syntax checking.  */
@@ -1153,6 +1155,63 @@  build_stack_save_restore (gimple *save, gimple *restore)
 			 1, tmp_var);
 }
 
+/* Returns the function decl that corresponds the function called in
+   CALL_EXPR if call instrumentation is enabled.  */
+
+static tree
+addr_expr_for_call_instrumentation (tree call_expr)
+{
+  tree addr_expr = NULL_TREE;
+
+  if (!gimplify_ctxp->into_ssa && flag_instrument_calls_before_after
+      && !DECL_NO_INSTRUMENT_CALLS_BEFORE_AFTER (current_function_decl)
+      && !flag_instrument_calls_exclude_p (current_function_decl))
+    {
+      tree fndecl = get_callee_fndecl (call_expr);
+      if (fndecl)
+        {
+	  if (!DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (fndecl)
+	      && !flag_instrument_functions_exclude_p (fndecl))
+	    {
+	      if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL
+		      && DECL_FUNCTION_CODE (fndecl) == BUILT_IN_APPLY
+		      && call_expr_nargs (call_expr) > 0)
+		  addr_expr = CALL_EXPR_ARG (call_expr, 0);
+	      else if (!DECL_IS_BUILTIN (fndecl))
+		  addr_expr = build_fold_addr_expr (fndecl);
+	    }
+        }
+      else
+	  addr_expr = CALL_EXPR_FN (call_expr);
+    }
+
+  if (addr_expr)
+    {
+      if (TREE_CODE (addr_expr) == OBJ_TYPE_REF)
+	addr_expr = OBJ_TYPE_REF_EXPR (addr_expr);
+      else if (!is_gimple_val (addr_expr))
+	addr_expr = NULL_TREE;
+    }
+
+  return addr_expr;
+}
+
+/* Prepare call to PROFILE_CALL_* builtin (specified by CODE) for
+   function with decl FNDECL and add it to the sequence of GIMPLE
+   statements in PRE_P.  */
+
+static void
+maybe_add_profile_call (tree addr_expr, enum built_in_function code,
+			gimple_seq *pre_p)
+{
+  if (addr_expr)
+    {
+      tree x = builtin_decl_implicit (code);
+      gimple call = gimple_build_call (x, 1, addr_expr);
+      gimplify_seq_add_stmt (pre_p, call);
+    }
+}
+
 /* Gimplify a BIND_EXPR.  Just voidify and recurse.  */
 
 static enum gimplify_status
@@ -2684,15 +2743,22 @@  gimplify_call_expr (tree *expr_p, gimple_seq *pre_p, bool want_value)
      gimplify_modify_expr.  */
   if (!want_value)
     {
+      tree addr_expr = addr_expr_for_call_instrumentation (*expr_p);
+      gimple_stmt_iterator gsi;
+
+      maybe_add_profile_call (addr_expr, BUILT_IN_PROFILE_CALL_BEFORE, pre_p);
+
       /* The CALL_EXPR in *EXPR_P is already in GIMPLE form, so all we
 	 have to do is replicate it as a GIMPLE_CALL tuple.  */
-      gimple_stmt_iterator gsi;
       call = gimple_build_call_from_tree (*expr_p);
       gimple_call_set_fntype (call, TREE_TYPE (fnptrtype));
       notice_special_calls (call);
       gimplify_seq_add_stmt (pre_p, call);
       gsi = gsi_last (*pre_p);
       fold_stmt (&gsi);
+
+      maybe_add_profile_call (addr_expr, BUILT_IN_PROFILE_CALL_AFTER, pre_p);
+
       *expr_p = NULL_TREE;
     }
   else
@@ -4793,6 +4859,7 @@  gimplify_modify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p,
   gimple assign;
   location_t loc = EXPR_LOCATION (*expr_p);
   gimple_stmt_iterator gsi;
+  tree addr_expr = NULL_TREE;
 
   gcc_assert (TREE_CODE (*expr_p) == MODIFY_EXPR
 	      || TREE_CODE (*expr_p) == INIT_EXPR);
@@ -4922,9 +4989,13 @@  gimplify_modify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p,
 
   if (TREE_CODE (*from_p) == CALL_EXPR)
     {
+      tree fnptrtype;
+
+      addr_expr = addr_expr_for_call_instrumentation (*from_p);
+
       /* Since the RHS is a CALL_EXPR, we need to create a GIMPLE_CALL
 	 instead of a GIMPLE_ASSIGN.  */
-      tree fnptrtype = TREE_TYPE (CALL_EXPR_FN (*from_p));
+      fnptrtype = TREE_TYPE (CALL_EXPR_FN (*from_p));
       CALL_EXPR_FN (*from_p) = TREE_OPERAND (CALL_EXPR_FN (*from_p), 0);
       STRIP_USELESS_TYPE_CONVERSION (CALL_EXPR_FN (*from_p));
       assign = gimple_build_call_from_tree (*from_p);
@@ -4945,7 +5016,9 @@  gimplify_modify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p,
       gcc_assert (TREE_CODE (*to_p) == SSA_NAME);
     }
 
+  maybe_add_profile_call (addr_expr, BUILT_IN_PROFILE_CALL_BEFORE, pre_p);
   gimplify_seq_add_stmt (pre_p, assign);
+  maybe_add_profile_call (addr_expr, BUILT_IN_PROFILE_CALL_AFTER, pre_p);
   gsi = gsi_last (*pre_p);
   fold_stmt (&gsi);
 
@@ -8284,6 +8357,42 @@  flag_instrument_functions_exclude_p (tree fndecl)
   return false;
 }
 
+/* Return whether we should exclude FNDECL from call instrumentation.  */
+
+static bool
+flag_instrument_calls_exclude_p (tree fndecl)
+{
+  vec<char_p> *v;
+
+  v = (vec<char_p> *) flag_instrument_calls_exclude_functions;
+  if (v && v->length () > 0)
+    {
+      const char *name;
+      int i;
+      char *s;
+
+      name = lang_hooks.decl_printable_name (fndecl, 0);
+      FOR_EACH_VEC_ELT (*v, i, s)
+	if (strstr (name, s) != NULL)
+	  return true;
+    }
+
+  v = (vec<char_p> *) flag_instrument_calls_exclude_files;
+  if (v && v->length () > 0)
+    {
+      const char *name;
+      int i;
+      char *s;
+
+      name = DECL_SOURCE_FILE (fndecl);
+      FOR_EACH_VEC_ELT (*v, i, s)
+	if (strstr (name, s) != NULL)
+	  return true;
+    }
+
+  return false;
+}
+
 /* Entry point to the gimplification pass.  FNDECL is the FUNCTION_DECL
    node for the function we want to gimplify.
 
diff --git a/gcc/ipa.c b/gcc/ipa.c
index 7c0d495..b62b301 100644
--- a/gcc/ipa.c
+++ b/gcc/ipa.c
@@ -1429,6 +1429,7 @@  cgraph_build_static_cdtor_1 (char which, tree body, int priority, bool final)
   TREE_USED (decl) = 1;
   DECL_ARTIFICIAL (decl) = 1;
   DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (decl) = 1;
+  DECL_NO_INSTRUMENT_CALLS_BEFORE_AFTER (decl) = 1;
   DECL_SAVED_TREE (decl) = body;
   if (!targetm.have_ctors_dtors && final)
     {
diff --git a/gcc/java/jcf-parse.c b/gcc/java/jcf-parse.c
index fbd4e00..5ead4a6 100644
--- a/gcc/java/jcf-parse.c
+++ b/gcc/java/jcf-parse.c
@@ -1715,6 +1715,7 @@  java_emit_static_constructor (void)
       TREE_USED (decl) = 1;
       DECL_ARTIFICIAL (decl) = 1;
       DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (decl) = 1;
+      DECL_NO_INSTRUMENT_CALLS_BEFORE_AFTER (decl) = 1;
       DECL_SAVED_TREE (decl) = body;
       DECL_UNINLINABLE (decl) = 1;
 
diff --git a/gcc/libfuncs.h b/gcc/libfuncs.h
index 04a4dc2..4c7faa4 100644
--- a/gcc/libfuncs.h
+++ b/gcc/libfuncs.h
@@ -40,6 +40,9 @@  enum libfunc_index
   LTI_profile_function_entry,
   LTI_profile_function_exit,
 
+  LTI_profile_call_before,
+  LTI_profile_call_after,
+
   LTI_synchronize,
 
   LTI_gcov_flush,
@@ -98,6 +101,9 @@  extern struct target_libfuncs *this_target_libfuncs;
 #define profile_function_entry_libfunc	(libfunc_table[LTI_profile_function_entry])
 #define profile_function_exit_libfunc	(libfunc_table[LTI_profile_function_exit])
 
+#define profile_call_before_libfunc	(libfunc_table[LTI_profile_call_before])
+#define profile_call_after_libfunc	(libfunc_table[LTI_profile_call_after])
+
 #define synchronize_libfunc	(libfunc_table[LTI_synchronize])
 
 #define gcov_flush_libfunc	(libfunc_table[LTI_gcov_flush])
diff --git a/gcc/optabs.c b/gcc/optabs.c
index a3051ad..9b93900 100644
--- a/gcc/optabs.c
+++ b/gcc/optabs.c
@@ -6202,6 +6202,12 @@  init_optabs (void)
   profile_function_exit_libfunc
     = init_one_libfunc ("__cyg_profile_func_exit");
 
+  /* For call before/after instrumentation.  */
+  profile_call_before_libfunc
+    = init_one_libfunc ("__cyg_profile_call_before");
+  profile_call_after_libfunc
+    = init_one_libfunc ("__cyg_profile_call_after");
+
   gcov_flush_libfunc = init_one_libfunc ("__gcov_flush");
 
   /* Allow the target to add more libcalls or rename some, etc.  */
diff --git a/gcc/opts.c b/gcc/opts.c
index 6856c3c..14bb78d 100644
--- a/gcc/opts.c
+++ b/gcc/opts.c
@@ -1540,6 +1540,16 @@  common_handle_option (struct gcc_options *opts,
 	(&opts->x_flag_instrument_functions_exclude_files, arg);
       break;
 
+    case OPT_finstrument_calls_exclude_function_list_:
+      add_comma_separated_to_vector
+	(&opts->x_flag_instrument_calls_exclude_functions, arg);
+      break;
+
+    case OPT_finstrument_calls_exclude_file_list_:
+      add_comma_separated_to_vector
+	(&opts->x_flag_instrument_calls_exclude_files, arg);
+      break;
+
     case OPT_fmessage_length_:
       pp_set_line_maximum_length (dc->printer, value);
       diagnostic_set_caret_max_width (dc, value);
diff --git a/gcc/testsuite/g++.dg/other/instrument_calls-1.C b/gcc/testsuite/g++.dg/other/instrument_calls-1.C
new file mode 100644
index 0000000..e1bf365
--- /dev/null
+++ b/gcc/testsuite/g++.dg/other/instrument_calls-1.C
@@ -0,0 +1,14 @@ 
+/* { dg-do compile } */
+/* { dg-options "-finstrument-calls" } */
+
+class Base
+{
+public:
+	virtual void foo();
+};
+
+void fn_caller( Base* b ) { b->foo(); }
+
+/* { dg-final { scan-assembler "__cyg_profile_call_before" } } */
+/* { dg-final { scan-assembler "__cyg_profile_call_after" } } */
+
diff --git a/gcc/testsuite/g++.dg/other/instrument_calls-2.C b/gcc/testsuite/g++.dg/other/instrument_calls-2.C
new file mode 100644
index 0000000..02b113c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/other/instrument_calls-2.C
@@ -0,0 +1,20 @@ 
+/* { dg-do compile } */
+/* { dg-options "-finstrument-calls" } */
+
+#define CALL_MEMBER_FN(object,ptrToMember)  ((object).*(ptrToMember))
+
+class Base
+{
+public:
+	virtual void foo();
+	virtual void bar();
+	virtual void foobar(int i);
+	virtual void barfoo(int i);
+};
+
+typedef void (Base::*BaseMemFn)(int i);
+void fn_caller( Base& obj, BaseMemFn memfnptr ) { CALL_MEMBER_FN(obj, memfnptr)(42); }
+
+/* { dg-final { scan-assembler "__cyg_profile_call_before" } } */
+/* { dg-final { scan-assembler "__cyg_profile_call_after" } } */
+
diff --git a/gcc/testsuite/g++.dg/other/instrument_calls-3.C b/gcc/testsuite/g++.dg/other/instrument_calls-3.C
new file mode 100644
index 0000000..eccf7c1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/other/instrument_calls-3.C
@@ -0,0 +1,17 @@ 
+/* { dg-do compile } */
+/* { dg-options "-finstrument-calls" } */
+
+class Base
+{
+public:
+	int bar();
+};
+
+int fn_caller( Base& b )
+{
+   b.bar();
+}
+
+/* { dg-final { scan-assembler "__cyg_profile_call_before" } } */
+/* { dg-final { scan-assembler "__cyg_profile_call_after" } } */
+
diff --git a/gcc/testsuite/gcc.dg/instrument_calls-1.c b/gcc/testsuite/gcc.dg/instrument_calls-1.c
new file mode 100644
index 0000000..eac08f4
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/instrument_calls-1.c
@@ -0,0 +1,8 @@ 
+/* { dg-do compile } */
+/* { dg-options "-finstrument-calls" } */
+
+void fn () { }
+void fn_caller () { fn (); }
+
+/* { dg-final { scan-assembler "__cyg_profile_call_before" } } */
+/* { dg-final { scan-assembler "__cyg_profile_call_after" } } */
diff --git a/gcc/testsuite/gcc.dg/instrument_calls-2.c b/gcc/testsuite/gcc.dg/instrument_calls-2.c
new file mode 100644
index 0000000..b897d98
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/instrument_calls-2.c
@@ -0,0 +1,8 @@ 
+/* { dg-do compile } */
+/* { dg-options "-finstrument-calls -finstrument-calls-exclude-function-list=fn_caller" } */
+
+void fn () { }
+void fn_caller () { fn (); }
+
+/* { dg-final { scan-assembler-not "__cyg_profile_call_before" } } */
+/* { dg-final { scan-assembler-not "__cyg_profile_call_after" } } */
diff --git a/gcc/testsuite/gcc.dg/instrument_calls-3.c b/gcc/testsuite/gcc.dg/instrument_calls-3.c
new file mode 100644
index 0000000..875de8d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/instrument_calls-3.c
@@ -0,0 +1,8 @@ 
+/* { dg-do compile } */
+/* { dg-options "-finstrument-calls -finstrument-calls-exclude-file-list=instrument_calls-3" } */
+
+void fn () { }
+void fn_caller () { fn (); }
+
+/* { dg-final { scan-assembler-not "__cyg_profile_call_before" } } */
+/* { dg-final { scan-assembler-not "__cyg_profile_call_after" } } */
diff --git a/gcc/testsuite/gcc.dg/instrument_calls-4.c b/gcc/testsuite/gcc.dg/instrument_calls-4.c
new file mode 100644
index 0000000..e2f896f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/instrument_calls-4.c
@@ -0,0 +1,8 @@ 
+/* { dg-do compile } */
+/* { dg-options "-finstrument-calls" } */
+
+#include <stdio.h>
+void fn_caller () { puts (""); }
+
+/* { dg-final { scan-assembler "__cyg_profile_call_before" } } */
+/* { dg-final { scan-assembler "__cyg_profile_call_after" } } */
diff --git a/gcc/testsuite/gcc.dg/instrument_calls-5.c b/gcc/testsuite/gcc.dg/instrument_calls-5.c
new file mode 100644
index 0000000..84b078d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/instrument_calls-5.c
@@ -0,0 +1,11 @@ 
+/* { dg-do compile } */
+/* { dg-options "-finstrument-calls" } */
+
+int fn (int i) { }
+int fn_caller (int i)
+{
+    return fn (i);
+}
+
+/* { dg-final { scan-assembler "__cyg_profile_call_before" } } */
+/* { dg-final { scan-assembler "__cyg_profile_call_after" } } */
diff --git a/gcc/testsuite/gcc.dg/instrument_calls-6.c b/gcc/testsuite/gcc.dg/instrument_calls-6.c
new file mode 100644
index 0000000..8514cc8
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/instrument_calls-6.c
@@ -0,0 +1,11 @@ 
+/* { dg-do compile } */
+/* { dg-options "-finstrument-calls" } */
+
+void fn_caller ()
+{
+    void fn () { }
+    fn ();
+}
+
+/* { dg-final { scan-assembler "__cyg_profile_call_before" } } */
+/* { dg-final { scan-assembler "__cyg_profile_call_after" } } */
diff --git a/gcc/testsuite/gcc.dg/instrument_calls-7.c b/gcc/testsuite/gcc.dg/instrument_calls-7.c
new file mode 100644
index 0000000..86bcc52
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/instrument_calls-7.c
@@ -0,0 +1,13 @@ 
+/* { dg-do compile } */
+/* { dg-options "-finstrument-calls" } */
+
+void *p_fn[3];
+int fn_caller (int i, char *fmt, ...)
+{
+    void *arg = __builtin_apply_args();
+    void *ret = __builtin_apply(p_fn[i], arg, 0xff);
+    __builtin_return(ret);
+}
+
+/* { dg-final { scan-assembler "__cyg_profile_call_before" } } */
+/* { dg-final { scan-assembler "__cyg_profile_call_after" } } */
diff --git a/gcc/testsuite/gcc.dg/instrument_calls-8.c b/gcc/testsuite/gcc.dg/instrument_calls-8.c
new file mode 100644
index 0000000..e38b44c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/instrument_calls-8.c
@@ -0,0 +1,7 @@ 
+/* { dg-do compile } */
+/* { dg-options "-finstrument-calls" } */
+
+void fn_caller ( void (*p_fn)() ) { p_fn (); }
+
+/* { dg-final { scan-assembler "__cyg_profile_call_before" } } */
+/* { dg-final { scan-assembler "__cyg_profile_call_after" } } */
diff --git a/gcc/testsuite/gcc.dg/instrument_calls-9.c b/gcc/testsuite/gcc.dg/instrument_calls-9.c
new file mode 100644
index 0000000..88cf700
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/instrument_calls-9.c
@@ -0,0 +1,12 @@ 
+/* { dg-do compile } */
+/* { dg-options "-finstrument-calls" } */
+
+__attribute__((no_instrument_function)) void fn ();
+
+void fn_caller ()
+{
+    fn ();
+}
+
+/* { dg-final { scan-assembler-not "__cyg_profile_call_before" } } */
+/* { dg-final { scan-assembler-not "__cyg_profile_call_after" } } */
diff --git a/gcc/tree-streamer-in.c b/gcc/tree-streamer-in.c
index 00f78a1..967b1e7 100644
--- a/gcc/tree-streamer-in.c
+++ b/gcc/tree-streamer-in.c
@@ -305,6 +305,8 @@  unpack_ts_function_decl_value_fields (struct bitpack_d *bp, tree expr)
   DECL_NO_INLINE_WARNING_P (expr) = (unsigned) bp_unpack_value (bp, 1);
   DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (expr)
     			= (unsigned) bp_unpack_value (bp, 1);
+  DECL_NO_INSTRUMENT_CALLS_BEFORE_AFTER (expr)
+			= (unsigned) bp_unpack_value (bp, 1);
   DECL_NO_LIMIT_STACK (expr) = (unsigned) bp_unpack_value (bp, 1);
   DECL_DISREGARD_INLINE_LIMITS (expr) = (unsigned) bp_unpack_value (bp, 1);
   DECL_PURE_P (expr) = (unsigned) bp_unpack_value (bp, 1);
diff --git a/gcc/tree-streamer-out.c b/gcc/tree-streamer-out.c
index fa50ef5..f71fea6 100644
--- a/gcc/tree-streamer-out.c
+++ b/gcc/tree-streamer-out.c
@@ -271,6 +271,7 @@  pack_ts_function_decl_value_fields (struct bitpack_d *bp, tree expr)
   bp_pack_value (bp, DECL_STATIC_CHAIN (expr), 1);
   bp_pack_value (bp, DECL_NO_INLINE_WARNING_P (expr), 1);
   bp_pack_value (bp, DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (expr), 1);
+  bp_pack_value (bp, DECL_NO_INSTRUMENT_CALLS_BEFORE_AFTER (expr), 1);
   bp_pack_value (bp, DECL_NO_LIMIT_STACK (expr), 1);
   bp_pack_value (bp, DECL_DISREGARD_INLINE_LIMITS (expr), 1);
   bp_pack_value (bp, DECL_PURE_P (expr), 1);
diff --git a/gcc/tree.h b/gcc/tree.h
index b444517..456e41e 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -3375,6 +3375,11 @@  struct GTY(())
 #define DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT(NODE) \
   (FUNCTION_DECL_CHECK (NODE)->function_decl.no_instrument_function_entry_exit)
 
+/* Used in FUNCTION_DECLs to indicate that function calls in that function should
+   be instrumented with calls to support routines before and after each function call.  */
+#define DECL_NO_INSTRUMENT_CALLS_BEFORE_AFTER(NODE) \
+  (FUNCTION_DECL_CHECK (NODE)->function_decl.no_instrument_calls_before_after)
+
 /* Used in FUNCTION_DECLs to indicate that limit-stack-* should be
    disabled in this function.  */
 #define DECL_NO_LIMIT_STACK(NODE) \
@@ -3512,6 +3517,7 @@  struct GTY(()) tree_function_decl {
   unsigned no_inline_warning_flag : 1;
 
   unsigned no_instrument_function_entry_exit : 1;
+  unsigned no_instrument_calls_before_after : 1;
   unsigned no_limit_stack : 1;
   unsigned disregard_inline_limits : 1;
   unsigned pure_flag : 1;
@@ -3519,7 +3525,7 @@  struct GTY(()) tree_function_decl {
   unsigned has_debug_args_flag : 1;
   unsigned tm_clone_flag : 1;
   unsigned versioned_function : 1;
-  /* No bits left.  */
+  /* -1 bit left */
 };
 
 /* The source language of the translation-unit.  */