From: Stan Shebs Date: Tue, 27 Sep 2011 13:09:37 +0000 (+0000) Subject: Add return address collection for tracepoints. X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=6710bf39b7c037de481a4b351fb69cec6b48b138;p=binutils-gdb.git Add return address collection for tracepoints. * tracepoint.c (encode_actions_1): Add case for $_ret. (validate_actionline): Check for $_ret. (trace_dump_actions): Ditto. * ax-gdb.h (gen_trace_for_return_address): Declare. * ax-gdb.c: Include arch-utils.h. (gen_trace_for_return_address): New function. (agent_command): Add return address special case. * amd64-tdep.c: Include ax.h and ax-gdb.h. (amd64_gen_return_address): New function. (amd64_init_abi): Call it. * i386-tdep.c: Include ax.h and ax-gdb.h. (i386_gen_return_address): New function. (i386_init_abi): Call it. * arch-utils.h (default_gen_return_address): Declare. * arch-utils.c (default_gen_return_address): New function. * gdbarch.sh (gen_return_address): New method. * gdbarch.h, gdbarch.c: Regenerate. * gdb.texinfo (Tracepoint Action Lists): Document $_ret. * gdb.trace/collection.exp: Test collection of $_ret. --- diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 0838f02e274..5c59efcef03 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,24 @@ +2011-09-27 Stan Shebs + + Add return address collection for tracepoints. + * tracepoint.c (encode_actions_1): Add case for $_ret. + (validate_actionline): Check for $_ret. + (trace_dump_actions): Ditto. + * ax-gdb.h (gen_trace_for_return_address): Declare. + * ax-gdb.c: Include arch-utils.h. + (gen_trace_for_return_address): New function. + (agent_command): Add return address special case. + * amd64-tdep.c: Include ax.h and ax-gdb.h. + (amd64_gen_return_address): New function. + (amd64_init_abi): Call it. + * i386-tdep.c: Include ax.h and ax-gdb.h. + (i386_gen_return_address): New function. + (i386_init_abi): Call it. + * arch-utils.h (default_gen_return_address): Declare. + * arch-utils.c (default_gen_return_address): New function. + * gdbarch.sh (gen_return_address): New method. + * gdbarch.h, gdbarch.c: Regenerate. + 2011-09-23 Joseph Myers PR gdb/13079 diff --git a/gdb/amd64-tdep.c b/gdb/amd64-tdep.c index 55bedab07ac..b85f255d5bb 100644 --- a/gdb/amd64-tdep.c +++ b/gdb/amd64-tdep.c @@ -45,6 +45,9 @@ #include "features/i386/amd64.c" #include "features/i386/amd64-avx.c" +#include "ax.h" +#include "ax-gdb.h" + /* Note that the AMD64 architecture was previously known as x86-64. The latter is (forever) engraved into the canonical system name as returned by config.guess, and used as the name for the AMD64 port @@ -2165,6 +2168,22 @@ static const struct frame_unwind amd64_frame_unwind = default_frame_sniffer }; +/* Generate a bytecode expression to get the value of the saved PC. */ + +static void +amd64_gen_return_address (struct gdbarch *gdbarch, + struct agent_expr *ax, struct axs_value *value, + CORE_ADDR scope) +{ + /* The following sequence assumes the traditional use of the base + register. */ + ax_reg (ax, AMD64_RBP_REGNUM); + ax_const_l (ax, 8); + ax_simple (ax, aop_add); + value->type = register_type (gdbarch, AMD64_RIP_REGNUM); + value->kind = axs_lvalue_memory; +} + /* Signal trampolines. */ @@ -2669,6 +2688,8 @@ amd64_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) set_gdbarch_get_longjmp_target (gdbarch, amd64_get_longjmp_target); set_gdbarch_relocate_instruction (gdbarch, amd64_relocate_instruction); + + set_gdbarch_gen_return_address (gdbarch, amd64_gen_return_address); } /* Provide a prototype to silence -Wmissing-prototypes. */ diff --git a/gdb/arch-utils.c b/gdb/arch-utils.c index db79b9ab3d2..2cedb38f92c 100644 --- a/gdb/arch-utils.c +++ b/gdb/arch-utils.c @@ -786,6 +786,14 @@ default_remote_breakpoint_from_pc (struct gdbarch *gdbarch, CORE_ADDR *pcptr, gdbarch_breakpoint_from_pc (gdbarch, pcptr, kindptr); } +void +default_gen_return_address (struct gdbarch *gdbarch, + struct agent_expr *ax, struct axs_value *value, + CORE_ADDR scope) +{ + error (_("This architecture has no method to collect a return address.")); +} + /* */ /* -Wmissing-prototypes */ diff --git a/gdb/arch-utils.h b/gdb/arch-utils.h index 5d055358285..f5eb1a75470 100644 --- a/gdb/arch-utils.h +++ b/gdb/arch-utils.h @@ -164,6 +164,11 @@ extern int default_fast_tracepoint_valid_at (struct gdbarch *gdbarch, extern void default_remote_breakpoint_from_pc (struct gdbarch *, CORE_ADDR *pcptr, int *kindptr); +extern void default_gen_return_address (struct gdbarch *gdbarch, + struct agent_expr *ax, + struct axs_value *value, + CORE_ADDR scope); + extern const char *default_auto_charset (void); extern const char *default_auto_wide_charset (void); diff --git a/gdb/ax-gdb.c b/gdb/ax-gdb.c index 5258167df38..bd8800c60c5 100644 --- a/gdb/ax-gdb.c +++ b/gdb/ax-gdb.c @@ -40,6 +40,7 @@ #include "breakpoint.h" #include "tracepoint.h" #include "cp-support.h" +#include "arch-utils.h" /* To make sense of this file, you should read doc/agentexpr.texi. Then look at the types and enums in ax-gdb.h. For the code itself, @@ -2444,6 +2445,32 @@ gen_eval_for_expr (CORE_ADDR scope, struct expression *expr) return ax; } +struct agent_expr * +gen_trace_for_return_address (CORE_ADDR scope, struct gdbarch *gdbarch) +{ + struct cleanup *old_chain = 0; + struct agent_expr *ax = new_agent_expr (gdbarch, scope); + struct axs_value value; + + old_chain = make_cleanup_free_agent_expr (ax); + + trace_kludge = 1; + + gdbarch_gen_return_address (gdbarch, ax, &value, scope); + + /* Make sure we record the final object, and get rid of it. */ + gen_traced_pop (gdbarch, ax, &value); + + /* Oh, and terminate. */ + ax_simple (ax, aop_end); + + /* We have successfully built the agent expr, so cancel the cleanup + request. If we add more cleanups that we always want done, this + will have to get more complicated. */ + discard_cleanups (old_chain); + return ax; +} + static void agent_command (char *exp, int from_tty) { @@ -2462,10 +2489,22 @@ agent_command (char *exp, int from_tty) if (exp == 0) error_no_arg (_("expression to translate")); - expr = parse_expression (exp); - old_chain = make_cleanup (free_current_contents, &expr); - agent = gen_trace_for_expr (get_frame_pc (fi), expr); - make_cleanup_free_agent_expr (agent); + /* Recognize the return address collection directive specially. Note + that it is not really an expression of any sort. */ + if (strcmp (exp, "$_ret") == 0) + { + agent = gen_trace_for_return_address (get_frame_pc (fi), + get_current_arch ()); + old_chain = make_cleanup_free_agent_expr (agent); + } + else + { + expr = parse_expression (exp); + old_chain = make_cleanup (free_current_contents, &expr); + agent = gen_trace_for_expr (get_frame_pc (fi), expr); + make_cleanup_free_agent_expr (agent); + } + ax_reqs (agent); ax_print (gdb_stdout, agent); diff --git a/gdb/ax-gdb.h b/gdb/ax-gdb.h index a2367a67e39..a25d9947541 100644 --- a/gdb/ax-gdb.h +++ b/gdb/ax-gdb.h @@ -106,6 +106,9 @@ extern struct agent_expr *gen_trace_for_expr (CORE_ADDR, struct expression *); extern struct agent_expr *gen_trace_for_var (CORE_ADDR, struct gdbarch *, struct symbol *); +extern struct agent_expr *gen_trace_for_return_address (CORE_ADDR, + struct gdbarch *); + extern struct agent_expr *gen_eval_for_expr (CORE_ADDR, struct expression *); extern int trace_kludge; diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog index 7d1e0d40c47..298319654a2 100644 --- a/gdb/doc/ChangeLog +++ b/gdb/doc/ChangeLog @@ -1,3 +1,7 @@ +2011-09-27 Stan Shebs + + * gdb.texinfo (Tracepoint Action Lists): Document $_ret. + 2011-09-16 Hui Zhu * gdb.texinfo (Tracepoint Restrictions): Change *$esp@300 diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 051377d130b..c8bb0065cbf 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -10284,6 +10284,10 @@ Collect all function arguments. @item $locals Collect all local variables. +@item $_ret +Collect the return address. This is helpful if you want to see more +of a backtrace. + @item $_sdata @vindex $_sdata@r{, collect} Collect static tracepoint marker specific data. Only available for diff --git a/gdb/gdbarch.c b/gdb/gdbarch.c index 600cce61a3e..2b892b65e4f 100644 --- a/gdb/gdbarch.c +++ b/gdb/gdbarch.c @@ -270,6 +270,7 @@ struct gdbarch gdbarch_auto_wide_charset_ftype *auto_wide_charset; const char * solib_symbols_extension; int has_dos_based_file_system; + gdbarch_gen_return_address_ftype *gen_return_address; }; @@ -423,6 +424,7 @@ struct gdbarch startup_gdbarch = default_auto_wide_charset, /* auto_wide_charset */ 0, /* solib_symbols_extension */ 0, /* has_dos_based_file_system */ + default_gen_return_address, /* gen_return_address */ /* startup_gdbarch() */ }; @@ -513,6 +515,7 @@ gdbarch_alloc (const struct gdbarch_info *info, gdbarch->fast_tracepoint_valid_at = default_fast_tracepoint_valid_at; gdbarch->auto_charset = default_auto_charset; gdbarch->auto_wide_charset = default_auto_wide_charset; + gdbarch->gen_return_address = default_gen_return_address; /* gdbarch_alloc() */ return gdbarch; @@ -707,6 +710,7 @@ verify_gdbarch (struct gdbarch *gdbarch) /* Skip verify of auto_charset, invalid_p == 0 */ /* Skip verify of auto_wide_charset, invalid_p == 0 */ /* Skip verify of has_dos_based_file_system, invalid_p == 0 */ + /* Skip verify of gen_return_address, invalid_p == 0 */ buf = ui_file_xstrdup (log, &length); make_cleanup (xfree, buf); if (length > 0) @@ -946,6 +950,9 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file) fprintf_unfiltered (file, "gdbarch_dump: gcore_bfd_target = %s\n", gdbarch->gcore_bfd_target); + fprintf_unfiltered (file, + "gdbarch_dump: gen_return_address = <%s>\n", + host_address_to_string (gdbarch->gen_return_address)); fprintf_unfiltered (file, "gdbarch_dump: gdbarch_get_longjmp_target_p() = %d\n", gdbarch_get_longjmp_target_p (gdbarch)); @@ -3863,6 +3870,23 @@ set_gdbarch_has_dos_based_file_system (struct gdbarch *gdbarch, gdbarch->has_dos_based_file_system = has_dos_based_file_system; } +void +gdbarch_gen_return_address (struct gdbarch *gdbarch, struct agent_expr *ax, struct axs_value *value, CORE_ADDR scope) +{ + gdb_assert (gdbarch != NULL); + gdb_assert (gdbarch->gen_return_address != NULL); + if (gdbarch_debug >= 2) + fprintf_unfiltered (gdb_stdlog, "gdbarch_gen_return_address called\n"); + gdbarch->gen_return_address (gdbarch, ax, value, scope); +} + +void +set_gdbarch_gen_return_address (struct gdbarch *gdbarch, + gdbarch_gen_return_address_ftype gen_return_address) +{ + gdbarch->gen_return_address = gen_return_address; +} + /* Keep a registry of per-architecture data-pointers required by GDB modules. */ diff --git a/gdb/gdbarch.h b/gdb/gdbarch.h index 76195816855..01173220f09 100644 --- a/gdb/gdbarch.h +++ b/gdb/gdbarch.h @@ -54,6 +54,7 @@ struct displaced_step_closure; struct core_regset_section; struct syscall; struct agent_expr; +struct axs_value; /* The architecture associated with the connection to the target. @@ -1014,6 +1015,16 @@ extern void set_gdbarch_solib_symbols_extension (struct gdbarch *gdbarch, const extern int gdbarch_has_dos_based_file_system (struct gdbarch *gdbarch); extern void set_gdbarch_has_dos_based_file_system (struct gdbarch *gdbarch, int has_dos_based_file_system); +/* Generate bytecodes to collect the return address in a frame. + Since the bytecodes run on the target, possibly with GDB not even + connected, the full unwinding machinery is not available, and + typically this function will issue bytecodes for one or more likely + places that the return address may be found. */ + +typedef void (gdbarch_gen_return_address_ftype) (struct gdbarch *gdbarch, struct agent_expr *ax, struct axs_value *value, CORE_ADDR scope); +extern void gdbarch_gen_return_address (struct gdbarch *gdbarch, struct agent_expr *ax, struct axs_value *value, CORE_ADDR scope); +extern void set_gdbarch_gen_return_address (struct gdbarch *gdbarch, gdbarch_gen_return_address_ftype *gen_return_address); + /* Definition for an unknown syscall, used basically in error-cases. */ #define UNKNOWN_SYSCALL (-1) diff --git a/gdb/gdbarch.sh b/gdb/gdbarch.sh index 61094fb0400..dcf0343300f 100755 --- a/gdb/gdbarch.sh +++ b/gdb/gdbarch.sh @@ -820,6 +820,14 @@ v:const char *:solib_symbols_extension:::::::pstring (gdbarch->solib_symbols_ext # is, absolute paths include a drive name, and the backslash is # considered a directory separator. v:int:has_dos_based_file_system:::0:0::0 + +# Generate bytecodes to collect the return address in a frame. +# Since the bytecodes run on the target, possibly with GDB not even +# connected, the full unwinding machinery is not available, and +# typically this function will issue bytecodes for one or more likely +# places that the return address may be found. +m:void:gen_return_address:struct agent_expr *ax, struct axs_value *value, CORE_ADDR scope:ax, value, scope::default_gen_return_address::0 + EOF } @@ -934,6 +942,7 @@ struct displaced_step_closure; struct core_regset_section; struct syscall; struct agent_expr; +struct axs_value; /* The architecture associated with the connection to the target. diff --git a/gdb/i386-tdep.c b/gdb/i386-tdep.c index 98dfd02278c..179bc459ecb 100644 --- a/gdb/i386-tdep.c +++ b/gdb/i386-tdep.c @@ -60,6 +60,9 @@ #include "features/i386/i386-avx.c" #include "features/i386/i386-mmx.c" +#include "ax.h" +#include "ax-gdb.h" + /* Register names. */ static const char *i386_register_names[] = @@ -2074,6 +2077,22 @@ static const struct frame_unwind i386_stack_tramp_frame_unwind = i386_stack_tramp_frame_sniffer }; +/* Generate a bytecode expression to get the value of the saved PC. */ + +static void +i386_gen_return_address (struct gdbarch *gdbarch, + struct agent_expr *ax, struct axs_value *value, + CORE_ADDR scope) +{ + /* The following sequence assumes the traditional use of the base + register. */ + ax_reg (ax, I386_EBP_REGNUM); + ax_const_l (ax, 4); + ax_simple (ax, aop_add); + value->type = register_type (gdbarch, I386_EIP_REGNUM); + value->kind = axs_lvalue_memory; +} + /* Signal trampolines. */ @@ -7410,6 +7429,8 @@ i386_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) set_gdbarch_relocate_instruction (gdbarch, i386_relocate_instruction); + set_gdbarch_gen_return_address (gdbarch, i386_gen_return_address); + /* Hook in ABI-specific overrides, if they have been registered. */ info.tdep_info = (void *) tdesc_data; gdbarch_init_osabi (info, gdbarch); diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index d426e4230f2..c2cacb0a2a9 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,7 @@ +2011-09-27 Stan Shebs + + * gdb.trace/collection.exp: Test collection of $_ret. + 2011-09-22 Andreas Tobler * lib/gdb.exp (gdb_compile): Set rpath and remove -ldl from the diff --git a/gdb/testsuite/gdb.trace/collection.exp b/gdb/testsuite/gdb.trace/collection.exp index 4d57ad49b15..6b731841f8c 100644 --- a/gdb/testsuite/gdb.trace/collection.exp +++ b/gdb/testsuite/gdb.trace/collection.exp @@ -588,6 +588,37 @@ proc gdb_collect_global_in_pieces_test { } { "collect global in pieces: cease trace debugging" } +proc gdb_collect_return_test { } { + + prepare_for_trace_test + + # We'll simply re-use the args_test_function for this test + gdb_test "trace args_test_func" \ + "Tracepoint \[0-9\]+ at .*" \ + "collect \$_ret: set tracepoint" + gdb_trace_setactions "collect \$_ret: define actions" \ + "" \ + "collect \$_ret" "^$" + + # Begin the test. + run_trace_experiment \$_ret args_test_func + + # Since we can't guarantee that $_ret will give us the caller, + # pass either way, but giving different messages. + gdb_test_multiple "backtrace" "" { + -re ".*#1 .* in main .*" { + pass "collect \$_ret: backtrace lists main" + } + -re ".*#1 .* in ?? .*" { + pass "collect \$_ret: backtrace not listing main" + } + } + + gdb_test "tfind none" \ + "#0 end .*" \ + "collect \$_ret: cease trace debugging" +} + proc gdb_trace_collection_test {} { global fpreg global spreg @@ -696,6 +727,7 @@ proc gdb_trace_collection_test {} { gdb_collect_expression_test globals_test_func \ "globalarr\[\(l6, l7\)\]" "7" "a\[\(b, c\)\]" + gdb_collect_return_test } clean_restart $executable diff --git a/gdb/tracepoint.c b/gdb/tracepoint.c index fc9a17a59cc..d5c9a6d4764 100644 --- a/gdb/tracepoint.c +++ b/gdb/tracepoint.c @@ -667,6 +667,7 @@ validate_actionline (char **line, struct breakpoint *b) if (0 == strncasecmp ("reg", p + 1, 3) || 0 == strncasecmp ("arg", p + 1, 3) || 0 == strncasecmp ("loc", p + 1, 3) + || 0 == strncasecmp ("_ret", p + 1, 4) || 0 == strncasecmp ("_sdata", p + 1, 6)) { p = strchr (p, ','); @@ -1344,6 +1345,43 @@ encode_actions_1 (struct command_line *action, 'L'); action_exp = strchr (action_exp, ','); /* more? */ } + else if (0 == strncasecmp ("$_ret", action_exp, 5)) + { + struct cleanup *old_chain1 = NULL; + + aexpr = gen_trace_for_return_address (tloc->address, + t->gdbarch); + + old_chain1 = make_cleanup_free_agent_expr (aexpr); + + ax_reqs (aexpr); + report_agent_reqs_errors (aexpr); + + discard_cleanups (old_chain1); + add_aexpr (collect, aexpr); + + /* take care of the registers */ + if (aexpr->reg_mask_len > 0) + { + int ndx1, ndx2; + + for (ndx1 = 0; ndx1 < aexpr->reg_mask_len; ndx1++) + { + QUIT; /* allow user to bail out with ^C */ + if (aexpr->reg_mask[ndx1] != 0) + { + /* assume chars have 8 bits */ + for (ndx2 = 0; ndx2 < 8; ndx2++) + if (aexpr->reg_mask[ndx1] & (1 << ndx2)) + /* it's used -- record it */ + add_register (collect, + ndx1 * 8 + ndx2); + } + } + } + + action_exp = strchr (action_exp, ','); /* more? */ + } else if (0 == strncasecmp ("$_sdata", action_exp, 7)) { add_static_trace_data (collect); @@ -2555,6 +2593,8 @@ trace_dump_actions (struct command_line *action, if (0 == strncasecmp (action_exp, "$reg", 4)) registers_info (NULL, from_tty); + else if (0 == strncasecmp (action_exp, "$_ret", 5)) + ; else if (0 == strncasecmp (action_exp, "$loc", 4)) locals_info (NULL, from_tty); else if (0 == strncasecmp (action_exp, "$arg", 4))