diff options
author | Sergio Durigan Junior <sergiodj@redhat.com> | 2012-04-27 20:47:57 +0000 |
---|---|---|
committer | Sergio Durigan Junior <sergiodj@redhat.com> | 2012-04-27 20:47:57 +0000 |
commit | 55aa24fb2eb147288fec359a99e960f7136336e8 (patch) | |
tree | d9650d8e5cd7bdbe4f19fb124a2190c8d321db63 | |
parent | 2755f698e14dabda211bc592a414ee21e0421a2d (diff) | |
download | gdb-55aa24fb2eb147288fec359a99e960f7136336e8.zip gdb-55aa24fb2eb147288fec359a99e960f7136336e8.tar.gz gdb-55aa24fb2eb147288fec359a99e960f7136336e8.tar.bz2 |
2012-04-27 Sergio Durigan Junior <sergiodj@redhat.com>
Tom Tromey <tromey@redhat.com>
Jan Kratochvil <jan.kratochvil@redhat.com>
* Makefile.in (SFILES): Add `probe' and `stap-probe'.
(COMMON_OBS): Likewise.
(HFILES_NO_SRCDIR): Add `probe'.
* NEWS: Mention support for static and SystemTap probes.
* amd64-tdep.c (amd64_init_abi): Initializing proper fields used by
SystemTap probes' arguments parser.
* arm-linux-tdep.c: Including headers needed to perform the parsing
of SystemTap probes' arguments.
(arm_stap_is_single_operand): New function.
(arm_stap_parse_special_token): Likewise.
(arm_linux_init_abi): Initializing proper fields used by SystemTap
probes' arguments parser.
* ax-gdb.c (require_rvalue): Removing static declaration.
(gen_expr): Likewise.
* ax-gdb.h (gen_expr): Declaring function.
(require_rvalue): Likewise.
* breakpoint.c: Include `gdb_regex.h' and `probe.h'.
(bkpt_probe_breakpoint_ops): New variable.
(momentary_breakpoint_from_master): Set the `probe' value.
(add_location_to_breakpoint): Likewise.
(break_command_1): Using proper breakpoint_ops according to the
argument passed by the user in the command line.
(bkpt_probe_insert_location): New function.
(bkpt_probe_remove_location): Likewise.
(bkpt_probe_create_sals_from_address): Likewise.
(bkpt_probe_decode_linespec): Likewise.
(tracepoint_probe_create_sals_from_address): Likewise.
(tracepoint_probe_decode_linespec): Likewise.
(tracepoint_probe_breakpoint_ops): New variable.
(trace_command): Using proper breakpoint_ops according to the
argument passed by the user in the command line.
(initialize_breakpoint_ops): Initializing breakpoint_ops for
static probes on breakpoints and tracepoints.
* breakpoint.h (struct bp_location) <probe>: New field.
* cli-utils.c (skip_spaces_const): New function.
(extract_arg): Likewise.
* cli-utils.h (skip_spaces_const): Likewise.
(extract_arg): Likewise.
* coffread.c (coff_sym_fns): Add `sym_probe_fns' value.
* configure.ac: Append `stap-probe.o' to be generated when ELF
support is present.
* configure: Regenerate.
* dbxread.c (aout_sym_fns): Add `sym_probe_fns' value.
* elfread.c: Include `probe.h' and `arch-utils.h'.
(probe_key): New variable.
(elf_get_probes): New function.
(elf_get_probe_argument_count): Likewise.
(elf_evaluate_probe_argument): Likewise.
(elf_compile_to_ax): Likewise.
(elf_symfile_relocate_probe): Likewise.
(stap_probe_key_free): Likewise.
(elf_probe_fns): New variable.
(elf_sym_fns): Add `sym_probe_fns' value.
(elf_sym_fns_lazy_psyms): Likewise.
(elf_sym_fns_gdb_index): Likewise.
(_initialize_elfread): Initialize objfile cache for static
probes.
* gdb_vecs.h (struct probe): New forward declaration.
(probe_p): New VEC declaration.
* gdbarch.c: Regenerate.
* gdbarch.h: Regenerate.
* gdbarch.sh (stap_integer_prefix): New variable.
(stap_integer_suffix): Likewise.
(stap_register_prefix): Likewise.
(stap_register_suffix): Likewise.
(stap_register_indirection_prefix): Likewise.
(stap_register_indirection_suffix): Likewise.
(stap_gdb_register_prefix): Likewise.
(stap_gdb_register_suffix): Likewise.
(stap_is_single_operand): New function.
(stap_parse_special_token): Likewise.
(struct stap_parse_info): Forward declaration.
* i386-tdep.c: Including headers needed to perform the parsing
of SystemTap probes' arguments.
(i386_stap_is_single_operand): New function.
(i386_stap_parse_special_token): Likewise.
(i386_elf_init_abi): Initializing proper fields used by SystemTap
probes' arguments parser.
* i386-tdep.h (i386_stap_is_single_operand): New function.
(i386_stap_parse_special_token): Likewise.
* machoread.c (macho_sym_fns): Add `sym_probe_fns' value.
* mipsread.c (ecoff_sym_fns): Likewise.
* objfiles.c (objfile_relocate1): Support relocation for static
probes.
* parse.c (prefixify_expression): Remove static declaration.
(initialize_expout): Likewise.
(reallocate_expout): Likewise.
* parser-defs.h (initialize_expout): Declare function.
(reallocate_expout): Likewise.
(prefixify_expression): Likewise.
* ppc-linux-tdep.c: Including headers needed to perform the parsing
of SystemTap probes' arguments.
(ppc_stap_is_single_operand): New function.
(ppc_stap_parse_special_token): Likewise.
(ppc_linux_init_abi): Initializing proper fields used by SystemTap
probes' arguments parser.
* probe.c: New file, for generic statically defined probe support.
* probe.h: Likewise.
* s390-tdep.c: Including headers needed to perform the parsing of
SystemTap probes' arguments.
(s390_stap_is_single_operand): New function.
(s390_gdbarch_init): Initializing proper fields used by SystemTap
probes' arguments parser.
* somread.c (som_sym_fns): Add `sym_probe_fns' value.
* stap-probe.c: New file, for SystemTap probe support.
* stap-probe.h: Likewise.
* symfile.h: Include `gdb_vecs.h'.
(struct sym_probe_fns): New struct.
(struct sym_fns) <sym_probe_fns>: New field.
* symtab.c (init_sal): Initialize `probe' field.
* symtab.h (struct probe): Forward declaration.
(struct symtab_and_line) <probe>: New field.
* tracepoint.c (start_tracing): Adjust semaphore on breakpoints
locations.
(stop_tracing): Likewise.
* xcoffread.c (xcoff_sym_fns): Add `sym_probe_fns' value.
39 files changed, 4254 insertions, 35 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 22883eb..b223695 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,5 +1,126 @@ 2012-04-27 Sergio Durigan Junior <sergiodj@redhat.com> Tom Tromey <tromey@redhat.com> + Jan Kratochvil <jan.kratochvil@redhat.com> + + * Makefile.in (SFILES): Add `probe' and `stap-probe'. + (COMMON_OBS): Likewise. + (HFILES_NO_SRCDIR): Add `probe'. + * NEWS: Mention support for static and SystemTap probes. + * amd64-tdep.c (amd64_init_abi): Initializing proper fields used by + SystemTap probes' arguments parser. + * arm-linux-tdep.c: Including headers needed to perform the parsing + of SystemTap probes' arguments. + (arm_stap_is_single_operand): New function. + (arm_stap_parse_special_token): Likewise. + (arm_linux_init_abi): Initializing proper fields used by SystemTap + probes' arguments parser. + * ax-gdb.c (require_rvalue): Removing static declaration. + (gen_expr): Likewise. + * ax-gdb.h (gen_expr): Declaring function. + (require_rvalue): Likewise. + * breakpoint.c: Include `gdb_regex.h' and `probe.h'. + (bkpt_probe_breakpoint_ops): New variable. + (momentary_breakpoint_from_master): Set the `probe' value. + (add_location_to_breakpoint): Likewise. + (break_command_1): Using proper breakpoint_ops according to the + argument passed by the user in the command line. + (bkpt_probe_insert_location): New function. + (bkpt_probe_remove_location): Likewise. + (bkpt_probe_create_sals_from_address): Likewise. + (bkpt_probe_decode_linespec): Likewise. + (tracepoint_probe_create_sals_from_address): Likewise. + (tracepoint_probe_decode_linespec): Likewise. + (tracepoint_probe_breakpoint_ops): New variable. + (trace_command): Using proper breakpoint_ops according to the + argument passed by the user in the command line. + (initialize_breakpoint_ops): Initializing breakpoint_ops for + static probes on breakpoints and tracepoints. + * breakpoint.h (struct bp_location) <probe>: New field. + * cli-utils.c (skip_spaces_const): New function. + (extract_arg): Likewise. + * cli-utils.h (skip_spaces_const): Likewise. + (extract_arg): Likewise. + * coffread.c (coff_sym_fns): Add `sym_probe_fns' value. + * configure.ac: Append `stap-probe.o' to be generated when ELF + support is present. + * configure: Regenerate. + * dbxread.c (aout_sym_fns): Add `sym_probe_fns' value. + * elfread.c: Include `probe.h' and `arch-utils.h'. + (probe_key): New variable. + (elf_get_probes): New function. + (elf_get_probe_argument_count): Likewise. + (elf_evaluate_probe_argument): Likewise. + (elf_compile_to_ax): Likewise. + (elf_symfile_relocate_probe): Likewise. + (stap_probe_key_free): Likewise. + (elf_probe_fns): New variable. + (elf_sym_fns): Add `sym_probe_fns' value. + (elf_sym_fns_lazy_psyms): Likewise. + (elf_sym_fns_gdb_index): Likewise. + (_initialize_elfread): Initialize objfile cache for static + probes. + * gdb_vecs.h (struct probe): New forward declaration. + (probe_p): New VEC declaration. + * gdbarch.c: Regenerate. + * gdbarch.h: Regenerate. + * gdbarch.sh (stap_integer_prefix): New variable. + (stap_integer_suffix): Likewise. + (stap_register_prefix): Likewise. + (stap_register_suffix): Likewise. + (stap_register_indirection_prefix): Likewise. + (stap_register_indirection_suffix): Likewise. + (stap_gdb_register_prefix): Likewise. + (stap_gdb_register_suffix): Likewise. + (stap_is_single_operand): New function. + (stap_parse_special_token): Likewise. + (struct stap_parse_info): Forward declaration. + * i386-tdep.c: Including headers needed to perform the parsing + of SystemTap probes' arguments. + (i386_stap_is_single_operand): New function. + (i386_stap_parse_special_token): Likewise. + (i386_elf_init_abi): Initializing proper fields used by SystemTap + probes' arguments parser. + * i386-tdep.h (i386_stap_is_single_operand): New function. + (i386_stap_parse_special_token): Likewise. + * machoread.c (macho_sym_fns): Add `sym_probe_fns' value. + * mipsread.c (ecoff_sym_fns): Likewise. + * objfiles.c (objfile_relocate1): Support relocation for static + probes. + * parse.c (prefixify_expression): Remove static declaration. + (initialize_expout): Likewise. + (reallocate_expout): Likewise. + * parser-defs.h (initialize_expout): Declare function. + (reallocate_expout): Likewise. + (prefixify_expression): Likewise. + * ppc-linux-tdep.c: Including headers needed to perform the parsing + of SystemTap probes' arguments. + (ppc_stap_is_single_operand): New function. + (ppc_stap_parse_special_token): Likewise. + (ppc_linux_init_abi): Initializing proper fields used by SystemTap + probes' arguments parser. + * probe.c: New file, for generic statically defined probe support. + * probe.h: Likewise. + * s390-tdep.c: Including headers needed to perform the parsing of + SystemTap probes' arguments. + (s390_stap_is_single_operand): New function. + (s390_gdbarch_init): Initializing proper fields used by SystemTap + probes' arguments parser. + * somread.c (som_sym_fns): Add `sym_probe_fns' value. + * stap-probe.c: New file, for SystemTap probe support. + * stap-probe.h: Likewise. + * symfile.h: Include `gdb_vecs.h'. + (struct sym_probe_fns): New struct. + (struct sym_fns) <sym_probe_fns>: New field. + * symtab.c (init_sal): Initialize `probe' field. + * symtab.h (struct probe): Forward declaration. + (struct symtab_and_line) <probe>: New field. + * tracepoint.c (start_tracing): Adjust semaphore on breakpoints + locations. + (stop_tracing): Likewise. + * xcoffread.c (xcoff_sym_fns): Add `sym_probe_fns' value. + +2012-04-27 Sergio Durigan Junior <sergiodj@redhat.com> + Tom Tromey <tromey@redhat.com> * ax-gdb.c (gen_expr): Clean up code to handle internal variables and to compile agent expressions. diff --git a/gdb/Makefile.in b/gdb/Makefile.in index fbe5b53..4368f07 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -726,8 +726,8 @@ SFILES = ada-exp.y ada-lang.c ada-typeprint.c ada-valprint.c ada-tasks.c \ sentinel-frame.c \ serial.c ser-base.c ser-unix.c skip.c \ solib.c solib-target.c source.c \ - stabsread.c stack.c std-regs.c symfile.c symfile-mem.c symmisc.c \ - symtab.c \ + stabsread.c stack.c probe.c stap-probe.c std-regs.c \ + symfile.c symfile-mem.c symmisc.c symtab.c \ target.c target-descriptions.c target-memory.c \ thread.c top.c tracepoint.c \ trad-frame.c \ @@ -826,7 +826,7 @@ gnulib/import/extra/snippet/arg-nonnull.h gnulib/import/extra/snippet/c++defs.h gnulib/import/extra/snippet/warn-on-use.h \ gnulib/import/stddef.in.h gnulib/import/inttypes.in.h inline-frame.h skip.h \ common/common-utils.h common/xml-utils.h common/buffer.h common/ptid.h \ -common/linux-osdata.h gdb-dlfcn.h auto-load.h +common/linux-osdata.h gdb-dlfcn.h auto-load.h probe.h stap-probe.h # Header files that already have srcdir in them, or which are in objdir. @@ -914,7 +914,7 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \ xml-support.o xml-syscall.o xml-utils.o \ target-descriptions.o target-memory.o xml-tdesc.o xml-builtin.o \ inferior.o osdata.o gdb_usleep.o record.o gcore.o \ - jit.o progspace.o skip.o \ + jit.o progspace.o skip.o probe.o \ common-utils.o buffer.o ptid.o gdb-dlfcn.o common-agent.o TSOBS = inflow.o @@ -3,6 +3,13 @@ *** Changes since GDB 7.4 +* GDB now has support for SDT (Static Defined Tracing) probes. Currently, + the only implemented backend is for SystemTap probes (<sys/sdt.h>). You + can set a breakpoint using the new "-probe, "-pstap" or "-probe-stap" + options and inspect the probe arguments using the new $_probe_arg family + of convenience variables. You can obtain more information about SystemTap + in <http://sourceware.org/systemtap/>. + * GDB now supports reversible debugging on ARM, it allows you to debug basic ARM and THUMB instructions, and provides record/replay support. diff --git a/gdb/amd64-tdep.c b/gdb/amd64-tdep.c index d15acea..685fa48 100644 --- a/gdb/amd64-tdep.c +++ b/gdb/amd64-tdep.c @@ -2691,6 +2691,16 @@ amd64_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) set_gdbarch_relocate_instruction (gdbarch, amd64_relocate_instruction); set_gdbarch_gen_return_address (gdbarch, amd64_gen_return_address); + + /* SystemTap variables and functions. */ + set_gdbarch_stap_integer_prefix (gdbarch, "$"); + set_gdbarch_stap_register_prefix (gdbarch, "%"); + set_gdbarch_stap_register_indirection_prefix (gdbarch, "("); + set_gdbarch_stap_register_indirection_suffix (gdbarch, ")"); + set_gdbarch_stap_is_single_operand (gdbarch, + i386_stap_is_single_operand); + set_gdbarch_stap_parse_special_token (gdbarch, + i386_stap_parse_special_token); } /* Provide a prototype to silence -Wmissing-prototypes. */ diff --git a/gdb/arm-linux-tdep.c b/gdb/arm-linux-tdep.c index 486e6ed..f4eaa5c 100644 --- a/gdb/arm-linux-tdep.c +++ b/gdb/arm-linux-tdep.c @@ -43,6 +43,12 @@ #include "gdbthread.h" #include "symfile.h" +#include "cli/cli-utils.h" +#include "stap-probe.h" +#include "parser-defs.h" +#include "user-regs.h" +#include <ctype.h> + #include "gdb_string.h" /* This is defined in <elf.h> on ARM GNU/Linux systems. */ @@ -1056,6 +1062,122 @@ arm_linux_displaced_step_copy_insn (struct gdbarch *gdbarch, return dsc; } +static int +arm_stap_is_single_operand (struct gdbarch *gdbarch, const char *s) +{ + return (*s == '#' /* Literal number. */ + || *s == '[' /* Register indirection or + displacement. */ + || isalpha (*s)); /* Register value. */ +} + +/* This routine is used to parse a special token in ARM's assembly. + + The special tokens parsed by it are: + + - Register displacement (e.g, [fp, #-8]) + + It returns one if the special token has been parsed successfully, + or zero if the current token is not considered special. */ + +static int +arm_stap_parse_special_token (struct gdbarch *gdbarch, + struct stap_parse_info *p) +{ + if (*p->arg == '[') + { + /* Temporary holder for lookahead. */ + const char *tmp = p->arg; + /* Used to save the register name. */ + const char *start; + char *regname; + int len, offset; + int got_minus = 0; + long displacement; + struct stoken str; + + ++tmp; + start = tmp; + + /* Register name. */ + while (isalnum (*tmp)) + ++tmp; + + if (*tmp != ',') + return 0; + + len = tmp - start; + regname = alloca (len + 2); + + offset = 0; + if (isdigit (*start)) + { + /* If we are dealing with a register whose name begins with a + digit, it means we should prefix the name with the letter + `r', because GDB expects this name pattern. Otherwise (e.g., + we are dealing with the register `fp'), we don't need to + add such a prefix. */ + regname[0] = 'r'; + offset = 1; + } + + strncpy (regname + offset, start, len); + len += offset; + regname[len] = '\0'; + + if (user_reg_map_name_to_regnum (gdbarch, regname, len) == -1) + error (_("Invalid register name `%s' on expression `%s'."), + regname, p->saved_arg); + + ++tmp; + tmp = skip_spaces_const (tmp); + if (*tmp++ != '#') + return 0; + + if (*tmp == '-') + { + ++tmp; + got_minus = 1; + } + + displacement = strtol (tmp, (char **) &tmp, 10); + + /* Skipping last `]'. */ + if (*tmp++ != ']') + return 0; + + /* The displacement. */ + write_exp_elt_opcode (OP_LONG); + write_exp_elt_type (builtin_type (gdbarch)->builtin_long); + write_exp_elt_longcst (displacement); + write_exp_elt_opcode (OP_LONG); + if (got_minus) + write_exp_elt_opcode (UNOP_NEG); + + /* The register name. */ + write_exp_elt_opcode (OP_REGISTER); + str.ptr = regname; + str.length = len; + write_exp_string (str); + write_exp_elt_opcode (OP_REGISTER); + + write_exp_elt_opcode (BINOP_ADD); + + /* Casting to the expected type. */ + write_exp_elt_opcode (UNOP_CAST); + write_exp_elt_type (lookup_pointer_type (p->arg_type)); + write_exp_elt_opcode (UNOP_CAST); + + write_exp_elt_opcode (UNOP_IND); + + p->arg = tmp; + } + else + return 0; + + return 1; +} + static void arm_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) @@ -1158,6 +1280,16 @@ arm_linux_init_abi (struct gdbarch_info info, /* Reversible debugging, process record. */ set_gdbarch_process_record (gdbarch, arm_process_record); + /* SystemTap functions. */ + set_gdbarch_stap_integer_prefix (gdbarch, "#"); + set_gdbarch_stap_register_prefix (gdbarch, "r"); + set_gdbarch_stap_register_indirection_prefix (gdbarch, "["); + set_gdbarch_stap_register_indirection_suffix (gdbarch, "]"); + set_gdbarch_stap_gdb_register_prefix (gdbarch, "r"); + set_gdbarch_stap_is_single_operand (gdbarch, arm_stap_is_single_operand); + set_gdbarch_stap_parse_special_token (gdbarch, + arm_stap_parse_special_token); + tdep->syscall_next_pc = arm_linux_syscall_next_pc; /* Syscall record. */ diff --git a/gdb/ax-gdb.c b/gdb/ax-gdb.c index eebe61a..909f282 100644 --- a/gdb/ax-gdb.c +++ b/gdb/ax-gdb.c @@ -95,8 +95,6 @@ static void gen_int_literal (struct agent_expr *ax, struct axs_value *value, LONGEST k, struct type *type); - -static void require_rvalue (struct agent_expr *ax, struct axs_value *value); static void gen_usual_unary (struct expression *exp, struct agent_expr *ax, struct axs_value *value); static int type_wider_than (struct type *type1, struct type *type2); @@ -157,8 +155,6 @@ static void gen_repeat (struct expression *exp, union exp_element **pc, static void gen_sizeof (struct expression *exp, union exp_element **pc, struct agent_expr *ax, struct axs_value *value, struct type *size_type); -static void gen_expr (struct expression *exp, union exp_element **pc, - struct agent_expr *ax, struct axs_value *value); static void gen_expr_binop_rest (struct expression *exp, enum exp_opcode op, union exp_element **pc, struct agent_expr *ax, @@ -791,7 +787,7 @@ gen_int_literal (struct agent_expr *ax, struct axs_value *value, LONGEST k, /* Take what's on the top of the stack (as described by VALUE), and try to make an rvalue out of it. Signal an error if we can't do that. */ -static void +void require_rvalue (struct agent_expr *ax, struct axs_value *value) { /* Only deal with scalars, structs and such may be too large @@ -1803,7 +1799,7 @@ gen_sizeof (struct expression *exp, union exp_element **pc, /* XXX: i18n */ /* A gen_expr function written by a Gen-X'er guy. Append code for the subexpression of EXPR starting at *POS_P to AX. */ -static void +void gen_expr (struct expression *exp, union exp_element **pc, struct agent_expr *ax, struct axs_value *value) { diff --git a/gdb/ax-gdb.h b/gdb/ax-gdb.h index 48c35a4..09f6889 100644 --- a/gdb/ax-gdb.h +++ b/gdb/ax-gdb.h @@ -110,6 +110,11 @@ extern struct agent_expr *gen_trace_for_return_address (CORE_ADDR, extern struct agent_expr *gen_eval_for_expr (CORE_ADDR, struct expression *); +extern void gen_expr (struct expression *exp, union exp_element **pc, + struct agent_expr *ax, struct axs_value *value); + +extern void require_rvalue (struct agent_expr *ax, struct axs_value *value); + extern int trace_kludge; extern int trace_string_kludge; diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c index 4204e36..2415974 100644 --- a/gdb/breakpoint.c +++ b/gdb/breakpoint.c @@ -60,6 +60,8 @@ #include "jit.h" #include "xml-syscall.h" #include "parser-defs.h" +#include "gdb_regex.h" +#include "probe.h" #include "cli/cli-utils.h" #include "continuations.h" #include "stack.h" @@ -289,6 +291,9 @@ static struct breakpoint_ops momentary_breakpoint_ops; breakpoints. */ struct breakpoint_ops bkpt_breakpoint_ops; +/* Breakpoints set on probes. */ +static struct breakpoint_ops bkpt_probe_breakpoint_ops; + /* A reference-counted struct command_line. This lets multiple breakpoints share a single command list. */ struct counted_command_line @@ -8149,6 +8154,7 @@ momentary_breakpoint_from_master (struct breakpoint *orig, copy->loc->address = orig->loc->address; copy->loc->section = orig->loc->section; copy->loc->pspace = orig->loc->pspace; + copy->loc->probe = orig->loc->probe; if (orig->loc->source_file != NULL) copy->loc->source_file = xstrdup (orig->loc->source_file); @@ -8234,6 +8240,7 @@ add_location_to_breakpoint (struct breakpoint *b, loc->requested_address = sal->pc; loc->address = adjusted_address; loc->pspace = sal->pspace; + loc->probe = sal->probe; gdb_assert (loc->pspace != NULL); loc->section = sal->section; loc->gdbarch = loc_gdbarch; @@ -8973,6 +8980,14 @@ break_command_1 (char *arg, int flag, int from_tty) enum bptype type_wanted = (flag & BP_HARDWAREFLAG ? bp_hardware_breakpoint : bp_breakpoint); + struct breakpoint_ops *ops; + const char *arg_cp = arg; + + /* Matching breakpoints on probes. */ + if (arg && probe_linespec_to_ops (&arg_cp) != NULL) + ops = &bkpt_probe_breakpoint_ops; + else + ops = &bkpt_breakpoint_ops; create_breakpoint (get_current_arch (), arg, @@ -8980,7 +8995,7 @@ break_command_1 (char *arg, int flag, int from_tty) tempflag, type_wanted, 0 /* Ignore count */, pending_break_support, - &bkpt_breakpoint_ops, + ops, from_tty, 1 /* enabled */, 0 /* internal */, @@ -12454,6 +12469,57 @@ momentary_bkpt_print_mention (struct breakpoint *b) /* Nothing to mention. These breakpoints are internal. */ } +/* Specific methods for probe breakpoints. */ + +static int +bkpt_probe_insert_location (struct bp_location *bl) +{ + int v = bkpt_insert_location (bl); + + if (v == 0) + { + /* The insertion was successful, now let's set the probe's semaphore + if needed. */ + bl->probe->pops->set_semaphore (bl->probe, bl->gdbarch); + } + + return v; +} + +static int +bkpt_probe_remove_location (struct bp_location *bl) +{ + /* Let's clear the semaphore before removing the location. */ + bl->probe->pops->clear_semaphore (bl->probe, bl->gdbarch); + + return bkpt_remove_location (bl); +} + +static void +bkpt_probe_create_sals_from_address (char **arg, + struct linespec_result *canonical, + enum bptype type_wanted, + char *addr_start, char **copy_arg) +{ + struct linespec_sals lsal; + + lsal.sals = parse_probes (arg, canonical); + + *copy_arg = xstrdup (canonical->addr_string); + lsal.canonical = xstrdup (*copy_arg); + + VEC_safe_push (linespec_sals, canonical->sals, &lsal); +} + +static void +bkpt_probe_decode_linespec (struct breakpoint *b, char **s, + struct symtabs_and_lines *sals) +{ + *sals = parse_probes (s, NULL); + if (!sals->sals) + error (_("probe not found")); +} + /* The breakpoint_ops structure to be used in tracepoints. */ static void @@ -12577,6 +12643,30 @@ tracepoint_decode_linespec (struct breakpoint *b, char **s, struct breakpoint_ops tracepoint_breakpoint_ops; +/* The breakpoint_ops structure to be use on tracepoints placed in a + static probe. */ + +static void +tracepoint_probe_create_sals_from_address (char **arg, + struct linespec_result *canonical, + enum bptype type_wanted, + char *addr_start, char **copy_arg) +{ + /* We use the same method for breakpoint on probes. */ + bkpt_probe_create_sals_from_address (arg, canonical, type_wanted, + addr_start, copy_arg); +} + +static void +tracepoint_probe_decode_linespec (struct breakpoint *b, char **s, + struct symtabs_and_lines *sals) +{ + /* We use the same method for breakpoint on probes. */ + bkpt_probe_decode_linespec (b, s, sals); +} + +static struct breakpoint_ops tracepoint_probe_breakpoint_ops; + /* The breakpoint_ops structure to be used on static tracepoints with markers (`-m'). */ @@ -14208,6 +14298,14 @@ set_tracepoint_count (int num) static void trace_command (char *arg, int from_tty) { + struct breakpoint_ops *ops; + const char *arg_cp = arg; + + if (arg && probe_linespec_to_ops (&arg_cp)) + ops = &tracepoint_probe_breakpoint_ops; + else + ops = &tracepoint_breakpoint_ops; + if (create_breakpoint (get_current_arch (), arg, NULL, 0, 1 /* parse arg */, @@ -14215,7 +14313,7 @@ trace_command (char *arg, int from_tty) bp_tracepoint /* type_wanted */, 0 /* Ignore count */, pending_break_support, - &tracepoint_breakpoint_ops, + ops, from_tty, 1 /* enabled */, 0 /* internal */, 0)) @@ -14950,6 +15048,14 @@ initialize_breakpoint_ops (void) ops->print_it = momentary_bkpt_print_it; ops->print_mention = momentary_bkpt_print_mention; + /* Probe breakpoints. */ + ops = &bkpt_probe_breakpoint_ops; + *ops = bkpt_breakpoint_ops; + ops->insert_location = bkpt_probe_insert_location; + ops->remove_location = bkpt_probe_remove_location; + ops->create_sals_from_address = bkpt_probe_create_sals_from_address; + ops->decode_linespec = bkpt_probe_decode_linespec; + /* GNU v3 exception catchpoints. */ ops = &gnu_v3_exception_catchpoint_ops; *ops = bkpt_breakpoint_ops; @@ -14997,6 +15103,12 @@ initialize_breakpoint_ops (void) ops->create_breakpoints_sal = tracepoint_create_breakpoints_sal; ops->decode_linespec = tracepoint_decode_linespec; + /* Probe tracepoints. */ + ops = &tracepoint_probe_breakpoint_ops; + *ops = tracepoint_breakpoint_ops; + ops->create_sals_from_address = tracepoint_probe_create_sals_from_address; + ops->decode_linespec = tracepoint_probe_decode_linespec; + /* Static tracepoints with marker (`-m'). */ ops = &strace_marker_breakpoint_ops; *ops = tracepoint_breakpoint_ops; @@ -15075,7 +15187,8 @@ _initialize_breakpoint (void) observer_attach_inferior_exit (clear_syscall_counts); observer_attach_memory_changed (invalidate_bp_value_on_memory_change); - breakpoint_objfile_key = register_objfile_data (); + breakpoint_objfile_key + = register_objfile_data_with_cleanup (NULL, free_breakpoint_probes); catch_syscall_inferior_data = register_inferior_data_with_cleanup (catch_syscall_inferior_data_cleanup); diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h index 75b62d2..d23561a 100644 --- a/gdb/breakpoint.h +++ b/gdb/breakpoint.h @@ -418,6 +418,10 @@ struct bp_location processor's architectual constraints. */ CORE_ADDR requested_address; + /* If the location comes from a probe point, this is the probe associated + with it. */ + struct probe *probe; + char *function_name; /* Details of the placed breakpoint, when inserted. */ diff --git a/gdb/cli/cli-utils.c b/gdb/cli/cli-utils.c index a7b2718..3239a46 100644 --- a/gdb/cli/cli-utils.c +++ b/gdb/cli/cli-utils.c @@ -223,6 +223,18 @@ skip_spaces (char *chp) return chp; } +/* A const-correct version of the above. */ + +const char * +skip_spaces_const (const char *chp) +{ + if (chp == NULL) + return NULL; + while (*chp && isspace (*chp)) + chp++; + return chp; +} + /* See documentation in cli-utils.h. */ char * @@ -245,3 +257,32 @@ remove_trailing_whitespace (const char *start, char *s) return s; } + +/* See documentation in cli-utils.h. */ + +char * +extract_arg (char **arg) +{ + char *result, *copy; + + if (!*arg) + return NULL; + + /* Find the start of the argument. */ + *arg = skip_spaces (*arg); + if (!**arg) + return NULL; + result = *arg; + + /* Find the end of the argument. */ + *arg = skip_to_space (*arg + 1); + + if (result == *arg) + return NULL; + + copy = xmalloc (*arg - result + 1); + memcpy (copy, result, *arg - result); + copy[*arg - result] = '\0'; + + return copy; +} diff --git a/gdb/cli/cli-utils.h b/gdb/cli/cli-utils.h index e23c7d8..5f8a91d 100644 --- a/gdb/cli/cli-utils.h +++ b/gdb/cli/cli-utils.h @@ -94,6 +94,10 @@ extern int number_is_in_list (char *list, int number); extern char *skip_spaces (char *inp); +/* A const-correct version of the above. */ + +extern const char *skip_spaces_const (const char *inp); + /* Skip leading non-whitespace characters in INP, returning an updated pointer. If INP is NULL, return NULL. */ @@ -103,4 +107,11 @@ extern char *skip_to_space (char *inp); START. */ extern char *remove_trailing_whitespace (const char *start, char *s); + +/* A helper function to extract an argument from *ARG. An argument is + delimited by whitespace. The return value is either NULL if no + argument was found, or an xmalloc'd string. */ + +extern char *extract_arg (char **arg); + #endif /* CLI_UTILS_H */ diff --git a/gdb/coffread.c b/gdb/coffread.c index 7c59535..2a8ee42 100644 --- a/gdb/coffread.c +++ b/gdb/coffread.c @@ -2196,6 +2196,7 @@ static const struct sym_fns coff_sym_fns = default_symfile_relocate, /* sym_relocate: Relocate a debug section. */ + NULL, /* sym_probe_fns */ &psym_functions }; diff --git a/gdb/configure b/gdb/configure index fd81609..12260de 100755 --- a/gdb/configure +++ b/gdb/configure @@ -12462,7 +12462,7 @@ fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $gdb_cv_var_elf" >&5 $as_echo "$gdb_cv_var_elf" >&6; } if test $gdb_cv_var_elf = yes; then - CONFIG_OBS="$CONFIG_OBS elfread.o" + CONFIG_OBS="$CONFIG_OBS elfread.o stap-probe.o" $as_echo "#define HAVE_ELF 1" >>confdefs.h diff --git a/gdb/configure.ac b/gdb/configure.ac index 0f5d651..b45c57d 100644 --- a/gdb/configure.ac +++ b/gdb/configure.ac @@ -1948,7 +1948,7 @@ AC_CACHE_CHECK([for ELF support in BFD], gdb_cv_var_elf, [bfd *abfd = NULL; bfd_get_elf_phdr_upper_bound (abfd); ], gdb_cv_var_elf=yes, gdb_cv_var_elf=no)]) if test $gdb_cv_var_elf = yes; then - CONFIG_OBS="$CONFIG_OBS elfread.o" + CONFIG_OBS="$CONFIG_OBS elfread.o stap-probe.o" AC_DEFINE(HAVE_ELF, 1, [Define if ELF support should be included.]) # -ldl is provided by bfd/Makfile.am (LIBDL) <PLUGINS>. diff --git a/gdb/dbxread.c b/gdb/dbxread.c index 1725112..2d47407 100644 --- a/gdb/dbxread.c +++ b/gdb/dbxread.c @@ -3589,6 +3589,7 @@ static const struct sym_fns aout_sym_fns = default_symfile_segments, /* Get segment information from a file. */ NULL, default_symfile_relocate, /* Relocate a debug section. */ + NULL, /* sym_probe_fns */ &psym_functions }; diff --git a/gdb/elfread.c b/gdb/elfread.c index 117e674..d825d9a 100644 --- a/gdb/elfread.c +++ b/gdb/elfread.c @@ -36,6 +36,8 @@ #include "demangle.h" #include "psympriv.h" #include "filenames.h" +#include "probe.h" +#include "arch-utils.h" #include "gdbtypes.h" #include "value.h" #include "infcall.h" @@ -60,6 +62,10 @@ struct elfinfo asection *mdebugsect; /* Section pointer for .mdebug section */ }; +/* Per-objfile data for probe info. */ + +static const struct objfile_data *probe_key = NULL; + static void free_elfinfo (void *); /* Minimal symbols located at the GOT entries for .plt - that is the real @@ -1576,7 +1582,117 @@ elfstab_offset_sections (struct objfile *objfile, struct partial_symtab *pst) complaint (&symfile_complaints, _("elf/stab section information missing for %s"), filename); } + +/* Implementation of `sym_get_probes', as documented in symfile.h. */ + +static VEC (probe_p) * +elf_get_probes (struct objfile *objfile) +{ + VEC (probe_p) *probes_per_objfile; + + /* Have we parsed this objfile's probes already? */ + probes_per_objfile = objfile_data (objfile, probe_key); + + if (!probes_per_objfile) + { + int ix; + const struct probe_ops *probe_ops; + + /* Here we try to gather information about all types of probes from the + objfile. */ + for (ix = 0; VEC_iterate (probe_ops_cp, all_probe_ops, ix, probe_ops); + ix++) + probe_ops->get_probes (&probes_per_objfile, objfile); + + if (probes_per_objfile == NULL) + { + VEC_reserve (probe_p, probes_per_objfile, 1); + gdb_assert (probes_per_objfile != NULL); + } + + set_objfile_data (objfile, probe_key, probes_per_objfile); + } + + return probes_per_objfile; +} + +/* Implementation of `sym_get_probe_argument_count', as documented in + symfile.h. */ + +static unsigned +elf_get_probe_argument_count (struct objfile *objfile, + struct probe *probe) +{ + return probe->pops->get_probe_argument_count (probe, objfile); +} + +/* Implementation of `sym_evaluate_probe_argument', as documented in + symfile.h. */ + +static struct value * +elf_evaluate_probe_argument (struct objfile *objfile, + struct probe *probe, + unsigned n) +{ + return probe->pops->evaluate_probe_argument (probe, objfile, n); +} + +/* Implementation of `sym_compile_to_ax', as documented in symfile.h. */ + +static void +elf_compile_to_ax (struct objfile *objfile, + struct probe *probe, + struct agent_expr *expr, + struct axs_value *value, + unsigned n) +{ + probe->pops->compile_to_ax (probe, objfile, expr, value, n); +} + +/* Implementation of `sym_relocate_probe', as documented in symfile.h. */ + +static void +elf_symfile_relocate_probe (struct objfile *objfile, + struct section_offsets *new_offsets, + struct section_offsets *delta) +{ + int ix; + VEC (probe_p) *probes = objfile_data (objfile, probe_key); + struct probe *probe; + + for (ix = 0; VEC_iterate (probe_p, probes, ix, probe); ix++) + probe->pops->relocate (probe, ANOFFSET (delta, SECT_OFF_TEXT (objfile))); +} + +/* Helper function used to free the space allocated for storing SystemTap + probe information. */ + +static void +probe_key_free (struct objfile *objfile, void *d) +{ + int ix; + VEC (probe_p) *probes = d; + struct probe *probe; + + for (ix = 0; VEC_iterate (probe_p, probes, ix, probe); ix++) + probe->pops->destroy (probe); + + VEC_free (probe_p, probes); +} + + +/* Implementation `sym_probe_fns', as documented in symfile.h. */ + +static const struct sym_probe_fns elf_probe_fns = +{ + elf_get_probes, /* sym_get_probes */ + elf_get_probe_argument_count, /* sym_get_probe_argument_count */ + elf_evaluate_probe_argument, /* sym_evaluate_probe_argument */ + elf_compile_to_ax, /* sym_compile_to_ax */ + elf_symfile_relocate_probe, /* sym_relocate_probe */ +}; + /* Register that we are able to handle ELF object file formats. */ static const struct sym_fns elf_sym_fns = @@ -1591,6 +1707,7 @@ static const struct sym_fns elf_sym_fns = elf_symfile_segments, /* Get segment information from a file. */ NULL, default_symfile_relocate, /* Relocate a debug section. */ + &elf_probe_fns, /* sym_probe_fns */ &psym_functions }; @@ -1609,6 +1726,7 @@ static const struct sym_fns elf_sym_fns_lazy_psyms = elf_symfile_segments, /* Get segment information from a file. */ NULL, default_symfile_relocate, /* Relocate a debug section. */ + &elf_probe_fns, /* sym_probe_fns */ &psym_functions }; @@ -1626,6 +1744,7 @@ static const struct sym_fns elf_sym_fns_gdb_index = elf_symfile_segments, /* Get segment information from a file. */ NULL, default_symfile_relocate, /* Relocate a debug section. */ + &elf_probe_fns, /* sym_probe_fns */ &dwarf2_gdb_index_functions }; @@ -1642,6 +1761,7 @@ static const struct gnu_ifunc_fns elf_gnu_ifunc_fns = void _initialize_elfread (void) { + probe_key = register_objfile_data_with_cleanup (NULL, probe_key_free); add_symtab_fns (&elf_sym_fns); elf_objfile_gnu_ifunc_cache_data = register_objfile_data (); diff --git a/gdb/gdb_vecs.h b/gdb/gdb_vecs.h index ce32de3..d6de54a 100644 --- a/gdb/gdb_vecs.h +++ b/gdb/gdb_vecs.h @@ -23,6 +23,8 @@ #include "vec.h" +struct probe; + DEF_VEC_P (char_ptr); DEF_VEC_P (const_char_ptr); @@ -39,4 +41,7 @@ extern void dirnames_to_char_ptr_vec_append (VEC (char_ptr) **vecp, extern VEC (char_ptr) *dirnames_to_char_ptr_vec (const char *dirnames); +typedef struct probe *probe_p; +DEF_VEC_P (probe_p); + #endif /* GDB_VECS_H */ diff --git a/gdb/gdbarch.c b/gdb/gdbarch.c index c079932..056dd5a 100644 --- a/gdb/gdbarch.c +++ b/gdb/gdbarch.c @@ -265,6 +265,16 @@ struct gdbarch gdbarch_get_siginfo_type_ftype *get_siginfo_type; gdbarch_record_special_symbol_ftype *record_special_symbol; gdbarch_get_syscall_number_ftype *get_syscall_number; + const char * stap_integer_prefix; + const char * stap_integer_suffix; + const char * stap_register_prefix; + const char * stap_register_suffix; + const char * stap_register_indirection_prefix; + const char * stap_register_indirection_suffix; + const char * stap_gdb_register_prefix; + const char * stap_gdb_register_suffix; + gdbarch_stap_is_single_operand_ftype *stap_is_single_operand; + gdbarch_stap_parse_special_token_ftype *stap_parse_special_token; int has_global_solist; int has_global_breakpoints; gdbarch_has_shared_address_space_ftype *has_shared_address_space; @@ -423,6 +433,16 @@ struct gdbarch startup_gdbarch = 0, /* get_siginfo_type */ 0, /* record_special_symbol */ 0, /* get_syscall_number */ + 0, /* stap_integer_prefix */ + 0, /* stap_integer_suffix */ + 0, /* stap_register_prefix */ + 0, /* stap_register_suffix */ + 0, /* stap_register_indirection_prefix */ + 0, /* stap_register_indirection_suffix */ + 0, /* stap_gdb_register_prefix */ + 0, /* stap_gdb_register_suffix */ + 0, /* stap_is_single_operand */ + 0, /* stap_parse_special_token */ 0, /* has_global_solist */ 0, /* has_global_breakpoints */ default_has_shared_address_space, /* has_shared_address_space */ @@ -715,6 +735,16 @@ verify_gdbarch (struct gdbarch *gdbarch) /* Skip verify of get_siginfo_type, has predicate. */ /* Skip verify of record_special_symbol, has predicate. */ /* Skip verify of get_syscall_number, has predicate. */ + /* Skip verify of stap_integer_prefix, invalid_p == 0 */ + /* Skip verify of stap_integer_suffix, invalid_p == 0 */ + /* Skip verify of stap_register_prefix, invalid_p == 0 */ + /* Skip verify of stap_register_suffix, invalid_p == 0 */ + /* Skip verify of stap_register_indirection_prefix, invalid_p == 0 */ + /* Skip verify of stap_register_indirection_suffix, invalid_p == 0 */ + /* Skip verify of stap_gdb_register_prefix, invalid_p == 0 */ + /* Skip verify of stap_gdb_register_suffix, invalid_p == 0 */ + /* Skip verify of stap_is_single_operand, has predicate. */ + /* Skip verify of stap_parse_special_token, has predicate. */ /* Skip verify of has_global_solist, invalid_p == 0 */ /* Skip verify of has_global_breakpoints, invalid_p == 0 */ /* Skip verify of has_shared_address_space, invalid_p == 0 */ @@ -1267,6 +1297,42 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file) "gdbarch_dump: stabs_argument_has_addr = <%s>\n", host_address_to_string (gdbarch->stabs_argument_has_addr)); fprintf_unfiltered (file, + "gdbarch_dump: stap_gdb_register_prefix = %s\n", + gdbarch->stap_gdb_register_prefix); + fprintf_unfiltered (file, + "gdbarch_dump: stap_gdb_register_suffix = %s\n", + gdbarch->stap_gdb_register_suffix); + fprintf_unfiltered (file, + "gdbarch_dump: stap_integer_prefix = %s\n", + gdbarch->stap_integer_prefix); + fprintf_unfiltered (file, + "gdbarch_dump: stap_integer_suffix = %s\n", + gdbarch->stap_integer_suffix); + fprintf_unfiltered (file, + "gdbarch_dump: gdbarch_stap_is_single_operand_p() = %d\n", + gdbarch_stap_is_single_operand_p (gdbarch)); + fprintf_unfiltered (file, + "gdbarch_dump: stap_is_single_operand = <%s>\n", + host_address_to_string (gdbarch->stap_is_single_operand)); + fprintf_unfiltered (file, + "gdbarch_dump: gdbarch_stap_parse_special_token_p() = %d\n", + gdbarch_stap_parse_special_token_p (gdbarch)); + fprintf_unfiltered (file, + "gdbarch_dump: stap_parse_special_token = <%s>\n", + host_address_to_string (gdbarch->stap_parse_special_token)); + fprintf_unfiltered (file, + "gdbarch_dump: stap_register_indirection_prefix = %s\n", + gdbarch->stap_register_indirection_prefix); + fprintf_unfiltered (file, + "gdbarch_dump: stap_register_indirection_suffix = %s\n", + gdbarch->stap_register_indirection_suffix); + fprintf_unfiltered (file, + "gdbarch_dump: stap_register_prefix = %s\n", + gdbarch->stap_register_prefix); + fprintf_unfiltered (file, + "gdbarch_dump: stap_register_suffix = %s\n", + gdbarch->stap_register_suffix); + fprintf_unfiltered (file, "gdbarch_dump: gdbarch_static_transform_name_p() = %d\n", gdbarch_static_transform_name_p (gdbarch)); fprintf_unfiltered (file, @@ -3834,6 +3900,190 @@ set_gdbarch_get_syscall_number (struct gdbarch *gdbarch, gdbarch->get_syscall_number = get_syscall_number; } +const char * +gdbarch_stap_integer_prefix (struct gdbarch *gdbarch) +{ + gdb_assert (gdbarch != NULL); + /* Skip verify of stap_integer_prefix, invalid_p == 0 */ + if (gdbarch_debug >= 2) + fprintf_unfiltered (gdb_stdlog, "gdbarch_stap_integer_prefix called\n"); + return gdbarch->stap_integer_prefix; +} + +void +set_gdbarch_stap_integer_prefix (struct gdbarch *gdbarch, + const char * stap_integer_prefix) +{ + gdbarch->stap_integer_prefix = stap_integer_prefix; +} + +const char * +gdbarch_stap_integer_suffix (struct gdbarch *gdbarch) +{ + gdb_assert (gdbarch != NULL); + /* Skip verify of stap_integer_suffix, invalid_p == 0 */ + if (gdbarch_debug >= 2) + fprintf_unfiltered (gdb_stdlog, "gdbarch_stap_integer_suffix called\n"); + return gdbarch->stap_integer_suffix; +} + +void +set_gdbarch_stap_integer_suffix (struct gdbarch *gdbarch, + const char * stap_integer_suffix) +{ + gdbarch->stap_integer_suffix = stap_integer_suffix; +} + +const char * +gdbarch_stap_register_prefix (struct gdbarch *gdbarch) +{ + gdb_assert (gdbarch != NULL); + /* Skip verify of stap_register_prefix, invalid_p == 0 */ + if (gdbarch_debug >= 2) + fprintf_unfiltered (gdb_stdlog, "gdbarch_stap_register_prefix called\n"); + return gdbarch->stap_register_prefix; +} + +void +set_gdbarch_stap_register_prefix (struct gdbarch *gdbarch, + const char * stap_register_prefix) +{ + gdbarch->stap_register_prefix = stap_register_prefix; +} + +const char * +gdbarch_stap_register_suffix (struct gdbarch *gdbarch) +{ + gdb_assert (gdbarch != NULL); + /* Skip verify of stap_register_suffix, invalid_p == 0 */ + if (gdbarch_debug >= 2) + fprintf_unfiltered (gdb_stdlog, "gdbarch_stap_register_suffix called\n"); + return gdbarch->stap_register_suffix; +} + +void +set_gdbarch_stap_register_suffix (struct gdbarch *gdbarch, + const char * stap_register_suffix) +{ + gdbarch->stap_register_suffix = stap_register_suffix; +} + +const char * +gdbarch_stap_register_indirection_prefix (struct gdbarch *gdbarch) +{ + gdb_assert (gdbarch != NULL); + /* Skip verify of stap_register_indirection_prefix, invalid_p == 0 */ + if (gdbarch_debug >= 2) + fprintf_unfiltered (gdb_stdlog, "gdbarch_stap_register_indirection_prefix called\n"); + return gdbarch->stap_register_indirection_prefix; +} + +void +set_gdbarch_stap_register_indirection_prefix (struct gdbarch *gdbarch, + const char * stap_register_indirection_prefix) +{ + gdbarch->stap_register_indirection_prefix = stap_register_indirection_prefix; +} + +const char * +gdbarch_stap_register_indirection_suffix (struct gdbarch *gdbarch) +{ + gdb_assert (gdbarch != NULL); + /* Skip verify of stap_register_indirection_suffix, invalid_p == 0 */ + if (gdbarch_debug >= 2) + fprintf_unfiltered (gdb_stdlog, "gdbarch_stap_register_indirection_suffix called\n"); + return gdbarch->stap_register_indirection_suffix; +} + +void +set_gdbarch_stap_register_indirection_suffix (struct gdbarch *gdbarch, + const char * stap_register_indirection_suffix) +{ + gdbarch->stap_register_indirection_suffix = stap_register_indirection_suffix; +} + +const char * +gdbarch_stap_gdb_register_prefix (struct gdbarch *gdbarch) +{ + gdb_assert (gdbarch != NULL); + /* Skip verify of stap_gdb_register_prefix, invalid_p == 0 */ + if (gdbarch_debug >= 2) + fprintf_unfiltered (gdb_stdlog, "gdbarch_stap_gdb_register_prefix called\n"); + return gdbarch->stap_gdb_register_prefix; +} + +void +set_gdbarch_stap_gdb_register_prefix (struct gdbarch *gdbarch, + const char * stap_gdb_register_prefix) +{ + gdbarch->stap_gdb_register_prefix = stap_gdb_register_prefix; +} + +const char * +gdbarch_stap_gdb_register_suffix (struct gdbarch *gdbarch) +{ + gdb_assert (gdbarch != NULL); + /* Skip verify of stap_gdb_register_suffix, invalid_p == 0 */ + if (gdbarch_debug >= 2) + fprintf_unfiltered (gdb_stdlog, "gdbarch_stap_gdb_register_suffix called\n"); + return gdbarch->stap_gdb_register_suffix; +} + +void +set_gdbarch_stap_gdb_register_suffix (struct gdbarch *gdbarch, + const char * stap_gdb_register_suffix) +{ + gdbarch->stap_gdb_register_suffix = stap_gdb_register_suffix; +} + +int +gdbarch_stap_is_single_operand_p (struct gdbarch *gdbarch) +{ + gdb_assert (gdbarch != NULL); + return gdbarch->stap_is_single_operand != NULL; +} + +int +gdbarch_stap_is_single_operand (struct gdbarch *gdbarch, const char *s) +{ + gdb_assert (gdbarch != NULL); + gdb_assert (gdbarch->stap_is_single_operand != NULL); + if (gdbarch_debug >= 2) + fprintf_unfiltered (gdb_stdlog, "gdbarch_stap_is_single_operand called\n"); + return gdbarch->stap_is_single_operand (gdbarch, s); +} + +void +set_gdbarch_stap_is_single_operand (struct gdbarch *gdbarch, + gdbarch_stap_is_single_operand_ftype stap_is_single_operand) +{ + gdbarch->stap_is_single_operand = stap_is_single_operand; +} + +int +gdbarch_stap_parse_special_token_p (struct gdbarch *gdbarch) +{ + gdb_assert (gdbarch != NULL); + return gdbarch->stap_parse_special_token != NULL; +} + +int +gdbarch_stap_parse_special_token (struct gdbarch *gdbarch, struct stap_parse_info *p) +{ + gdb_assert (gdbarch != NULL); + gdb_assert (gdbarch->stap_parse_special_token != NULL); + if (gdbarch_debug >= 2) + fprintf_unfiltered (gdb_stdlog, "gdbarch_stap_parse_special_token called\n"); + return gdbarch->stap_parse_special_token (gdbarch, p); +} + +void +set_gdbarch_stap_parse_special_token (struct gdbarch *gdbarch, + gdbarch_stap_parse_special_token_ftype stap_parse_special_token) +{ + gdbarch->stap_parse_special_token = stap_parse_special_token; +} + int gdbarch_has_global_solist (struct gdbarch *gdbarch) { diff --git a/gdb/gdbarch.h b/gdb/gdbarch.h index 84e6ff8..2d832aa 100644 --- a/gdb/gdbarch.h +++ b/gdb/gdbarch.h @@ -55,6 +55,7 @@ struct core_regset_section; struct syscall; struct agent_expr; struct axs_value; +struct stap_parse_info; /* The architecture associated with the connection to the target. @@ -979,6 +980,125 @@ typedef LONGEST (gdbarch_get_syscall_number_ftype) (struct gdbarch *gdbarch, pti extern LONGEST gdbarch_get_syscall_number (struct gdbarch *gdbarch, ptid_t ptid); extern void set_gdbarch_get_syscall_number (struct gdbarch *gdbarch, gdbarch_get_syscall_number_ftype *get_syscall_number); +/* SystemTap related fields and functions. + Prefix used to mark an integer constant on the architecture's assembly + For example, on x86 integer constants are written as: + + $10 ;; integer constant 10 + + in this case, this prefix would be the character `$'. */ + +extern const char * gdbarch_stap_integer_prefix (struct gdbarch *gdbarch); +extern void set_gdbarch_stap_integer_prefix (struct gdbarch *gdbarch, const char * stap_integer_prefix); + +/* Suffix used to mark an integer constant on the architecture's assembly. */ + +extern const char * gdbarch_stap_integer_suffix (struct gdbarch *gdbarch); +extern void set_gdbarch_stap_integer_suffix (struct gdbarch *gdbarch, const char * stap_integer_suffix); + +/* Prefix used to mark a register name on the architecture's assembly. + For example, on x86 the register name is written as: + + %eax ;; register eax + + in this case, this prefix would be the character `%'. */ + +extern const char * gdbarch_stap_register_prefix (struct gdbarch *gdbarch); +extern void set_gdbarch_stap_register_prefix (struct gdbarch *gdbarch, const char * stap_register_prefix); + +/* Suffix used to mark a register name on the architecture's assembly */ + +extern const char * gdbarch_stap_register_suffix (struct gdbarch *gdbarch); +extern void set_gdbarch_stap_register_suffix (struct gdbarch *gdbarch, const char * stap_register_suffix); + +/* Prefix used to mark a register indirection on the architecture's assembly. + For example, on x86 the register indirection is written as: + + (%eax) ;; indirecting eax + + in this case, this prefix would be the charater `('. + + Please note that we use the indirection prefix also for register + displacement, e.g., `4(%eax)' on x86. */ + +extern const char * gdbarch_stap_register_indirection_prefix (struct gdbarch *gdbarch); +extern void set_gdbarch_stap_register_indirection_prefix (struct gdbarch *gdbarch, const char * stap_register_indirection_prefix); + +/* Suffix used to mark a register indirection on the architecture's assembly. + For example, on x86 the register indirection is written as: + + (%eax) ;; indirecting eax + + in this case, this prefix would be the charater `)'. + + Please note that we use the indirection suffix also for register + displacement, e.g., `4(%eax)' on x86. */ + +extern const char * gdbarch_stap_register_indirection_suffix (struct gdbarch *gdbarch); +extern void set_gdbarch_stap_register_indirection_suffix (struct gdbarch *gdbarch, const char * stap_register_indirection_suffix); + +/* Prefix used to name a register using GDB's nomenclature. + + For example, on PPC a register is represented by a number in the assembly + language (e.g., `10' is the 10th general-purpose register). However, + inside GDB this same register has an `r' appended to its name, so the 10th + register would be represented as `r10' internally. */ + +extern const char * gdbarch_stap_gdb_register_prefix (struct gdbarch *gdbarch); +extern void set_gdbarch_stap_gdb_register_prefix (struct gdbarch *gdbarch, const char * stap_gdb_register_prefix); + +/* Suffix used to name a register using GDB's nomenclature. */ + +extern const char * gdbarch_stap_gdb_register_suffix (struct gdbarch *gdbarch); +extern void set_gdbarch_stap_gdb_register_suffix (struct gdbarch *gdbarch, const char * stap_gdb_register_suffix); + +/* Check if S is a single operand. + + Single operands can be: + - Literal integers, e.g. `$10' on x86 + - Register access, e.g. `%eax' on x86 + - Register indirection, e.g. `(%eax)' on x86 + - Register displacement, e.g. `4(%eax)' on x86 + + This function should check for these patterns on the string + and return 1 if some were found, or zero otherwise. Please try to match + as much info as you can from the string, i.e., if you have to match + something like `(%', do not match just the `('. */ + +extern int gdbarch_stap_is_single_operand_p (struct gdbarch *gdbarch); + +typedef int (gdbarch_stap_is_single_operand_ftype) (struct gdbarch *gdbarch, const char *s); +extern int gdbarch_stap_is_single_operand (struct gdbarch *gdbarch, const char *s); +extern void set_gdbarch_stap_is_single_operand (struct gdbarch *gdbarch, gdbarch_stap_is_single_operand_ftype *stap_is_single_operand); + +/* Function used to handle a "special case" in the parser. + + A "special case" is considered to be an unknown token, i.e., a token + that the parser does not know how to parse. A good example of special + case would be ARM's register displacement syntax: + + [R0, #4] ;; displacing R0 by 4 + + Since the parser assumes that a register displacement is of the form: + + <number> <indirection_prefix> <register_name> <indirection_suffix> + + it means that it will not be able to recognize and parse this odd syntax. + Therefore, we should add a special case function that will handle this token. + + This function should generate the proper expression form of the expression + using GDB's internal expression mechanism (e.g., `write_exp_elt_opcode' + and so on). It should also return 1 if the parsing was successful, or zero + if the token was not recognized as a special token (in this case, returning + zero means that the special parser is deferring the parsing to the generic + parser), and should advance the buffer pointer (p->arg). */ + +extern int gdbarch_stap_parse_special_token_p (struct gdbarch *gdbarch); + +typedef int (gdbarch_stap_parse_special_token_ftype) (struct gdbarch *gdbarch, struct stap_parse_info *p); +extern int gdbarch_stap_parse_special_token (struct gdbarch *gdbarch, struct stap_parse_info *p); +extern void set_gdbarch_stap_parse_special_token (struct gdbarch *gdbarch, gdbarch_stap_parse_special_token_ftype *stap_parse_special_token); + /* True if the list of shared libraries is one and only for all processes, as opposed to a list of shared libraries per inferior. This usually means that all processes, although may or may not share diff --git a/gdb/gdbarch.sh b/gdb/gdbarch.sh index 5831172..956734a 100755 --- a/gdb/gdbarch.sh +++ b/gdb/gdbarch.sh @@ -792,6 +792,101 @@ M:void:record_special_symbol:struct objfile *objfile, asymbol *sym:objfile, sym # Get architecture-specific system calls information from registers. M:LONGEST:get_syscall_number:ptid_t ptid:ptid +# SystemTap related fields and functions. + +# Prefix used to mark an integer constant on the architecture's assembly +# For example, on x86 integer constants are written as: +# +# \$10 ;; integer constant 10 +# +# in this case, this prefix would be the character \`\$\'. +v:const char *:stap_integer_prefix:::0:0::0:gdbarch->stap_integer_prefix + +# Suffix used to mark an integer constant on the architecture's assembly. +v:const char *:stap_integer_suffix:::0:0::0:gdbarch->stap_integer_suffix + +# Prefix used to mark a register name on the architecture's assembly. +# For example, on x86 the register name is written as: +# +# \%eax ;; register eax +# +# in this case, this prefix would be the character \`\%\'. +v:const char *:stap_register_prefix:::0:0::0:gdbarch->stap_register_prefix + +# Suffix used to mark a register name on the architecture's assembly +v:const char *:stap_register_suffix:::0:0::0:gdbarch->stap_register_suffix + +# Prefix used to mark a register indirection on the architecture's assembly. +# For example, on x86 the register indirection is written as: +# +# \(\%eax\) ;; indirecting eax +# +# in this case, this prefix would be the charater \`\(\'. +# +# Please note that we use the indirection prefix also for register +# displacement, e.g., \`4\(\%eax\)\' on x86. +v:const char *:stap_register_indirection_prefix:::0:0::0:gdbarch->stap_register_indirection_prefix + +# Suffix used to mark a register indirection on the architecture's assembly. +# For example, on x86 the register indirection is written as: +# +# \(\%eax\) ;; indirecting eax +# +# in this case, this prefix would be the charater \`\)\'. +# +# Please note that we use the indirection suffix also for register +# displacement, e.g., \`4\(\%eax\)\' on x86. +v:const char *:stap_register_indirection_suffix:::0:0::0:gdbarch->stap_register_indirection_suffix + +# Prefix used to name a register using GDB's nomenclature. +# +# For example, on PPC a register is represented by a number in the assembly +# language (e.g., \`10\' is the 10th general-purpose register). However, +# inside GDB this same register has an \`r\' appended to its name, so the 10th +# register would be represented as \`r10\' internally. +v:const char *:stap_gdb_register_prefix:::0:0::0:gdbarch->stap_gdb_register_prefix + +# Suffix used to name a register using GDB's nomenclature. +v:const char *:stap_gdb_register_suffix:::0:0::0:gdbarch->stap_gdb_register_suffix + +# Check if S is a single operand. +# +# Single operands can be: +# \- Literal integers, e.g. \`\$10\' on x86 +# \- Register access, e.g. \`\%eax\' on x86 +# \- Register indirection, e.g. \`\(\%eax\)\' on x86 +# \- Register displacement, e.g. \`4\(\%eax\)\' on x86 +# +# This function should check for these patterns on the string +# and return 1 if some were found, or zero otherwise. Please try to match +# as much info as you can from the string, i.e., if you have to match +# something like \`\(\%\', do not match just the \`\(\'. +M:int:stap_is_single_operand:const char *s:s + +# Function used to handle a "special case" in the parser. +# +# A "special case" is considered to be an unknown token, i.e., a token +# that the parser does not know how to parse. A good example of special +# case would be ARM's register displacement syntax: +# +# [R0, #4] ;; displacing R0 by 4 +# +# Since the parser assumes that a register displacement is of the form: +# +# <number> <indirection_prefix> <register_name> <indirection_suffix> +# +# it means that it will not be able to recognize and parse this odd syntax. +# Therefore, we should add a special case function that will handle this token. +# +# This function should generate the proper expression form of the expression +# using GDB\'s internal expression mechanism (e.g., \`write_exp_elt_opcode\' +# and so on). It should also return 1 if the parsing was successful, or zero +# if the token was not recognized as a special token (in this case, returning +# zero means that the special parser is deferring the parsing to the generic +# parser), and should advance the buffer pointer (p->arg). +M:int:stap_parse_special_token:struct stap_parse_info *p:p + + # True if the list of shared libraries is one and only for all # processes, as opposed to a list of shared libraries per inferior. # This usually means that all processes, although may or may not share @@ -954,6 +1049,7 @@ struct core_regset_section; struct syscall; struct agent_expr; struct axs_value; +struct stap_parse_info; /* The architecture associated with the connection to the target. diff --git a/gdb/i386-tdep.c b/gdb/i386-tdep.c index d18aa99..769ef42 100644 --- a/gdb/i386-tdep.c +++ b/gdb/i386-tdep.c @@ -61,6 +61,13 @@ #include "ax.h" #include "ax-gdb.h" +#include "stap-probe.h" +#include "user-regs.h" +#include "cli/cli-utils.h" +#include "expression.h" +#include "parser-defs.h" +#include <ctype.h> + /* Register names. */ static const char *i386_register_names[] = @@ -3363,6 +3370,325 @@ i386_svr4_sigcontext_addr (struct frame_info *this_frame) return read_memory_unsigned_integer (sp + 8, 4, byte_order); } + + + +/* Implementation of `gdbarch_stap_is_single_operand', as defined in + gdbarch.h. */ + +int +i386_stap_is_single_operand (struct gdbarch *gdbarch, const char *s) +{ + return (*s == '$' /* Literal number. */ + || (isdigit (*s) && s[1] == '(' && s[2] == '%') /* Displacement. */ + || (*s == '(' && s[1] == '%') /* Register indirection. */ + || (*s == '%' && isalpha (s[1]))); /* Register access. */ +} + +/* Implementation of `gdbarch_stap_parse_special_token', as defined in + gdbarch.h. */ + +int +i386_stap_parse_special_token (struct gdbarch *gdbarch, + struct stap_parse_info *p) +{ + const char *s = p->arg; + + /* In order to parse special tokens, we use a state-machine that go + through every known token and try to get a match. */ + enum + { + TRIPLET, + THREE_ARG_DISPLACEMENT, + DONE + } current_state; + + current_state = TRIPLET; + + /* The special tokens to be parsed here are: + + - `register base + (register index * size) + offset', as represented + in `(%rcx,%rax,8)', or `[OFFSET](BASE_REG,INDEX_REG[,SIZE])'. + + - Operands of the form `-8+3+1(%rbp)', which must be interpreted as + `*(-8 + 3 - 1 + (void *) $eax)'. */ + + while (current_state != DONE) + { + const char *s = p->arg; + + switch (current_state) + { + case TRIPLET: + { + if (isdigit (*s) || *s == '-' || *s == '+') + { + int got_minus[3]; + int i; + long displacements[3]; + const char *start; + char *regname; + int len; + struct stoken str; + + got_minus[0] = 0; + if (*s == '+') + ++s; + else if (*s == '-') + { + ++s; + got_minus[0] = 1; + } + + displacements[0] = strtol (s, (char **) &s, 10); + + if (*s != '+' && *s != '-') + { + /* We are not dealing with a triplet. */ + break; + } + + got_minus[1] = 0; + if (*s == '+') + ++s; + else + { + ++s; + got_minus[1] = 1; + } + + displacements[1] = strtol (s, (char **) &s, 10); + + if (*s != '+' && *s != '-') + { + /* We are not dealing with a triplet. */ + break; + } + + got_minus[2] = 0; + if (*s == '+') + ++s; + else + { + ++s; + got_minus[2] = 1; + } + + displacements[2] = strtol (s, (char **) &s, 10); + + if (*s != '(' || s[1] != '%') + break; + + s += 2; + start = s; + + while (isalnum (*s)) + ++s; + + if (*s++ != ')') + break; + + len = s - start; + regname = alloca (len + 1); + + strncpy (regname, start, len); + regname[len] = '\0'; + + if (user_reg_map_name_to_regnum (gdbarch, + regname, len) == -1) + error (_("Invalid register name `%s' " + "on expression `%s'."), + regname, p->saved_arg); + + for (i = 0; i < 3; i++) + { + write_exp_elt_opcode (OP_LONG); + write_exp_elt_type + (builtin_type (gdbarch)->builtin_long); + write_exp_elt_longcst (displacements[i]); + write_exp_elt_opcode (OP_LONG); + if (got_minus[i]) + write_exp_elt_opcode (UNOP_NEG); + } + + write_exp_elt_opcode (OP_REGISTER); + str.ptr = regname; + str.length = len; + write_exp_string (str); + write_exp_elt_opcode (OP_REGISTER); + + write_exp_elt_opcode (UNOP_CAST); + write_exp_elt_type (builtin_type (gdbarch)->builtin_data_ptr); + write_exp_elt_opcode (UNOP_CAST); + + write_exp_elt_opcode (BINOP_ADD); + write_exp_elt_opcode (BINOP_ADD); + write_exp_elt_opcode (BINOP_ADD); + + write_exp_elt_opcode (UNOP_CAST); + write_exp_elt_type (lookup_pointer_type (p->arg_type)); + write_exp_elt_opcode (UNOP_CAST); + + write_exp_elt_opcode (UNOP_IND); + + p->arg = s; + + return 1; + } + break; + } + case THREE_ARG_DISPLACEMENT: + { + if (isdigit (*s) || *s == '(' || *s == '-' || *s == '+') + { + int offset_minus = 0; + long offset = 0; + int size_minus = 0; + long size = 0; + const char *start; + char *base; + int len_base; + char *index; + int len_index; + struct stoken base_token, index_token; + + if (*s == '+') + ++s; + else if (*s == '-') + { + ++s; + offset_minus = 1; + } + + if (offset_minus && !isdigit (*s)) + break; + + if (isdigit (*s)) + offset = strtol (s, (char **) &s, 10); + + if (*s != '(' || s[1] != '%') + break; + + s += 2; + start = s; + + while (isalnum (*s)) + ++s; + + if (*s != ',' || s[1] != '%') + break; + + len_base = s - start; + base = alloca (len_base + 1); + strncpy (base, start, len_base); + base[len_base] = '\0'; + + if (user_reg_map_name_to_regnum (gdbarch, + base, len_base) == -1) + error (_("Invalid register name `%s' " + "on expression `%s'."), + base, p->saved_arg); + + s += 2; + start = s; + + while (isalnum (*s)) + ++s; + + len_index = s - start; + index = alloca (len_index + 1); + strncpy (index, start, len_index); + index[len_index] = '\0'; + + if (user_reg_map_name_to_regnum (gdbarch, + index, len_index) == -1) + error (_("Invalid register name `%s' " + "on expression `%s'."), + index, p->saved_arg); + + if (*s != ',' && *s != ')') + break; + + if (*s == ',') + { + ++s; + if (*s == '+') + ++s; + else if (*s == '-') + { + ++s; + size_minus = 1; + } + + size = strtol (s, (char **) &s, 10); + + if (*s != ')') + break; + } + + ++s; + + if (offset) + { + write_exp_elt_opcode (OP_LONG); + write_exp_elt_type + (builtin_type (gdbarch)->builtin_long); + write_exp_elt_longcst (offset); + write_exp_elt_opcode (OP_LONG); + if (offset_minus) + write_exp_elt_opcode (UNOP_NEG); + } + + write_exp_elt_opcode (OP_REGISTER); + base_token.ptr = base; + base_token.length = len_base; + write_exp_string (base_token); + write_exp_elt_opcode (OP_REGISTER); + + if (offset) + write_exp_elt_opcode (BINOP_ADD); + + write_exp_elt_opcode (OP_REGISTER); + index_token.ptr = index; + index_token.length = len_index; + write_exp_string (index_token); + write_exp_elt_opcode (OP_REGISTER); + + if (size) + { + write_exp_elt_opcode (OP_LONG); + write_exp_elt_type + (builtin_type (gdbarch)->builtin_long); + write_exp_elt_longcst (size); + write_exp_elt_opcode (OP_LONG); + if (size_minus) + write_exp_elt_opcode (UNOP_NEG); + write_exp_elt_opcode (BINOP_MUL); + } + + write_exp_elt_opcode (BINOP_ADD); + + write_exp_elt_opcode (UNOP_CAST); + write_exp_elt_type (lookup_pointer_type (p->arg_type)); + write_exp_elt_opcode (UNOP_CAST); + + write_exp_elt_opcode (UNOP_IND); + + p->arg = s; + + return 1; + } + break; + } + } + + /* Advancing to the next state. */ + ++current_state; + } + + return 0; +} + /* Generic ELF. */ @@ -3372,6 +3698,16 @@ i386_elf_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) { /* We typically use stabs-in-ELF with the SVR4 register numbering. */ set_gdbarch_stab_reg_to_regnum (gdbarch, i386_svr4_reg_to_regnum); + + /* Registering SystemTap handlers. */ + set_gdbarch_stap_integer_prefix (gdbarch, "$"); + set_gdbarch_stap_register_prefix (gdbarch, "%"); + set_gdbarch_stap_register_indirection_prefix (gdbarch, "("); + set_gdbarch_stap_register_indirection_suffix (gdbarch, ")"); + set_gdbarch_stap_is_single_operand (gdbarch, + i386_stap_is_single_operand); + set_gdbarch_stap_parse_special_token (gdbarch, + i386_stap_parse_special_token); } /* System V Release 4 (SVR4). */ diff --git a/gdb/i386-tdep.h b/gdb/i386-tdep.h index 870054f..f297ae7 100644 --- a/gdb/i386-tdep.h +++ b/gdb/i386-tdep.h @@ -379,6 +379,7 @@ extern void i386_svr4_init_abi (struct gdbarch_info, struct gdbarch *); extern int i386_process_record (struct gdbarch *gdbarch, struct regcache *regcache, CORE_ADDR addr); + /* Functions and variables exported from i386bsd-tdep.c. */ @@ -394,4 +395,12 @@ extern int i386nbsd_sc_reg_offset[]; extern int i386obsd_sc_reg_offset[]; extern int i386bsd_sc_reg_offset[]; +/* SystemTap related functions. */ + +extern int i386_stap_is_single_operand (struct gdbarch *gdbarch, + const char *s); + +extern int i386_stap_parse_special_token (struct gdbarch *gdbarch, + struct stap_parse_info *p); + #endif /* i386-tdep.h */ diff --git a/gdb/machoread.c b/gdb/machoread.c index 8a6b500..1986f54 100644 --- a/gdb/machoread.c +++ b/gdb/machoread.c @@ -1032,6 +1032,7 @@ static const struct sym_fns macho_sym_fns = { default_symfile_segments, /* Get segment information from a file. */ NULL, macho_symfile_relocate, /* Relocate a debug section. */ + NULL, /* sym_get_probes */ &psym_functions }; diff --git a/gdb/mipsread.c b/gdb/mipsread.c index 5790730..23ceece 100644 --- a/gdb/mipsread.c +++ b/gdb/mipsread.c @@ -401,6 +401,7 @@ static const struct sym_fns ecoff_sym_fns = default_symfile_segments, /* Get segment information from a file. */ NULL, default_symfile_relocate, /* Relocate a debug section. */ + NULL, /* sym_probe_fns */ &psym_functions }; diff --git a/gdb/objfiles.c b/gdb/objfiles.c index e29b3a7..1f4913a 100644 --- a/gdb/objfiles.c +++ b/gdb/objfiles.c @@ -795,6 +795,11 @@ objfile_relocate1 (struct objfile *objfile, obj_section_addr (s)); } + /* Relocating probes. */ + if (objfile->sf && objfile->sf->sym_probe_fns) + objfile->sf->sym_probe_fns->sym_relocate_probe (objfile, + new_offsets, delta); + /* Data changed. */ return 1; } diff --git a/gdb/parse.c b/gdb/parse.c index 32a3bd6..79b2e21 100644 --- a/gdb/parse.c +++ b/gdb/parse.c @@ -113,8 +113,6 @@ show_parserdebug (struct ui_file *file, int from_tty, static void free_funcalls (void *ignore); -static int prefixify_expression (struct expression *); - static int prefixify_subexp (struct expression *, struct expression *, int, int); @@ -182,13 +180,9 @@ free_funcalls (void *ignore) /* This page contains the functions for adding data to the struct expression being constructed. */ -/* Helper function to initialize the expout, expout_size, expout_ptr - trio before it is used to store expression elements created during - the parsing of an expression. INITIAL_SIZE is the initial size of - the expout array. LANG is the language used to parse the expression. - And GDBARCH is the gdbarch to use during parsing. */ +/* See definition in parser-defs.h. */ -static void +void initialize_expout (int initial_size, const struct language_defn *lang, struct gdbarch *gdbarch) { @@ -200,11 +194,9 @@ initialize_expout (int initial_size, const struct language_defn *lang, expout->gdbarch = gdbarch; } -/* Helper function that frees any unsed space in the expout array. - It is generally used when the parser has just been parsed and - created. */ +/* See definition in parser-defs.h. */ -static void +void reallocate_expout (void) { /* Record the actual number of expression elements, and then @@ -804,14 +796,10 @@ copy_name (struct stoken token) return namecopy; } -/* Reverse an expression from suffix form (in which it is constructed) - to prefix form (in which we can conveniently print or execute it). - Ordinarily this always returns -1. However, if EXPOUT_LAST_STRUCT - is not -1 (i.e., we are trying to complete a field name), it will - return the index of the subexpression which is the left-hand-side - of the struct operation at EXPOUT_LAST_STRUCT. */ -static int +/* See comments on parser-defs.h. */ + +int prefixify_expression (struct expression *expr) { int len = sizeof (struct expression) + EXP_ELEM_TO_BYTES (expr->nelts); diff --git a/gdb/parser-defs.h b/gdb/parser-defs.h index 16b40ac..72b9e2f 100644 --- a/gdb/parser-defs.h +++ b/gdb/parser-defs.h @@ -130,6 +130,30 @@ union type_stack_elt extern union type_stack_elt *type_stack; extern int type_stack_depth, type_stack_size; +/* Helper function to initialize the expout, expout_size, expout_ptr + trio before it is used to store expression elements created during + the parsing of an expression. INITIAL_SIZE is the initial size of + the expout array. LANG is the language used to parse the expression. + And GDBARCH is the gdbarch to use during parsing. */ + +extern void initialize_expout (int, const struct language_defn *, + struct gdbarch *); + +/* Helper function that frees any unsed space in the expout array. + It is generally used when the parser has just been parsed and + created. */ + +extern void reallocate_expout (void); + +/* Reverse an expression from suffix form (in which it is constructed) + to prefix form (in which we can conveniently print or execute it). + Ordinarily this always returns -1. However, if EXPOUT_LAST_STRUCT + is not -1 (i.e., we are trying to complete a field name), it will + return the index of the subexpression which is the left-hand-side + of the struct operation at EXPOUT_LAST_STRUCT. */ + +extern int prefixify_expression (struct expression *expr); + extern void write_exp_elt_opcode (enum exp_opcode); extern void write_exp_elt_sym (struct symbol *); diff --git a/gdb/ppc-linux-tdep.c b/gdb/ppc-linux-tdep.c index b94dea2..3392e67 100644 --- a/gdb/ppc-linux-tdep.c +++ b/gdb/ppc-linux-tdep.c @@ -50,6 +50,14 @@ #include "xml-syscall.h" #include "linux-tdep.h" +#include "stap-probe.h" +#include "ax.h" +#include "ax-gdb.h" +#include "cli/cli-utils.h" +#include "parser-defs.h" +#include "user-regs.h" +#include <ctype.h> + #include "features/rs6000/powerpc-32l.c" #include "features/rs6000/powerpc-altivec32l.c" #include "features/rs6000/powerpc-cell32l.c" @@ -1276,6 +1284,75 @@ ppc_linux_core_read_description (struct gdbarch *gdbarch, } } +/* Implementation of `gdbarch_stap_is_single_operand', as defined in + gdbarch.h. */ + +static int +ppc_stap_is_single_operand (struct gdbarch *gdbarch, const char *s) +{ + return (*s == 'i' /* Literal number. */ + || (isdigit (*s) && s[1] == '(' + && isdigit (s[2])) /* Displacement. */ + || (*s == '(' && isdigit (s[1])) /* Register indirection. */ + || isdigit (*s)); /* Register value. */ +} + +/* Implementation of `gdbarch_stap_parse_special_token', as defined in + gdbarch.h. */ + +static int +ppc_stap_parse_special_token (struct gdbarch *gdbarch, + struct stap_parse_info *p) +{ + if (isdigit (*p->arg)) + { + /* This temporary pointer is needed because we have to do a lookahead. + We could be dealing with a register displacement, and in such case + we would not need to do anything. */ + const char *s = p->arg; + char *regname; + int len; + struct stoken str; + + while (isdigit (*s)) + ++s; + + if (*s == '(') + { + /* It is a register displacement indeed. Returning 0 means we are + deferring the treatment of this case to the generic parser. */ + return 0; + } + + len = s - p->arg; + regname = alloca (len + 2); + regname[0] = 'r'; + + strncpy (regname + 1, p->arg, len); + ++len; + regname[len] = '\0'; + + if (user_reg_map_name_to_regnum (gdbarch, regname, len) == -1) + error (_("Invalid register name `%s' on expression `%s'."), + regname, p->saved_arg); + + write_exp_elt_opcode (OP_REGISTER); + str.ptr = regname; + str.length = len; + write_exp_string (str); + write_exp_elt_opcode (OP_REGISTER); + + p->arg = s; + } + else + { + /* All the other tokens should be handled correctly by the generic + parser. */ + return 0; + } + + return 1; +} /* Cell/B.E. active SPE context tracking support. */ @@ -1593,6 +1670,15 @@ ppc_linux_init_abi (struct gdbarch_info info, /* Get the syscall number from the arch's register. */ set_gdbarch_get_syscall_number (gdbarch, ppc_linux_get_syscall_number); + /* SystemTap functions. */ + set_gdbarch_stap_integer_prefix (gdbarch, "i"); + set_gdbarch_stap_register_indirection_prefix (gdbarch, "("); + set_gdbarch_stap_register_indirection_suffix (gdbarch, ")"); + set_gdbarch_stap_gdb_register_prefix (gdbarch, "r"); + set_gdbarch_stap_is_single_operand (gdbarch, ppc_stap_is_single_operand); + set_gdbarch_stap_parse_special_token (gdbarch, + ppc_stap_parse_special_token); + if (tdep->wordsize == 4) { /* Until November 2001, gcc did not comply with the 32 bit SysV diff --git a/gdb/probe.c b/gdb/probe.c new file mode 100644 index 0000000..4e1b6a0 --- /dev/null +++ b/gdb/probe.c @@ -0,0 +1,788 @@ +/* Generic static probe support for GDB. + + Copyright (C) 2012 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include "defs.h" +#include "probe.h" +#include "command.h" +#include "cli/cli-cmds.h" +#include "cli/cli-utils.h" +#include "objfiles.h" +#include "symtab.h" +#include "progspace.h" +#include "filenames.h" +#include "exceptions.h" +#include "linespec.h" +#include "gdb_regex.h" +#include "frame.h" +#include "arch-utils.h" +#include <ctype.h> + + + +/* See definition in probe.h. */ + +struct symtabs_and_lines +parse_probes (char **argptr, struct linespec_result *canonical) +{ + char *arg_start, *arg_end, *arg; + char *objfile_name = NULL, *provider = NULL, *name, *p; + struct cleanup *cleanup; + struct symtabs_and_lines result; + struct objfile *objfile; + struct program_space *pspace; + const struct probe_ops *probe_ops; + const char *cs; + + result.sals = NULL; + result.nelts = 0; + + arg_start = *argptr; + + cs = *argptr; + probe_ops = probe_linespec_to_ops (&cs); + gdb_assert (probe_ops != NULL); + + arg = (char *) cs; + arg = skip_spaces (arg); + if (!*arg) + error (_("argument to `%s' missing"), arg_start); + + arg_end = skip_to_space (arg); + + /* We make a copy here so we can write over parts with impunity. */ + arg = savestring (arg, arg_end - arg); + cleanup = make_cleanup (xfree, arg); + + /* Extract each word from the argument, separated by ":"s. */ + p = strchr (arg, ':'); + if (p == NULL) + { + /* This is `-p name'. */ + name = arg; + } + else + { + char *hold = p + 1; + + *p = '\0'; + p = strchr (hold, ':'); + if (p == NULL) + { + /* This is `-p provider:name'. */ + provider = arg; + name = hold; + } + else + { + /* This is `-p objfile:provider:name'. */ + *p = '\0'; + objfile_name = arg; + provider = hold; + name = p + 1; + } + } + + if (*name == '\0') + error (_("no probe name specified")); + if (provider && *provider == '\0') + error (_("invalid provider name")); + if (objfile_name && *objfile_name == '\0') + error (_("invalid objfile name")); + + ALL_PSPACES (pspace) + ALL_PSPACE_OBJFILES (pspace, objfile) + { + VEC (probe_p) *probes; + struct probe *probe; + int ix; + + if (!objfile->sf || !objfile->sf->sym_probe_fns) + continue; + + if (objfile_name + && FILENAME_CMP (objfile->name, objfile_name) != 0 + && FILENAME_CMP (lbasename (objfile->name), objfile_name) != 0) + continue; + + if (objfile->separate_debug_objfile_backlink != NULL) + continue; + + probes = objfile->sf->sym_probe_fns->sym_get_probes (objfile); + + for (ix = 0; VEC_iterate (probe_p, probes, ix, probe); ix++) + { + struct symtab_and_line *sal; + + if (probe_ops != &probe_ops_any && probe->pops != probe_ops) + continue; + + if (provider && strcmp (probe->provider, provider) != 0) + continue; + + if (strcmp (probe->name, name) != 0) + continue; + + ++result.nelts; + result.sals = xrealloc (result.sals, + result.nelts + * sizeof (struct symtab_and_line)); + sal = &result.sals[result.nelts - 1]; + + init_sal (sal); + + sal->pc = probe->address; + sal->explicit_pc = 1; + sal->section = find_pc_overlay (sal->pc); + sal->pspace = pspace; + sal->probe = probe; + } + } + + if (result.nelts == 0) + { + throw_error (NOT_FOUND_ERROR, + _("No probe matching objfile=`%s', provider=`%s', name=`%s'"), + objfile_name ? objfile_name : _("<any>"), + provider ? provider : _("<any>"), + name); + } + + if (canonical) + { + canonical->special_display = 1; + canonical->pre_expanded = 1; + canonical->addr_string = savestring (*argptr, arg_end - *argptr); + } + + *argptr = arg_end; + do_cleanups (cleanup); + + return result; +} + +/* See definition in probe.h. */ + +VEC (probe_p) * +find_probes_in_objfile (struct objfile *objfile, const char *provider, + const char *name) +{ + VEC (probe_p) *probes, *result = NULL; + int ix; + struct probe *probe; + + if (!objfile->sf || !objfile->sf->sym_probe_fns) + return NULL; + + probes = objfile->sf->sym_probe_fns->sym_get_probes (objfile); + for (ix = 0; VEC_iterate (probe_p, probes, ix, probe); ix++) + { + if (strcmp (probe->provider, provider) != 0) + continue; + + if (strcmp (probe->name, name) != 0) + continue; + + VEC_safe_push (probe_p, result, probe); + } + + return result; +} + +/* See definition in probe.h. */ + +struct probe * +find_probe_by_pc (CORE_ADDR pc, struct objfile **objfile_out) +{ + struct objfile *objfile; + + ALL_OBJFILES (objfile) + { + VEC (probe_p) *probes; + int ix; + struct probe *probe; + + if (!objfile->sf || !objfile->sf->sym_probe_fns) + continue; + + /* If this proves too inefficient, we can replace with a hash. */ + probes = objfile->sf->sym_probe_fns->sym_get_probes (objfile); + for (ix = 0; VEC_iterate (probe_p, probes, ix, probe); ix++) + if (probe->address == pc) + { + *objfile_out = objfile; + return probe; + } + } + + return NULL; +} + + + +/* A utility structure. A VEC of these is built when handling "info + probes". */ + +struct probe_and_objfile +{ + /* The probe. */ + struct probe *probe; + + /* The probe's objfile. */ + struct objfile *objfile; +}; + +typedef struct probe_and_objfile probe_and_objfile_s; +DEF_VEC_O (probe_and_objfile_s); + +/* A helper function for collect_probes that compiles a regexp and + throws an exception on error. This installs a cleanup to free the + resulting pattern on success. If RX is NULL, this does nothing. */ + +static void +compile_rx_or_error (regex_t *pattern, const char *rx, const char *message) +{ + int code; + + if (!rx) + return; + + code = regcomp (pattern, rx, REG_NOSUB); + if (code == 0) + make_regfree_cleanup (pattern); + else + { + char *err = get_regcomp_error (code, pattern); + + make_cleanup (xfree, err); + error ("%s: %s", message, err); + } +} + +/* Make a vector of probes matching OBJNAME, PROVIDER, and PROBE_NAME. + If POPS is not NULL, only probes of this certain probe_ops will match. + Each argument is a regexp, or NULL, which matches anything. */ + +static VEC (probe_and_objfile_s) * +collect_probes (char *objname, char *provider, char *probe_name, + const struct probe_ops *pops) +{ + struct objfile *objfile; + VEC (probe_and_objfile_s) *result = NULL; + struct cleanup *cleanup, *cleanup_temps; + regex_t obj_pat, prov_pat, probe_pat; + + cleanup = make_cleanup (VEC_cleanup (probe_and_objfile_s), &result); + + cleanup_temps = make_cleanup (null_cleanup, NULL); + compile_rx_or_error (&prov_pat, provider, _("Invalid provider regexp")); + compile_rx_or_error (&probe_pat, probe_name, _("Invalid probe regexp")); + compile_rx_or_error (&obj_pat, objname, _("Invalid object file regexp")); + + ALL_OBJFILES (objfile) + { + VEC (probe_p) *probes; + struct probe *probe; + int ix; + + if (! objfile->sf || ! objfile->sf->sym_probe_fns) + continue; + + if (objname) + { + if (regexec (&obj_pat, objfile->name, 0, NULL, 0) != 0) + continue; + } + + probes = objfile->sf->sym_probe_fns->sym_get_probes (objfile); + + for (ix = 0; VEC_iterate (probe_p, probes, ix, probe); ix++) + { + probe_and_objfile_s entry; + + if (pops != NULL && probe->pops != pops) + continue; + + if (provider + && regexec (&prov_pat, probe->provider, 0, NULL, 0) != 0) + continue; + + if (probe_name + && regexec (&probe_pat, probe->name, 0, NULL, 0) != 0) + continue; + + entry.probe = probe; + entry.objfile = objfile; + VEC_safe_push (probe_and_objfile_s, result, &entry); + } + } + + do_cleanups (cleanup_temps); + discard_cleanups (cleanup); + return result; +} + +/* A qsort comparison function for probe_and_objfile_s objects. */ + +static int +compare_entries (const void *a, const void *b) +{ + const probe_and_objfile_s *ea = a; + const probe_and_objfile_s *eb = b; + int v; + + v = strcmp (ea->probe->provider, eb->probe->provider); + if (v) + return v; + + v = strcmp (ea->probe->name, eb->probe->name); + if (v) + return v; + + if (ea->probe->address < eb->probe->address) + return -1; + if (ea->probe->address > eb->probe->address) + return 1; + + return strcmp (ea->objfile->name, eb->objfile->name); +} + +/* Helper function that generate entries in the ui_out table being + crafted by `info_probes_for_ops'. */ + +static void +gen_ui_out_table_header_info (VEC (probe_and_objfile_s) *probes, + const struct probe_ops *p) +{ + /* `headings' refers to the names of the columns when printing `info + probes'. */ + VEC (info_probe_column_s) *headings = NULL; + struct cleanup *c; + info_probe_column_s *column; + size_t headings_size; + int ix; + + gdb_assert (p != NULL); + + if (p->gen_info_probes_table_header == NULL + && p->gen_info_probes_table_values == NULL) + return; + + gdb_assert (p->gen_info_probes_table_header != NULL + && p->gen_info_probes_table_values != NULL); + + c = make_cleanup (VEC_cleanup (info_probe_column_s), &headings); + p->gen_info_probes_table_header (&headings); + + headings_size = VEC_length (info_probe_column_s, headings); + + for (ix = 0; + VEC_iterate (info_probe_column_s, headings, ix, column); + ++ix) + { + probe_and_objfile_s *entry; + int jx; + size_t size_max = strlen (column->print_name); + + for (jx = 0; VEC_iterate (probe_and_objfile_s, probes, jx, entry); ++jx) + { + /* `probe_fields' refers to the values of each new field that this + probe will display. */ + VEC (const_char_ptr) *probe_fields = NULL; + struct cleanup *c2; + const char *val; + int kx; + + if (entry->probe->pops != p) + continue; + + c2 = make_cleanup (VEC_cleanup (const_char_ptr), &probe_fields); + p->gen_info_probes_table_values (entry->probe, entry->objfile, + &probe_fields); + + gdb_assert (VEC_length (const_char_ptr, probe_fields) + == headings_size); + + for (kx = 0; VEC_iterate (const_char_ptr, probe_fields, kx, val); + ++kx) + { + /* It is valid to have a NULL value here, which means that the + backend does not have something to write and this particular + field should be skipped. */ + if (val == NULL) + continue; + + size_max = max (strlen (val), size_max); + } + do_cleanups (c2); + } + + ui_out_table_header (current_uiout, size_max, ui_left, + column->field_name, column->print_name); + } + + do_cleanups (c); +} + +/* Helper function to print extra information about a probe and an objfile + represented by ENTRY. */ + +static void +print_ui_out_info (probe_and_objfile_s *entry) +{ + int ix; + int j = 0; + /* `values' refers to the actual values of each new field in the output + of `info probe'. `headings' refers to the names of each new field. */ + VEC (const_char_ptr) *values = NULL; + VEC (info_probe_column_s) *headings = NULL; + info_probe_column_s *column; + struct cleanup *c; + + gdb_assert (entry != NULL); + gdb_assert (entry->probe != NULL); + gdb_assert (entry->probe->pops != NULL); + + if (entry->probe->pops->gen_info_probes_table_header == NULL + && entry->probe->pops->gen_info_probes_table_values == NULL) + return; + + gdb_assert (entry->probe->pops->gen_info_probes_table_header != NULL + && entry->probe->pops->gen_info_probes_table_values != NULL); + + c = make_cleanup (VEC_cleanup (info_probe_column_s), &headings); + make_cleanup (VEC_cleanup (const_char_ptr), &values); + + entry->probe->pops->gen_info_probes_table_header (&headings); + entry->probe->pops->gen_info_probes_table_values (entry->probe, + entry->objfile, &values); + + gdb_assert (VEC_length (info_probe_column_s, headings) + == VEC_length (const_char_ptr, values)); + + for (ix = 0; + VEC_iterate (info_probe_column_s, headings, ix, column); + ++ix) + { + const char *val = VEC_index (const_char_ptr, values, j++); + + if (val == NULL) + ui_out_field_skip (current_uiout, column->field_name); + else + ui_out_field_string (current_uiout, column->field_name, val); + } + + do_cleanups (c); +} + +/* Helper function that returns the number of extra fields which POPS will + need. */ + +static int +get_number_extra_fields (const struct probe_ops *pops) +{ + VEC (info_probe_column_s) *headings = NULL; + struct cleanup *c; + int n; + + if (pops->gen_info_probes_table_header == NULL) + return 0; + + c = make_cleanup (VEC_cleanup (info_probe_column_s), &headings); + pops->gen_info_probes_table_header (&headings); + + n = VEC_length (info_probe_column_s, headings); + + do_cleanups (c); + + return n; +} + +/* See comment in probe.h. */ + +void +info_probes_for_ops (char *arg, int from_tty, const struct probe_ops *pops) +{ + char *provider, *probe = NULL, *objname = NULL; + struct cleanup *cleanup = make_cleanup (null_cleanup, NULL); + VEC (probe_and_objfile_s) *items; + int i, any_found; + int ui_out_extra_fields = 0; + size_t size_addr; + size_t size_name = strlen ("Name"); + size_t size_objname = strlen ("Object"); + size_t size_provider = strlen ("Provider"); + probe_and_objfile_s *entry; + struct gdbarch *gdbarch = get_current_arch (); + + /* Do we have a `provider:probe:objfile' style of linespec? */ + provider = extract_arg (&arg); + if (provider) + { + make_cleanup (xfree, provider); + + probe = extract_arg (&arg); + if (probe) + { + make_cleanup (xfree, probe); + + objname = extract_arg (&arg); + if (objname) + make_cleanup (xfree, objname); + } + } + + if (pops == NULL) + { + const struct probe_ops *po; + int ix; + + /* If the probe_ops is NULL, it means the user has requested a "simple" + `info probes', i.e., she wants to print all information about all + probes. For that, we have to identify how many extra fields we will + need to add in the ui_out table. + + To do that, we iterate over all probe_ops, querying each one about + its extra fields, and incrementing `ui_out_extra_fields' to reflect + that number. */ + + for (ix = 0; VEC_iterate (probe_ops_cp, all_probe_ops, ix, po); ++ix) + ui_out_extra_fields += get_number_extra_fields (po); + } + else + ui_out_extra_fields = get_number_extra_fields (pops); + + items = collect_probes (objname, provider, probe, pops); + make_cleanup (VEC_cleanup (probe_and_objfile_s), &items); + make_cleanup_ui_out_table_begin_end (current_uiout, + 4 + ui_out_extra_fields, + VEC_length (probe_and_objfile_s, items), + "StaticProbes"); + + if (!VEC_empty (probe_and_objfile_s, items)) + qsort (VEC_address (probe_and_objfile_s, items), + VEC_length (probe_and_objfile_s, items), + sizeof (probe_and_objfile_s), compare_entries); + + /* What's the size of an address in our architecture? */ + size_addr = gdbarch_addr_bit (gdbarch) == 64 ? 18 : 10; + + /* Determining the maximum size of each field (`provider', `name' and + `objname'). */ + for (i = 0; VEC_iterate (probe_and_objfile_s, items, i, entry); ++i) + { + size_name = max (strlen (entry->probe->name), size_name); + size_provider = max (strlen (entry->probe->provider), size_provider); + size_objname = max (strlen (entry->objfile->name), size_objname); + } + + ui_out_table_header (current_uiout, size_provider, ui_left, "provider", + _("Provider")); + ui_out_table_header (current_uiout, size_name, ui_left, "name", _("Name")); + ui_out_table_header (current_uiout, size_addr, ui_left, "addr", _("Where")); + + if (pops == NULL) + { + const struct probe_ops *po; + int ix; + + /* We have to generate the table header for each new probe type that we + will print. */ + for (ix = 0; VEC_iterate (probe_ops_cp, all_probe_ops, ix, po); ++ix) + gen_ui_out_table_header_info (items, po); + } + else + gen_ui_out_table_header_info (items, pops); + + ui_out_table_header (current_uiout, size_objname, ui_left, "object", + _("Object")); + ui_out_table_body (current_uiout); + + for (i = 0; VEC_iterate (probe_and_objfile_s, items, i, entry); ++i) + { + struct cleanup *inner; + + inner = make_cleanup_ui_out_tuple_begin_end (current_uiout, "probe"); + + ui_out_field_string (current_uiout, "provider", entry->probe->provider); + ui_out_field_string (current_uiout, "name", entry->probe->name); + ui_out_field_core_addr (current_uiout, "addr", + get_objfile_arch (entry->objfile), + entry->probe->address); + + if (pops == NULL) + { + const struct probe_ops *po; + int ix; + + for (ix = 0; VEC_iterate (probe_ops_cp, all_probe_ops, ix, po); + ++ix) + if (entry->probe->pops == po) + print_ui_out_info (entry); + } + else + print_ui_out_info (entry); + + ui_out_field_string (current_uiout, "object", entry->objfile->name); + ui_out_text (current_uiout, "\n"); + + do_cleanups (inner); + } + + any_found = !VEC_empty (probe_and_objfile_s, items); + do_cleanups (cleanup); + + if (!any_found) + ui_out_message (current_uiout, 0, _("No probes matched.\n")); +} + +/* Implementation of the `info probes' command. */ + +static void +info_probes_command (char *arg, int from_tty) +{ + info_probes_for_ops (arg, from_tty, NULL); +} + +/* See comments in probe.h. */ + +struct value * +probe_safe_evaluate_at_pc (struct frame_info *frame, unsigned n) +{ + struct probe *probe; + struct objfile *objfile; + unsigned n_probes; + + probe = find_probe_by_pc (get_frame_pc (frame), &objfile); + if (!probe) + return NULL; + gdb_assert (objfile->sf && objfile->sf->sym_probe_fns); + + n_probes + = objfile->sf->sym_probe_fns->sym_get_probe_argument_count (objfile, + probe); + if (n >= n_probes) + return NULL; + + return objfile->sf->sym_probe_fns->sym_evaluate_probe_argument (objfile, + probe, + n); +} + +/* See comment in probe.h. */ + +const struct probe_ops * +probe_linespec_to_ops (const char **linespecp) +{ + int ix; + const struct probe_ops *probe_ops; + + for (ix = 0; VEC_iterate (probe_ops_cp, all_probe_ops, ix, probe_ops); ix++) + if (probe_ops->is_linespec (linespecp)) + return probe_ops; + + return NULL; +} + +/* See comment in probe.h. */ + +int +probe_is_linespec_by_keyword (const char **linespecp, const char *const *keywords) +{ + const char *s = *linespecp; + const char *const *csp; + + for (csp = keywords; *csp; csp++) + { + const char *keyword = *csp; + size_t len = strlen (keyword); + + if (strncmp (s, keyword, len) == 0 && isspace (s[len])) + { + *linespecp += len + 1; + return 1; + } + } + + return 0; +} + +/* Implementation of `is_linespec' method for `struct probe_ops'. */ + +static int +probe_any_is_linespec (const char **linespecp) +{ + static const char *const keywords[] = { "-p", "-probe", NULL }; + + return probe_is_linespec_by_keyword (linespecp, keywords); +} + +/* Dummy method used for `probe_ops_any'. */ + +static void +probe_any_get_probes (VEC (probe_p) **probesp, struct objfile *objfile) +{ + /* No probes can be provided by this dummy backend. */ +} + +/* Operations associated with a generic probe. */ + +const struct probe_ops probe_ops_any = +{ + probe_any_is_linespec, + probe_any_get_probes, +}; + +/* See comments in probe.h. */ + +struct cmd_list_element ** +info_probes_cmdlist_get (void) +{ + static struct cmd_list_element *info_probes_cmdlist; + + if (info_probes_cmdlist == NULL) + add_prefix_cmd ("probes", class_info, info_probes_command, + _("\ +Show available static probes.\n\ +Usage: info probes [all|TYPE [ARGS]]\n\ +TYPE specifies the type of the probe, and can be one of the following:\n\ + - stap\n\ +If you specify TYPE, there may be additional arguments needed by the\n\ +subcommand.\n\ +If you do not specify any argument, or specify `all', then the command\n\ +will show information about all types of probes."), + &info_probes_cmdlist, "info probes ", + 0/*allow-unknown*/, &infolist); + + return &info_probes_cmdlist; +} + +VEC (probe_ops_cp) *all_probe_ops; + +void _initialize_probe (void); + +void +_initialize_probe (void) +{ + VEC_safe_push (probe_ops_cp, all_probe_ops, &probe_ops_any); + + add_cmd ("all", class_info, info_probes_command, + _("\ +Show information about all type of probes."), + info_probes_cmdlist_get ()); +} diff --git a/gdb/probe.h b/gdb/probe.h new file mode 100644 index 0000000..8d44ca2 --- /dev/null +++ b/gdb/probe.h @@ -0,0 +1,221 @@ +/* Generic SDT probe support for GDB. + + Copyright (C) 2012 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#if !defined (PROBE_H) +#define PROBE_H 1 + +#include "gdb_vecs.h" + +struct linespec_result; + +/* Structure useful for passing the header names in the method + `gen_ui_out_table_header'. */ + +struct info_probe_column + { + /* The internal name of the field. This string cannot be capitalized nor + localized, e.g., "extra_field". */ + + const char *field_name; + + /* The field name to be printed in the `info probes' command. This + string can be capitalized and localized, e.g., _("Extra Field"). */ + const char *print_name; + }; + +typedef struct info_probe_column info_probe_column_s; +DEF_VEC_O (info_probe_column_s); + +/* Operations associated with a probe. */ + +struct probe_ops + { + /* Method responsible for verifying if LINESPECP is a valid linespec for + a probe breakpoint. It should return 1 if it is, or zero if it is not. + It also should update LINESPECP in order to discard the breakpoint + option associated with this linespec. For example, if the option is + `-probe', and the LINESPECP is `-probe abc', the function should + return 1 and set LINESPECP to `abc'. */ + + int (*is_linespec) (const char **linespecp); + + /* Function that should fill PROBES with known probes from OBJFILE. */ + + void (*get_probes) (VEC (probe_p) **probes, struct objfile *objfile); + + /* Function used to relocate addresses from PROBE according to some DELTA + provided. */ + + void (*relocate) (struct probe *probe, CORE_ADDR delta); + + /* Return the number of arguments of PROBE. */ + + unsigned (*get_probe_argument_count) (struct probe *probe, + struct objfile *objfile); + + /* Evaluate the Nth argument from the PROBE, returning a value + corresponding to it. The argument number is represented N. */ + + struct value *(*evaluate_probe_argument) (struct probe *probe, + struct objfile *objfile, + unsigned n); + + /* Compile the Nth argument of the PROBE to an agent expression. + The argument number is represented by N. */ + + void (*compile_to_ax) (struct probe *probe, struct objfile *objfile, + struct agent_expr *aexpr, + struct axs_value *axs_value, unsigned n); + + /* Set the semaphore associated with the PROBE. This function only makes + sense if the probe has a concept of semaphore associated to a + probe. */ + + void (*set_semaphore) (struct probe *probe, struct gdbarch *gdbarch); + + /* Clear the semaphore associated with the PROBE. This function only + makes sense if the probe has a concept of semaphore associated to + a probe. */ + + void (*clear_semaphore) (struct probe *probe, struct gdbarch *gdbarch); + + /* Function called to destroy PROBE's specific data. This function + shall not free PROBE itself. */ + + void (*destroy) (struct probe *probe); + + /* Function responsible for providing the extra fields that will be + printed in the `info probes' command. It should fill HEADS + with whatever extra fields it needs. If the backend doesn't need + to print extra fields, it can set this method to NULL. */ + + void (*gen_info_probes_table_header) (VEC (info_probe_column_s) **heads); + + /* Function that will fill VALUES with the values of the extra fields + to be printed for PROBE and OBJFILE. If the backend implements + the `gen_ui_out_table_header' method, then it should implement + this method as well. The backend should also guarantee that the + order and the number of values in the vector is exactly the same + as the order of the extra fields provided in the method + `gen_ui_out_table_header'. If a certain field is to be skipped + when printing the information, you can push a NULL value in that + position in the vector. */ + + void (*gen_info_probes_table_values) (struct probe *probe, + struct objfile *objfile, + VEC (const_char_ptr) **values); + }; + +/* Definition of a vector of probe_ops. */ + +typedef const struct probe_ops *probe_ops_cp; +DEF_VEC_P (probe_ops_cp); +extern VEC (probe_ops_cp) *all_probe_ops; + +/* The probe_ops associated with the generic probe. */ + +extern const struct probe_ops probe_ops_any; + +/* Helper function that, given KEYWORDS, iterate over it trying to match + each keyword with LINESPECP. If it succeeds, it updates the LINESPECP + pointer and returns 1. Otherwise, nothing is done to LINESPECP and zero + is returned. */ + +extern int probe_is_linespec_by_keyword (const char **linespecp, + const char *const *keywords); + +/* Return specific PROBE_OPS * matching *LINESPECP and possibly updating + *LINESPECP to skip its "-probe-type " prefix. Return &probe_ops_any if + *LINESPECP matches "-probe ", that is any unspecific probe. Return NULL if + *LINESPECP is not identified as any known probe type, *LINESPECP is not + modified in such case. */ + +extern const struct probe_ops *probe_linespec_to_ops (const char **linespecp); + +/* The probe itself. The struct contains generic information about the + probe, and then some specific information which should be stored in + the `probe_info' field. */ + +struct probe + { + /* The operations associated with this probe. */ + const struct probe_ops *pops; + + /* The name of the probe. */ + const char *name; + + /* The provider of the probe. It generally defaults to the name of + the objfile which contains the probe. */ + const char *provider; + + /* The address where the probe is inserted. */ + CORE_ADDR address; + }; + +/* A helper for linespec that decodes a probe specification. It returns a + symtabs_and_lines object and updates *ARGPTR or throws an error. The + argument PTYPE specifies the type of the probe(s) to be parsed. */ + +extern struct symtabs_and_lines parse_probes (char **argptr, + struct linespec_result *canon); + +/* Helper function to register the proper probe_ops to a newly created probe. + This function is mainly called from `sym_get_probes'. */ + +extern void register_probe_ops (struct probe *probe); + +/* Given a PC, find an associated probe with type PTYPE. If a probe is + found, set *OBJFILE_OUT to the probe's objfile, and return the + probe. If no probe is found, return NULL. */ + +extern struct probe *find_probe_by_pc (CORE_ADDR pc, + struct objfile **objfile_out); + +/* Search OBJFILE for a probe with the given PROVIDER, NAME and PTYPE. + Return a VEC of all probes that were found. If no matching probe + is found, return NULL. The caller must free the VEC. */ + +extern VEC (probe_p) *find_probes_in_objfile (struct objfile *objfile, + const char *provider, + const char *name); + +/* Generate a `info probes' command output for probe_ops represented by + POPS. If POPS is NULL it considers any probes types. It is a helper + function that can be used by the probe backends to print their + `info probe TYPE'. */ + +extern void info_probes_for_ops (char *arg, int from_tty, + const struct probe_ops *pops); + +/* Return the `cmd_list_element' associated with the `info probes' command, + or create a new one if it doesn't exist. Helper function that serves the + purpose of avoiding the case of a backend using the `cmd_list_element' + associated with `info probes', without having it registered yet. */ + +extern struct cmd_list_element **info_probes_cmdlist_get (void); + +/* A convenience function that finds a probe at the PC in FRAME and + evaluates argument N, with 0 <= N < number_of_args. If there is no + probe at that location, or if the probe does not have enough arguments, + this returns NULL. */ + +extern struct value *probe_safe_evaluate_at_pc (struct frame_info *frame, + unsigned n); + +#endif /* !defined (PROBE_H) */ diff --git a/gdb/s390-tdep.c b/gdb/s390-tdep.c index ac0c526..038a3ce 100644 --- a/gdb/s390-tdep.c +++ b/gdb/s390-tdep.c @@ -45,6 +45,13 @@ #include "linux-tdep.h" #include "s390-tdep.h" +#include "stap-probe.h" +#include "ax.h" +#include "ax-gdb.h" +#include "user-regs.h" +#include "cli/cli-utils.h" +#include <ctype.h> + #include "features/s390-linux32.c" #include "features/s390-linux32v1.c" #include "features/s390-linux32v2.c" @@ -55,7 +62,6 @@ #include "features/s390x-linux64v1.c" #include "features/s390x-linux64v2.c" - /* The tdep structure. */ struct gdbarch_tdep @@ -2953,6 +2959,18 @@ s390_address_class_name_to_type_flags (struct gdbarch *gdbarch, return 0; } +/* Implementation of `gdbarch_stap_is_single_operand', as defined in + gdbarch.h. */ + +static int +s390_stap_is_single_operand (struct gdbarch *gdbarch, const char *s) +{ + return ((isdigit (*s) && s[1] == '(' && s[2] == '%') /* Displacement + or indirection. */ + || *s == '%' /* Register access. */ + || isdigit (*s)); /* Literal number. */ +} + /* Set up gdbarch struct. */ static struct gdbarch * @@ -3283,6 +3301,12 @@ s390_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) set_gdbarch_get_siginfo_type (gdbarch, linux_get_siginfo_type); + /* SystemTap functions. */ + set_gdbarch_stap_register_prefix (gdbarch, "%"); + set_gdbarch_stap_register_indirection_prefix (gdbarch, "("); + set_gdbarch_stap_register_indirection_suffix (gdbarch, ")"); + set_gdbarch_stap_is_single_operand (gdbarch, s390_stap_is_single_operand); + return gdbarch; } diff --git a/gdb/somread.c b/gdb/somread.c index e621cba..19a15e2 100644 --- a/gdb/somread.c +++ b/gdb/somread.c @@ -427,6 +427,7 @@ static const struct sym_fns som_sym_fns = default_symfile_segments, /* Get segment information from a file. */ NULL, default_symfile_relocate, /* Relocate a debug section. */ + NULL, /* sym_get_probes */ &psym_functions }; diff --git a/gdb/stap-probe.c b/gdb/stap-probe.c new file mode 100644 index 0000000..13ba281 --- /dev/null +++ b/gdb/stap-probe.c @@ -0,0 +1,1558 @@ +/* SystemTap probe support for GDB. + + Copyright (C) 2012 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include "defs.h" +#include "stap-probe.h" +#include "probe.h" +#include "vec.h" +#include "ui-out.h" +#include "objfiles.h" +#include "arch-utils.h" +#include "command.h" +#include "gdbcmd.h" +#include "filenames.h" +#include "value.h" +#include "exceptions.h" +#include "ax.h" +#include "ax-gdb.h" +#include "complaints.h" +#include "cli/cli-utils.h" +#include "linespec.h" +#include "user-regs.h" +#include "parser-defs.h" +#include "language.h" +#include "elf-bfd.h" + +#include <ctype.h> + +/* The name of the SystemTap section where we will find information about + the probes. */ + +#define STAP_BASE_SECTION_NAME ".stapsdt.base" + +/* Forward declaration. */ + +static const struct probe_ops stap_probe_ops; + +/* Should we display debug information for the probe's argument expression + parsing? */ + +static int stap_expression_debug = 0; + +/* The various possibilities of bitness defined for a probe's argument. + + The relationship is: + + - STAP_ARG_BITNESS_UNDEFINED: The user hasn't specified the bitness. + - STAP_ARG_BITNESS_32BIT_UNSIGNED: argument string starts with `4@'. + - STAP_ARG_BITNESS_32BIT_SIGNED: argument string starts with `-4@'. + - STAP_ARG_BITNESS_64BIT_UNSIGNED: argument string starts with `8@'. + - STAP_ARG_BITNESS_64BIT_SIGNED: argument string starts with `-8@'. */ + +enum stap_arg_bitness +{ + STAP_ARG_BITNESS_UNDEFINED, + STAP_ARG_BITNESS_32BIT_UNSIGNED, + STAP_ARG_BITNESS_32BIT_SIGNED, + STAP_ARG_BITNESS_64BIT_UNSIGNED, + STAP_ARG_BITNESS_64BIT_SIGNED, +}; + +/* The following structure represents a single argument for the probe. */ + +struct stap_probe_arg +{ + /* The bitness of this argument. */ + enum stap_arg_bitness bitness; + + /* The corresponding `struct type *' to the bitness. */ + struct type *atype; + + /* The argument converted to an internal GDB expression. */ + struct expression *aexpr; +}; + +typedef struct stap_probe_arg stap_probe_arg_s; +DEF_VEC_O (stap_probe_arg_s); + +struct stap_probe +{ + /* Generic information about the probe. This shall be the first element + of this struct, in order to maintain binary compatibility with the + `struct probe' and be able to fully abstract it. */ + struct probe p; + + /* If the probe has a semaphore associated, then this is the value of + it. */ + CORE_ADDR sem_addr; + + unsigned int args_parsed : 1; + union + { + const char *text; + + /* Information about each argument. This is an array of `stap_probe_arg', + with each entry representing one argument. */ + VEC (stap_probe_arg_s) *vec; + } + args_u; +}; + +/* When parsing the arguments, we have to establish different precedences + for the various kinds of asm operators. This enumeration represents those + precedences. + + This logic behind this is available at + <http://sourceware.org/binutils/docs/as/Infix-Ops.html#Infix-Ops>, or using + the command "info '(as)Infix Ops'". */ + +enum stap_operand_prec +{ + /* Lowest precedence, used for non-recognized operands or for the beginning + of the parsing process. */ + STAP_OPERAND_PREC_NONE = 0, + + /* Precedence of logical OR. */ + STAP_OPERAND_PREC_LOGICAL_OR, + + /* Precedence of logical AND. */ + STAP_OPERAND_PREC_LOGICAL_AND, + + /* Precedence of additive (plus, minus) and comparative (equal, less, + greater-than, etc) operands. */ + STAP_OPERAND_PREC_ADD_CMP, + + /* Precedence of bitwise operands (bitwise OR, XOR, bitwise AND, + logical NOT). */ + STAP_OPERAND_PREC_BITWISE, + + /* Precedence of multiplicative operands (multiplication, division, + remainder, left shift and right shift). */ + STAP_OPERAND_PREC_MUL +}; + +static void stap_parse_argument_1 (struct stap_parse_info *p, int has_lhs, + enum stap_operand_prec prec); + +static void stap_parse_argument_conditionally (struct stap_parse_info *p); + +/* Returns 1 if *S is an operator, zero otherwise. */ + +static int stap_is_operator (char op); + +static void +show_stapexpressiondebug (struct ui_file *file, int from_tty, + struct cmd_list_element *c, const char *value) +{ + fprintf_filtered (file, _("SystemTap Probe expression debugging is %s.\n"), + value); +} + +/* Returns the operator precedence level of OP, or STAP_OPERAND_PREC_NONE + if the operator code was not recognized. */ + +static enum stap_operand_prec +stap_get_operator_prec (enum exp_opcode op) +{ + switch (op) + { + case BINOP_LOGICAL_OR: + return STAP_OPERAND_PREC_LOGICAL_OR; + + case BINOP_LOGICAL_AND: + return STAP_OPERAND_PREC_LOGICAL_AND; + + case BINOP_ADD: + case BINOP_SUB: + case BINOP_EQUAL: + case BINOP_NOTEQUAL: + case BINOP_LESS: + case BINOP_LEQ: + case BINOP_GTR: + case BINOP_GEQ: + return STAP_OPERAND_PREC_ADD_CMP; + + case BINOP_BITWISE_IOR: + case BINOP_BITWISE_AND: + case BINOP_BITWISE_XOR: + case UNOP_LOGICAL_NOT: + return STAP_OPERAND_PREC_BITWISE; + + case BINOP_MUL: + case BINOP_DIV: + case BINOP_REM: + case BINOP_LSH: + case BINOP_RSH: + return STAP_OPERAND_PREC_MUL; + + default: + return STAP_OPERAND_PREC_NONE; + } +} + +/* Given S, read the operator in it and fills the OP pointer with its code. + Return 1 on success, zero if the operator was not recognized. */ + +static int +stap_get_opcode (const char **s, enum exp_opcode *op) +{ + const char c = **s; + int ret = 1; + + *s += 1; + + switch (c) + { + case '*': + *op = BINOP_MUL; + break; + + case '/': + *op = BINOP_DIV; + break; + + case '%': + *op = BINOP_REM; + break; + + case '<': + *op = BINOP_LESS; + if (**s == '<') + { + *s += 1; + *op = BINOP_LSH; + } + else if (**s == '=') + { + *s += 1; + *op = BINOP_LEQ; + } + else if (**s == '>') + { + *s += 1; + *op = BINOP_NOTEQUAL; + } + break; + + case '>': + *op = BINOP_GTR; + if (**s == '>') + { + *s += 1; + *op = BINOP_RSH; + } + else if (**s == '=') + { + *s += 1; + *op = BINOP_GEQ; + } + break; + + case '|': + *op = BINOP_BITWISE_IOR; + if (**s == '|') + { + *s += 1; + *op = BINOP_LOGICAL_OR; + } + break; + + case '&': + *op = BINOP_BITWISE_AND; + if (**s == '&') + { + *s += 1; + *op = BINOP_LOGICAL_AND; + } + break; + + case '^': + *op = BINOP_BITWISE_XOR; + break; + + case '!': + *op = UNOP_LOGICAL_NOT; + break; + + case '+': + *op = BINOP_ADD; + break; + + case '-': + *op = BINOP_SUB; + break; + + case '=': + if (**s != '=') + { + ret = 0; + break; + } + *op = BINOP_EQUAL; + break; + + default: + /* We didn't find any operator. */ + *s -= 1; + return 0; + } + + return ret; +} + +/* Given the bitness of the argument, represented by B, return the + corresponding `struct type *'. */ + +static struct type * +stap_get_expected_argument_type (struct gdbarch *gdbarch, + enum stap_arg_bitness b) +{ + switch (b) + { + case STAP_ARG_BITNESS_UNDEFINED: + if (gdbarch_addr_bit (gdbarch) == 32) + return builtin_type (gdbarch)->builtin_uint32; + else + return builtin_type (gdbarch)->builtin_uint64; + + case STAP_ARG_BITNESS_32BIT_SIGNED: + return builtin_type (gdbarch)->builtin_int32; + + case STAP_ARG_BITNESS_32BIT_UNSIGNED: + return builtin_type (gdbarch)->builtin_uint32; + + case STAP_ARG_BITNESS_64BIT_SIGNED: + return builtin_type (gdbarch)->builtin_int64; + + case STAP_ARG_BITNESS_64BIT_UNSIGNED: + return builtin_type (gdbarch)->builtin_uint64; + + default: + internal_error (__FILE__, __LINE__, + _("Undefined bitness for probe.")); + break; + } +} + +/* Function responsible for parsing a register operand according to + SystemTap parlance. Assuming: + + RP = register prefix + RS = register suffix + RIP = register indirection prefix + RIS = register indirection suffix + + Then a register operand can be: + + [RIP] [RP] REGISTER [RS] [RIS] + + This function takes care of a register's indirection, displacement and + direct access. It also takes into consideration the fact that some + registers are named differently inside and outside GDB, e.g., PPC's + general-purpose registers are represented by integers in the assembly + language (e.g., `15' is the 15th general-purpose register), but inside + GDB they have a prefix (the letter `r') appended. */ + +static void +stap_parse_register_operand (struct stap_parse_info *p) +{ + /* Simple flag to indicate whether we have seen a minus signal before + certain number. */ + int got_minus = 0; + + /* Flags to indicate whether this register access is being displaced and/or + indirected. */ + int disp_p = 0, indirect_p = 0; + struct gdbarch *gdbarch = p->gdbarch; + + /* Needed to generate the register name as a part of an expression. */ + struct stoken str; + + /* Variables used to extract the register name from the probe's + argument. */ + const char *start; + char *regname; + int len; + + /* Prefixes for the parser. */ + const char *reg_prefix = gdbarch_stap_register_prefix (gdbarch); + const char *reg_ind_prefix + = gdbarch_stap_register_indirection_prefix (gdbarch); + const char *gdb_reg_prefix = gdbarch_stap_gdb_register_prefix (gdbarch); + int reg_prefix_len = reg_prefix ? strlen (reg_prefix) : 0; + int reg_ind_prefix_len = reg_ind_prefix ? strlen (reg_ind_prefix) : 0; + int gdb_reg_prefix_len = gdb_reg_prefix ? strlen (gdb_reg_prefix) : 0; + + /* Suffixes for the parser. */ + const char *reg_suffix = gdbarch_stap_register_suffix (gdbarch); + const char *reg_ind_suffix + = gdbarch_stap_register_indirection_suffix (gdbarch); + const char *gdb_reg_suffix = gdbarch_stap_gdb_register_suffix (gdbarch); + int reg_suffix_len = reg_suffix ? strlen (reg_suffix) : 0; + int reg_ind_suffix_len = reg_ind_suffix ? strlen (reg_ind_suffix) : 0; + int gdb_reg_suffix_len = gdb_reg_suffix ? strlen (gdb_reg_suffix) : 0; + + /* Checking for a displacement argument. */ + if (*p->arg == '+') + { + /* If it's a plus sign, we don't need to do anything, just advance the + pointer. */ + ++p->arg; + } + + if (*p->arg == '-') + { + got_minus = 1; + ++p->arg; + } + + if (isdigit (*p->arg)) + { + /* The value of the displacement. */ + long displacement; + + disp_p = 1; + displacement = strtol (p->arg, (char **) &p->arg, 10); + + /* Generating the expression for the displacement. */ + write_exp_elt_opcode (OP_LONG); + write_exp_elt_type (builtin_type (gdbarch)->builtin_long); + write_exp_elt_longcst (displacement); + write_exp_elt_opcode (OP_LONG); + if (got_minus) + write_exp_elt_opcode (UNOP_NEG); + } + + /* Getting rid of register indirection prefix. */ + if (reg_ind_prefix + && strncmp (p->arg, reg_ind_prefix, reg_ind_prefix_len) == 0) + { + indirect_p = 1; + p->arg += reg_ind_prefix_len; + } + + if (disp_p && !indirect_p) + error (_("Invalid register displacement syntax on expression `%s'."), + p->saved_arg); + + /* Getting rid of register prefix. */ + if (reg_prefix && strncmp (p->arg, reg_prefix, reg_prefix_len) == 0) + p->arg += reg_prefix_len; + + /* Now we should have only the register name. Let's extract it and get + the associated number. */ + start = p->arg; + + /* We assume the register name is composed by letters and numbers. */ + while (isalnum (*p->arg)) + ++p->arg; + + len = p->arg - start; + + regname = alloca (len + gdb_reg_prefix_len + gdb_reg_suffix_len + 1); + regname[0] = '\0'; + + /* We only add the GDB's register prefix/suffix if we are dealing with + a numeric register. */ + if (gdb_reg_prefix && isdigit (*start)) + { + strncpy (regname, gdb_reg_prefix, gdb_reg_prefix_len); + strncpy (regname + gdb_reg_prefix_len, start, len); + + if (gdb_reg_suffix) + strncpy (regname + gdb_reg_prefix_len + len, + gdb_reg_suffix, gdb_reg_suffix_len); + + len += gdb_reg_prefix_len + gdb_reg_suffix_len; + } + else + strncpy (regname, start, len); + + regname[len] = '\0'; + + /* Is this a valid register name? */ + if (user_reg_map_name_to_regnum (gdbarch, regname, len) == -1) + error (_("Invalid register name `%s' on expression `%s'."), + regname, p->saved_arg); + + write_exp_elt_opcode (OP_REGISTER); + str.ptr = regname; + str.length = len; + write_exp_string (str); + write_exp_elt_opcode (OP_REGISTER); + + if (indirect_p) + { + if (disp_p) + write_exp_elt_opcode (BINOP_ADD); + + /* Casting to the expected type. */ + write_exp_elt_opcode (UNOP_CAST); + write_exp_elt_type (lookup_pointer_type (p->arg_type)); + write_exp_elt_opcode (UNOP_CAST); + + write_exp_elt_opcode (UNOP_IND); + } + + /* Getting rid of the register name suffix. */ + if (reg_suffix) + { + if (strncmp (p->arg, reg_suffix, reg_suffix_len) != 0) + error (_("Missing register name suffix `%s' on expression `%s'."), + reg_suffix, p->saved_arg); + + p->arg += reg_suffix_len; + } + + /* Getting rid of the register indirection suffix. */ + if (indirect_p && reg_ind_suffix) + { + if (strncmp (p->arg, reg_ind_suffix, reg_ind_suffix_len) != 0) + error (_("Missing indirection suffix `%s' on expression `%s'."), + reg_ind_suffix, p->saved_arg); + + p->arg += reg_ind_suffix_len; + } +} + +/* This function is responsible for parsing a single operand. + + A single operand can be: + + - an unary operation (e.g., `-5', `~2', or even with subexpressions + like `-(2 + 1)') + - a register displacement, which will be treated as a register + operand (e.g., `-4(%eax)' on x86) + - a numeric constant, or + - a register operand (see function `stap_parse_register_operand') + + The function also calls special-handling functions to deal with + unrecognized operands, allowing arch-specific parsers to be + created. */ + +static void +stap_parse_single_operand (struct stap_parse_info *p) +{ + struct gdbarch *gdbarch = p->gdbarch; + + /* Prefixes for the parser. */ + const char *const_prefix = gdbarch_stap_integer_prefix (gdbarch); + const char *reg_prefix = gdbarch_stap_register_prefix (gdbarch); + const char *reg_ind_prefix + = gdbarch_stap_register_indirection_prefix (gdbarch); + int const_prefix_len = const_prefix ? strlen (const_prefix) : 0; + int reg_prefix_len = reg_prefix ? strlen (reg_prefix) : 0; + int reg_ind_prefix_len = reg_ind_prefix ? strlen (reg_ind_prefix) : 0; + + /* Suffixes for the parser. */ + const char *const_suffix = gdbarch_stap_integer_suffix (gdbarch); + const char *reg_suffix = gdbarch_stap_register_suffix (gdbarch); + const char *reg_ind_suffix + = gdbarch_stap_register_indirection_suffix (gdbarch); + int const_suffix_len = const_suffix ? strlen (const_suffix) : 0; + int reg_suffix_len = reg_suffix ? strlen (reg_suffix) : 0; + int reg_ind_suffix_len = reg_ind_suffix ? strlen (reg_ind_suffix) : 0; + + /* We first try to parse this token as a "special token". */ + if (gdbarch_stap_parse_special_token_p (gdbarch)) + { + int ret = gdbarch_stap_parse_special_token (gdbarch, p); + + if (ret) + { + /* If the return value of the above function is not zero, + it means it successfully parsed the special token. + + If it is NULL, we try to parse it using our method. */ + return; + } + } + + if (*p->arg == '-' || *p->arg == '~' || *p->arg == '+') + { + char c = *p->arg; + int number; + + /* We use this variable to do a lookahead. */ + const char *tmp = p->arg; + + ++tmp; + + /* This is an unary operation. Here is a list of allowed tokens + here: + + - numeric literal; + - number (from register displacement) + - subexpression (beginning with `(') + + We handle the register displacement here, and the other cases + recursively. */ + if (p->inside_paren_p) + tmp = skip_spaces_const (tmp); + + if (isdigit (*tmp)) + number = strtol (tmp, (char **) &tmp, 10); + + if (!reg_ind_prefix + || strncmp (tmp, reg_ind_prefix, reg_ind_prefix_len) != 0) + { + /* This is not a displacement. We skip the operator, and deal + with it later. */ + ++p->arg; + stap_parse_argument_conditionally (p); + if (c == '-') + write_exp_elt_opcode (UNOP_NEG); + else if (c == '~') + write_exp_elt_opcode (UNOP_COMPLEMENT); + } + else + { + /* If we are here, it means it is a displacement. The only + operations allowed here are `-' and `+'. */ + if (c == '~') + error (_("Invalid operator `%c' for register displacement " + "on expression `%s'."), c, p->saved_arg); + + stap_parse_register_operand (p); + } + } + else if (isdigit (*p->arg)) + { + /* A temporary variable, needed for lookahead. */ + const char *tmp = p->arg; + long number; + + /* We can be dealing with a numeric constant (if `const_prefix' is + NULL), or with a register displacement. */ + number = strtol (tmp, (char **) &tmp, 10); + + if (p->inside_paren_p) + tmp = skip_spaces_const (tmp); + if (!const_prefix && reg_ind_prefix + && strncmp (tmp, reg_ind_prefix, reg_ind_prefix_len) != 0) + { + /* We are dealing with a numeric constant. */ + write_exp_elt_opcode (OP_LONG); + write_exp_elt_type (builtin_type (gdbarch)->builtin_long); + write_exp_elt_longcst (number); + write_exp_elt_opcode (OP_LONG); + + p->arg = tmp; + + if (const_suffix) + { + if (strncmp (p->arg, const_suffix, const_suffix_len) == 0) + p->arg += const_suffix_len; + else + error (_("Invalid constant suffix on expression `%s'."), + p->saved_arg); + } + } + else if (reg_ind_prefix + && strncmp (tmp, reg_ind_prefix, reg_ind_prefix_len) == 0) + stap_parse_register_operand (p); + else + error (_("Unknown numeric token on expression `%s'."), + p->saved_arg); + } + else if (const_prefix + && strncmp (p->arg, const_prefix, const_prefix_len) == 0) + { + /* We are dealing with a numeric constant. */ + long number; + + p->arg += const_prefix_len; + number = strtol (p->arg, (char **) &p->arg, 10); + + write_exp_elt_opcode (OP_LONG); + write_exp_elt_type (builtin_type (gdbarch)->builtin_long); + write_exp_elt_longcst (number); + write_exp_elt_opcode (OP_LONG); + + if (const_suffix) + { + if (strncmp (p->arg, const_suffix, const_suffix_len) == 0) + p->arg += const_suffix_len; + else + error (_("Invalid constant suffix on expression `%s'."), + p->saved_arg); + } + } + else if ((reg_prefix + && strncmp (p->arg, reg_prefix, reg_prefix_len) == 0) + || (reg_ind_prefix + && strncmp (p->arg, reg_ind_prefix, reg_ind_prefix_len) == 0)) + stap_parse_register_operand (p); + else + error (_("Operator `%c' not recognized on expression `%s'."), + *p->arg, p->saved_arg); +} + +/* This function parses an argument conditionally, based on single or + non-single operands. A non-single operand would be a parenthesized + expression (e.g., `(2 + 1)'), and a single operand is anything that + starts with `-', `~', `+' (i.e., unary operators), a digit, or + something recognized by `gdbarch_stap_is_single_operand'. */ + +static void +stap_parse_argument_conditionally (struct stap_parse_info *p) +{ + if (*p->arg == '-' || *p->arg == '~' || *p->arg == '+' /* Unary. */ + || isdigit (*p->arg) + || gdbarch_stap_is_single_operand (p->gdbarch, p->arg)) + stap_parse_single_operand (p); + else if (*p->arg == '(') + { + /* We are dealing with a parenthesized operand. It means we + have to parse it as it was a separate expression, without + left-side or precedence. */ + ++p->arg; + p->arg = skip_spaces_const (p->arg); + ++p->inside_paren_p; + + stap_parse_argument_1 (p, 0, STAP_OPERAND_PREC_NONE); + + --p->inside_paren_p; + if (*p->arg != ')') + error (_("Missign close-paren on expression `%s'."), + p->saved_arg); + + ++p->arg; + if (p->inside_paren_p) + p->arg = skip_spaces_const (p->arg); + } + else + error (_("Cannot parse expression `%s'."), p->saved_arg); +} + +/* Helper function for `stap_parse_argument'. Please, see its comments to + better understand what this function does. */ + +static void +stap_parse_argument_1 (struct stap_parse_info *p, int has_lhs, + enum stap_operand_prec prec) +{ + /* This is an operator-precedence parser. + + We work with left- and right-sides of expressions, and + parse them depending on the precedence of the operators + we find. */ + + if (p->inside_paren_p) + p->arg = skip_spaces_const (p->arg); + + if (!has_lhs) + { + /* We were called without a left-side, either because this is the + first call, or because we were called to parse a parenthesized + expression. It doesn't really matter; we have to parse the + left-side in order to continue the process. */ + stap_parse_argument_conditionally (p); + } + + /* Start to parse the right-side, and to "join" left and right sides + depending on the operation specified. + + This loop shall continue until we run out of characters in the input, + or until we find a close-parenthesis, which means that we've reached + the end of a sub-expression. */ + while (p->arg && *p->arg && *p->arg != ')' && !isspace (*p->arg)) + { + const char *tmp_exp_buf; + enum exp_opcode opcode; + enum stap_operand_prec cur_prec; + + if (!stap_is_operator (*p->arg)) + error (_("Invalid operator `%c' on expression `%s'."), *p->arg, + p->saved_arg); + + /* We have to save the current value of the expression buffer because + the `stap_get_opcode' modifies it in order to get the current + operator. If this operator's precedence is lower than PREC, we + should return and not advance the expression buffer pointer. */ + tmp_exp_buf = p->arg; + stap_get_opcode (&tmp_exp_buf, &opcode); + + cur_prec = stap_get_operator_prec (opcode); + if (cur_prec < prec) + { + /* If the precedence of the operator that we are seeing now is + lower than the precedence of the first operator seen before + this parsing process began, it means we should stop parsing + and return. */ + break; + } + + p->arg = tmp_exp_buf; + if (p->inside_paren_p) + p->arg = skip_spaces_const (p->arg); + + /* Parse the right-side of the expression. */ + stap_parse_argument_conditionally (p); + + /* While we still have operators, try to parse another + right-side, but using the current right-side as a left-side. */ + while (*p->arg && stap_is_operator (*p->arg)) + { + enum exp_opcode lookahead_opcode; + enum stap_operand_prec lookahead_prec; + + /* Saving the current expression buffer position. The explanation + is the same as above. */ + tmp_exp_buf = p->arg; + stap_get_opcode (&tmp_exp_buf, &lookahead_opcode); + lookahead_prec = stap_get_operator_prec (lookahead_opcode); + + if (lookahead_prec <= prec) + { + /* If we are dealing with an operator whose precedence is lower + than the first one, just abandon the attempt. */ + break; + } + + /* Parse the right-side of the expression, but since we already + have a left-side at this point, set `has_lhs' to 1. */ + stap_parse_argument_1 (p, 1, lookahead_prec); + } + + write_exp_elt_opcode (opcode); + } +} + +/* Parse a probe's argument. + + Assuming that: + + LP = literal integer prefix + LS = literal integer suffix + + RP = register prefix + RS = register suffix + + RIP = register indirection prefix + RIS = register indirection suffix + + This routine assumes that arguments' tokens are of the form: + + - [LP] NUMBER [LS] + - [RP] REGISTER [RS] + - [RIP] [RP] REGISTER [RS] [RIS] + - If we find a number without LP, we try to parse it as a literal integer + constant (if LP == NULL), or as a register displacement. + - We count parenthesis, and only skip whitespaces if we are inside them. + - If we find an operator, we skip it. + + This function can also call a special function that will try to match + unknown tokens. It will return 1 if the argument has been parsed + successfully, or zero otherwise. */ + +static struct expression * +stap_parse_argument (const char **arg, struct type *atype, + struct gdbarch *gdbarch) +{ + struct stap_parse_info p; + volatile struct gdb_exception e; + struct cleanup *back_to; + + /* We need to initialize the expression buffer, in order to begin + our parsing efforts. The language here does not matter, since we + are using our own parser. */ + initialize_expout (10, current_language, gdbarch); + back_to = make_cleanup (free_current_contents, &expout); + + p.saved_arg = *arg; + p.arg = *arg; + p.arg_type = atype; + p.gdbarch = gdbarch; + p.inside_paren_p = 0; + + stap_parse_argument_1 (&p, 0, STAP_OPERAND_PREC_NONE); + + discard_cleanups (back_to); + + gdb_assert (p.inside_paren_p == 0); + + /* Casting the final expression to the appropriate type. */ + write_exp_elt_opcode (UNOP_CAST); + write_exp_elt_type (atype); + write_exp_elt_opcode (UNOP_CAST); + + reallocate_expout (); + + p.arg = skip_spaces_const (p.arg); + *arg = p.arg; + + return expout; +} + +/* Function which parses an argument string from PROBE, correctly splitting + the arguments and storing their information in properly ways. + + Consider the following argument string (x86 syntax): + + `4@%eax 4@$10' + + We have two arguments, `%eax' and `$10', both with 32-bit unsigned bitness. + This function basically handles them, properly filling some structures with + this information. */ + +static void +stap_parse_probe_arguments (struct stap_probe *probe, struct objfile *objfile) +{ + const char *cur; + struct gdbarch *gdbarch = get_objfile_arch (objfile); + + gdb_assert (!probe->args_parsed); + cur = probe->args_u.text; + probe->args_parsed = 1; + probe->args_u.vec = NULL; + + if (!cur || !*cur || *cur == ':') + return; + + while (*cur) + { + struct stap_probe_arg arg; + enum stap_arg_bitness b; + int got_minus = 0; + struct expression *expr; + + memset (&arg, 0, sizeof (arg)); + + /* We expect to find something like: + + N@OP + + Where `N' can be [+,-][4,8]. This is not mandatory, so + we check it here. If we don't find it, go to the next + state. */ + if ((*cur == '-' && cur[1] && cur[2] != '@') + && cur[1] != '@') + arg.bitness = STAP_ARG_BITNESS_UNDEFINED; + else + { + if (*cur == '-') + { + /* Discard the `-'. */ + ++cur; + got_minus = 1; + } + + if (*cur == '4') + b = (got_minus ? STAP_ARG_BITNESS_32BIT_SIGNED + : STAP_ARG_BITNESS_32BIT_UNSIGNED); + else if (*cur == '8') + b = (got_minus ? STAP_ARG_BITNESS_64BIT_SIGNED + : STAP_ARG_BITNESS_64BIT_UNSIGNED); + else + { + /* We have an error, because we don't expect anything + except 4 and 8. */ + complaint (&symfile_complaints, + _("unrecognized bitness `%c' for probe `%s'"), + *cur, probe->p.name); + return; + } + + arg.bitness = b; + arg.atype = stap_get_expected_argument_type (gdbarch, b); + + /* Discard the number and the `@' sign. */ + cur += 2; + } + + expr = stap_parse_argument (&cur, arg.atype, gdbarch); + + if (stap_expression_debug) + dump_raw_expression (expr, gdb_stdlog, + "before conversion to prefix form"); + + prefixify_expression (expr); + + if (stap_expression_debug) + dump_prefix_expression (expr, gdb_stdlog); + + arg.aexpr = expr; + + /* Start it over again. */ + cur = skip_spaces_const (cur); + + VEC_safe_push (stap_probe_arg_s, probe->args_u.vec, &arg); + } +} + +/* Given PROBE, returns the number of arguments present in that probe's + argument string. */ + +static unsigned +stap_get_probe_argument_count (struct probe *probe_generic, + struct objfile *objfile) +{ + struct stap_probe *probe = (struct stap_probe *) probe_generic; + + gdb_assert (probe_generic->pops == &stap_probe_ops); + + if (!probe->args_parsed) + stap_parse_probe_arguments (probe, objfile); + + gdb_assert (probe->args_parsed); + return VEC_length (stap_probe_arg_s, probe->args_u.vec); +} + +/* Return 1 if OP is a valid operator inside a probe argument, or zero + otherwise. */ + +static int +stap_is_operator (char op) +{ + return (op == '+' || op == '-' || op == '*' || op == '/' + || op == '>' || op == '<' || op == '!' || op == '^' + || op == '|' || op == '&' || op == '%' || op == '='); +} + +static struct stap_probe_arg * +stap_get_arg (struct stap_probe *probe, struct objfile *objfile, unsigned n) +{ + if (!probe->args_parsed) + stap_parse_probe_arguments (probe, objfile); + + return VEC_index (stap_probe_arg_s, probe->args_u.vec, n); +} + +/* Evaluate the probe's argument N (indexed from 0), returning a value + corresponding to it. Assertion is thrown if N does not exist. */ + +static struct value * +stap_evaluate_probe_argument (struct probe *probe_generic, + struct objfile *objfile, unsigned n) +{ + struct stap_probe *stap_probe = (struct stap_probe *) probe_generic; + struct stap_probe_arg *arg; + int pos = 0; + + gdb_assert (probe_generic->pops == &stap_probe_ops); + + arg = stap_get_arg (stap_probe, objfile, n); + return evaluate_subexp_standard (arg->atype, arg->aexpr, &pos, EVAL_NORMAL); +} + +/* Compile the probe's argument N (indexed from 0) to agent expression. + Assertion is thrown if N does not exist. */ + +static void +stap_compile_to_ax (struct probe *probe_generic, struct objfile *objfile, + struct agent_expr *expr, struct axs_value *value, + unsigned n) +{ + struct stap_probe *stap_probe = (struct stap_probe *) probe_generic; + struct stap_probe_arg *arg; + union exp_element *pc; + + gdb_assert (probe_generic->pops == &stap_probe_ops); + + arg = stap_get_arg (stap_probe, objfile, n); + + pc = arg->aexpr->elts; + gen_expr (arg->aexpr, &pc, expr, value); + + require_rvalue (expr, value); + value->type = arg->atype; +} + +/* Destroy (free) the data related to PROBE. PROBE memory itself is not feed + as it is allocated from OBJFILE_OBSTACK. */ + +static void +stap_probe_destroy (struct probe *probe_generic) +{ + struct stap_probe *probe = (struct stap_probe *) probe_generic; + + gdb_assert (probe_generic->pops == &stap_probe_ops); + + if (probe->args_parsed) + { + struct stap_probe_arg *arg; + int ix; + + for (ix = 0; VEC_iterate (stap_probe_arg_s, probe->args_u.vec, ix, arg); + ++ix) + xfree (arg->aexpr); + VEC_free (stap_probe_arg_s, probe->args_u.vec); + } +} + + + +/* This is called to compute the value of one of the $_probe_arg* + convenience variables. */ + +static struct value * +compute_probe_arg (struct gdbarch *arch, struct internalvar *ivar, + void *data) +{ + struct frame_info *frame = get_selected_frame (_("No frame selected")); + CORE_ADDR pc = get_frame_pc (frame); + int sel = (int) (uintptr_t) data; + struct objfile *objfile; + struct probe *pc_probe; + unsigned n_args; + + /* SEL == -1 means "_probe_argc". */ + gdb_assert (sel >= -1); + + pc_probe = find_probe_by_pc (pc, &objfile); + if (pc_probe == NULL) + error (_("No SystemTap probe at PC %s"), core_addr_to_string (pc)); + + n_args + = objfile->sf->sym_probe_fns->sym_get_probe_argument_count (objfile, + pc_probe); + if (sel == -1) + return value_from_longest (builtin_type (arch)->builtin_int, n_args); + + if (sel >= n_args) + error (_("Invalid probe argument %d -- probe has %u arguments available"), + sel, n_args); + + return objfile->sf->sym_probe_fns->sym_evaluate_probe_argument (objfile, + pc_probe, + sel); +} + +/* This is called to compile one of the $_probe_arg* convenience + variables into an agent expression. */ + +static void +compile_probe_arg (struct internalvar *ivar, struct agent_expr *expr, + struct axs_value *value, void *data) +{ + CORE_ADDR pc = expr->scope; + int sel = (int) (uintptr_t) data; + struct objfile *objfile; + struct probe *pc_probe; + int n_probes; + + /* SEL == -1 means "_probe_argc". */ + gdb_assert (sel >= -1); + + pc_probe = find_probe_by_pc (pc, &objfile); + if (pc_probe == NULL) + error (_("No SystemTap probe at PC %s"), core_addr_to_string (pc)); + + n_probes + = objfile->sf->sym_probe_fns->sym_get_probe_argument_count (objfile, + pc_probe); + if (sel == -1) + { + value->kind = axs_rvalue; + value->type = builtin_type (expr->gdbarch)->builtin_int; + ax_const_l (expr, n_probes); + return; + } + + gdb_assert (sel >= 0); + if (sel >= n_probes) + error (_("Invalid probe argument %d -- probe has %d arguments available"), + sel, n_probes); + + objfile->sf->sym_probe_fns->sym_compile_to_ax (objfile, pc_probe, + expr, value, sel); +} + + + +/* Set or clear a SystemTap semaphore. ADDRESS is the semaphore's + address. SET is zero if the semaphore should be cleared, or one + if it should be set. This is a helper function for `stap_semaphore_down' + and `stap_semaphore_up'. */ + +static void +stap_modify_semaphore (CORE_ADDR address, int set, struct gdbarch *gdbarch) +{ + gdb_byte bytes[sizeof (LONGEST)]; + /* The ABI specifies "unsigned short". */ + struct type *type = builtin_type (gdbarch)->builtin_unsigned_short; + ULONGEST value; + + if (address == 0) + return; + + /* Swallow errors. */ + if (target_read_memory (address, bytes, TYPE_LENGTH (type)) != 0) + { + warning (_("Could not read the value of a SystemTap semaphore.")); + return; + } + + value = extract_unsigned_integer (bytes, TYPE_LENGTH (type), + gdbarch_byte_order (gdbarch)); + /* Note that we explicitly don't worry about overflow or + underflow. */ + if (set) + ++value; + else + --value; + + store_unsigned_integer (bytes, TYPE_LENGTH (type), + gdbarch_byte_order (gdbarch), value); + + if (target_write_memory (address, bytes, TYPE_LENGTH (type)) != 0) + warning (_("Could not write the value of a SystemTap semaphore.")); +} + +/* Set a SystemTap semaphore. SEM is the semaphore's address. Semaphores + act as reference counters, so calls to this function must be paired with + calls to `stap_semaphore_down'. + + This function and `stap_semaphore_down' race with another tool changing + the probes, but that is too rare to care. */ + +static void +stap_set_semaphore (struct probe *probe_generic, struct gdbarch *gdbarch) +{ + struct stap_probe *probe = (struct stap_probe *) probe_generic; + + gdb_assert (probe_generic->pops == &stap_probe_ops); + + stap_modify_semaphore (probe->sem_addr, 1, gdbarch); +} + +/* Clear a SystemTap semaphore. SEM is the semaphore's address. */ + +static void +stap_clear_semaphore (struct probe *probe_generic, struct gdbarch *gdbarch) +{ + struct stap_probe *probe = (struct stap_probe *) probe_generic; + + gdb_assert (probe_generic->pops == &stap_probe_ops); + + stap_modify_semaphore (probe->sem_addr, 0, gdbarch); +} + +/* Implementation of `$_probe_arg*' set of variables. */ + +static const struct internalvar_funcs probe_funcs = +{ + compute_probe_arg, + compile_probe_arg, + NULL +}; + +/* Helper function that parses the information contained in a + SystemTap's probe. Basically, the information consists in: + + - Probe's PC address; + - Link-time section address of `.stapsdt.base' section; + - Link-time address of the semaphore variable, or ZERO if the + probe doesn't have an associated semaphore; + - Probe's provider name; + - Probe's name; + - Probe's argument format + + This function returns 1 if the handling was successful, and zero + otherwise. */ + +static void +handle_stap_probe (struct objfile *objfile, struct sdt_note *el, + VEC (probe_p) **probesp, CORE_ADDR base) +{ + bfd *abfd = objfile->obfd; + int size = bfd_get_arch_size (abfd) / 8; + struct gdbarch *gdbarch = get_objfile_arch (objfile); + enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); + struct type *ptr_type = builtin_type (gdbarch)->builtin_data_ptr; + CORE_ADDR base_ref; + const char *probe_args = NULL; + struct stap_probe *ret; + + ret = obstack_alloc (&objfile->objfile_obstack, sizeof (*ret)); + ret->p.pops = &stap_probe_ops; + + /* Provider and the name of the probe. */ + ret->p.provider = &el->data[3 * size]; + ret->p.name = memchr (ret->p.provider, '\0', + (char *) el->data + el->size - ret->p.provider); + /* Making sure there is a name. */ + if (!ret->p.name) + { + complaint (&symfile_complaints, _("corrupt probe name when " + "reading `%s'"), objfile->name); + + /* There is no way to use a probe without a name or a provider, so + returning zero here makes sense. */ + return; + } + else + ++ret->p.name; + + /* Retrieving the probe's address. */ + ret->p.address = extract_typed_address (&el->data[0], ptr_type); + + /* Link-time sh_addr of `.stapsdt.base' section. */ + base_ref = extract_typed_address (&el->data[size], ptr_type); + + /* Semaphore address. */ + ret->sem_addr = extract_typed_address (&el->data[2 * size], ptr_type); + + ret->p.address += (ANOFFSET (objfile->section_offsets, + SECT_OFF_TEXT (objfile)) + + base - base_ref); + if (ret->sem_addr) + ret->sem_addr += (ANOFFSET (objfile->section_offsets, + SECT_OFF_DATA (objfile)) + + base - base_ref); + + /* Arguments. We can only extract the argument format if there is a valid + name for this probe. */ + probe_args = memchr (ret->p.name, '\0', + (char *) el->data + el->size - ret->p.name); + + if (probe_args != NULL) + ++probe_args; + + if (probe_args == NULL || (memchr (probe_args, '\0', + (char *) el->data + el->size - ret->p.name) + != el->data + el->size - 1)) + { + complaint (&symfile_complaints, _("corrupt probe argument when " + "reading `%s'"), objfile->name); + /* If the argument string is NULL, it means some problem happened with + it. So we return 0. */ + return; + } + + ret->args_parsed = 0; + ret->args_u.text = (void *) probe_args; + + /* Successfully created probe. */ + VEC_safe_push (probe_p, *probesp, (struct probe *) ret); +} + +/* Helper function which tries to find the base address of the SystemTap + base section named STAP_BASE_SECTION_NAME. */ + +static void +get_stap_base_address_1 (bfd *abfd, asection *sect, void *obj) +{ + asection **ret = obj; + + if ((sect->flags & (SEC_DATA | SEC_ALLOC | SEC_HAS_CONTENTS)) + && sect->name && !strcmp (sect->name, STAP_BASE_SECTION_NAME)) + *ret = sect; +} + +/* Helper function which iterates over every section in the BFD file, + trying to find the base address of the SystemTap base section. + Returns 1 if found (setting BASE to the proper value), zero otherwise. */ + +static int +get_stap_base_address (bfd *obfd, bfd_vma *base) +{ + asection *ret = NULL; + + bfd_map_over_sections (obfd, get_stap_base_address_1, (void *) &ret); + + if (!ret) + { + complaint (&symfile_complaints, _("could not obtain base address for " + "SystemTap section on objfile `%s'."), + obfd->filename); + return 0; + } + + if (base) + *base = ret->vma; + + return 1; +} + +/* Helper function for `elf_get_probes', which gathers information about all + SystemTap probes from OBJFILE. */ + +static void +stap_get_probes (VEC (probe_p) **probesp, struct objfile *objfile) +{ + /* If we are here, then this is the first time we are parsing the + SystemTap probe's information. We basically have to count how many + probes the objfile has, and then fill in the necessary information + for each one. */ + bfd *obfd = objfile->obfd; + bfd_vma base; + struct sdt_note *iter; + unsigned save_probesp_len = VEC_length (probe_p, *probesp); + + if (!elf_tdata (obfd)->sdt_note_head) + { + /* There isn't any probe here. */ + return; + } + + if (!get_stap_base_address (obfd, &base)) + { + /* There was an error finding the base address for the section. + Just return NULL. */ + return; + } + + /* Parsing each probe's information. */ + for (iter = elf_tdata (obfd)->sdt_note_head; iter; iter = iter->next) + { + /* We first have to handle all the information about the + probe which is present in the section. */ + handle_stap_probe (objfile, iter, probesp, base); + } + + if (save_probesp_len == VEC_length (probe_p, *probesp)) + { + /* If we are here, it means we have failed to parse every known + probe. */ + complaint (&symfile_complaints, _("could not parse SystemTap probe(s) " + "from inferior")); + return; + } +} + +static void +stap_relocate (struct probe *probe_generic, CORE_ADDR delta) +{ + struct stap_probe *probe = (struct stap_probe *) probe_generic; + + gdb_assert (probe_generic->pops == &stap_probe_ops); + + probe->p.address += delta; + if (probe->sem_addr) + probe->sem_addr += delta; +} + +static int +stap_probe_is_linespec (const char **linespecp) +{ + static const char *const keywords[] = { "-pstap", "-probe-stap", NULL }; + + return probe_is_linespec_by_keyword (linespecp, keywords); +} + +static void +stap_gen_info_probes_table_header (VEC (info_probe_column_s) **heads) +{ + info_probe_column_s stap_probe_column; + + stap_probe_column.field_name = "semaphore"; + stap_probe_column.print_name = _("Semaphore"); + + VEC_safe_push (info_probe_column_s, *heads, &stap_probe_column); +} + +static void +stap_gen_info_probes_table_values (struct probe *probe_generic, + struct objfile *objfile, + VEC (const_char_ptr) **ret) +{ + struct stap_probe *probe = (struct stap_probe *) probe_generic; + struct gdbarch *gdbarch = get_objfile_arch (objfile); + const char *val = NULL; + + gdb_assert (probe_generic->pops == &stap_probe_ops); + + if (probe->sem_addr) + val = print_core_address (gdbarch, probe->sem_addr); + + VEC_safe_push (const_char_ptr, *ret, val); +} + +/* SystemTap probe_ops. */ + +static const struct probe_ops stap_probe_ops = +{ + stap_probe_is_linespec, + stap_get_probes, + stap_relocate, + stap_get_probe_argument_count, + stap_evaluate_probe_argument, + stap_compile_to_ax, + stap_set_semaphore, + stap_clear_semaphore, + stap_probe_destroy, + stap_gen_info_probes_table_header, + stap_gen_info_probes_table_values, +}; + +/* Implementation of the `info probes stap' command. */ + +static void +info_probes_stap_command (char *arg, int from_tty) +{ + info_probes_for_ops (arg, from_tty, &stap_probe_ops); +} + +void _initialize_stap_probe (void); + +void +_initialize_stap_probe (void) +{ + VEC_safe_push (probe_ops_cp, all_probe_ops, &stap_probe_ops); + + add_setshow_zinteger_cmd ("stap-expression", class_maintenance, + &stap_expression_debug, + _("Set SystemTap expression debugging."), + _("Show SystemTap expression debugging."), + _("When non-zero, the internal representation " + "of SystemTap expressions will be printed."), + NULL, + show_stapexpressiondebug, + &setdebuglist, &showdebuglist); + + create_internalvar_type_lazy ("_probe_argc", &probe_funcs, + (void *) (uintptr_t) -1); + create_internalvar_type_lazy ("_probe_arg0", &probe_funcs, + (void *) (uintptr_t) 0); + create_internalvar_type_lazy ("_probe_arg1", &probe_funcs, + (void *) (uintptr_t) 1); + create_internalvar_type_lazy ("_probe_arg2", &probe_funcs, + (void *) (uintptr_t) 2); + create_internalvar_type_lazy ("_probe_arg3", &probe_funcs, + (void *) (uintptr_t) 3); + create_internalvar_type_lazy ("_probe_arg4", &probe_funcs, + (void *) (uintptr_t) 4); + create_internalvar_type_lazy ("_probe_arg5", &probe_funcs, + (void *) (uintptr_t) 5); + create_internalvar_type_lazy ("_probe_arg6", &probe_funcs, + (void *) (uintptr_t) 6); + create_internalvar_type_lazy ("_probe_arg7", &probe_funcs, + (void *) (uintptr_t) 7); + create_internalvar_type_lazy ("_probe_arg8", &probe_funcs, + (void *) (uintptr_t) 8); + create_internalvar_type_lazy ("_probe_arg9", &probe_funcs, + (void *) (uintptr_t) 9); + create_internalvar_type_lazy ("_probe_arg10", &probe_funcs, + (void *) (uintptr_t) 10); + create_internalvar_type_lazy ("_probe_arg11", &probe_funcs, + (void *) (uintptr_t) 11); + + add_cmd ("stap", class_info, info_probes_stap_command, + _("\ +Show information about SystemTap static probes.\n\ +Usage: info probes stap [PROVIDER [NAME [OBJECT]]]\n\ +Each argument is a regular expression, used to select probes.\n\ +PROVIDER matches probe provider names.\n\ +NAME matches the probe names.\n\ +OBJECT matches the executable or shared library name."), + info_probes_cmdlist_get ()); + +} diff --git a/gdb/stap-probe.h b/gdb/stap-probe.h new file mode 100644 index 0000000..0e9df06 --- /dev/null +++ b/gdb/stap-probe.h @@ -0,0 +1,52 @@ +/* SystemTap probe support for GDB. + + Copyright (C) 2012 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#if !defined (STAP_PROBE_H) +#define STAP_PROBE_H 1 + +#include "probe.h" + +/* Structure which holds information about the parsing process of one probe's + argument. */ + +struct stap_parse_info +{ + /* The probe's argument in a string format. */ + const char *arg; + + /* A pointer to the full chain of arguments. This is useful for printing + error messages. The parser functions should not modify this argument + directly; instead, they should use the ARG pointer above. */ + const char *saved_arg; + + /* The expected argument type (bitness), as defined in the probe's + argument. For instance, if the argument begins with `-8@', it means + the bitness is 64-bit signed. In this case, ARG_TYPE would represent + the type `int64_t'. */ + struct type *arg_type; + + /* A pointer to the current gdbarch. */ + struct gdbarch *gdbarch; + + /* Greater than zero if we are inside a parenthesized expression. Useful + for knowing when to skip spaces or not. */ + int inside_paren_p; +}; + +#endif /* !defined (STAP_PROBE_H) */ diff --git a/gdb/symfile.h b/gdb/symfile.h index 7024ace..bf3f7a0 100644 --- a/gdb/symfile.h +++ b/gdb/symfile.h @@ -22,6 +22,7 @@ /* This file requires that you first include "bfd.h". */ #include "symtab.h" +#include "gdb_vecs.h" /* Opaque declarations. */ struct target_section; @@ -29,6 +30,11 @@ struct objfile; struct obj_section; struct obstack; struct block; +struct probe; +struct value; +struct frame_info; +struct agent_expr; +struct axs_value; /* Comparison function for symbol look ups. */ @@ -297,6 +303,52 @@ struct quick_symbol_functions int need_fullname); }; +/* Structure of functions used for probe support. If one of these functions + is provided, all must be. */ + +struct sym_probe_fns +{ + /* If non-NULL, return an array of probe objects. + + The returned value does not have to be freed and it has lifetime of the + OBJFILE. */ + VEC (probe_p) *(*sym_get_probes) (struct objfile *); + + /* Return the number of arguments available to PROBE. PROBE will + have come from a call to this objfile's sym_get_probes method. + If you provide an implementation of sym_get_probes, you must + implement this method as well. */ + unsigned (*sym_get_probe_argument_count) (struct objfile *objfile, + struct probe *probe); + + /* Evaluate the Nth argument available to PROBE. PROBE will have + come from a call to this objfile's sym_get_probes method. N will + be between 0 and the number of arguments available to this probe. + FRAME is the frame in which the evaluation is done; the frame's + PC will match the address of the probe. If you provide an + implementation of sym_get_probes, you must implement this method + as well. */ + struct value *(*sym_evaluate_probe_argument) (struct objfile *objfile, + struct probe *probe, + unsigned n); + + /* Compile the Nth probe argument to an agent expression. PROBE + will have come from a call to this objfile's sym_get_probes + method. N will be between 0 and the number of arguments + available to this probe. EXPR and VALUE are the agent expression + that is being updated. */ + void (*sym_compile_to_ax) (struct objfile *objfile, + struct probe *probe, + struct agent_expr *expr, + struct axs_value *value, + unsigned n); + + /* Relocate the probe section of OBJFILE. */ + void (*sym_relocate_probe) (struct objfile *objfile, + struct section_offsets *new_offsets, + struct section_offsets *delta); +}; + /* Structure to keep track of symbol reading functions for various object file types. */ @@ -367,6 +419,10 @@ struct sym_fns bfd_byte *(*sym_relocate) (struct objfile *, asection *sectp, bfd_byte *buf); + /* If non-NULL, this objfile has probe support, and all the probe + functions referred to here will be non-NULL. */ + const struct sym_probe_fns *sym_probe_fns; + /* The "quick" (aka partial) symbol functions for this symbol reader. */ const struct quick_symbol_functions *qf; diff --git a/gdb/symtab.c b/gdb/symtab.c index 6c70113..d68e542 100644 --- a/gdb/symtab.c +++ b/gdb/symtab.c @@ -901,6 +901,7 @@ init_sal (struct symtab_and_line *sal) sal->end = 0; sal->explicit_pc = 0; sal->explicit_line = 0; + sal->probe = NULL; } diff --git a/gdb/symtab.h b/gdb/symtab.h index 6933c0c..61e7c0f 100644 --- a/gdb/symtab.h +++ b/gdb/symtab.h @@ -35,6 +35,7 @@ struct axs_value; struct agent_expr; struct program_space; struct language_defn; +struct probe; /* Some of the structures in this file are space critical. The space-critical structures are: @@ -1042,6 +1043,9 @@ struct symtab_and_line CORE_ADDR end; int explicit_pc; int explicit_line; + + /* The probe associated with this symtab_and_line. */ + struct probe *probe; }; extern void init_sal (struct symtab_and_line *sal); diff --git a/gdb/tracepoint.c b/gdb/tracepoint.c index c213374..e5b57e1 100644 --- a/gdb/tracepoint.c +++ b/gdb/tracepoint.c @@ -52,6 +52,7 @@ #include "memrange.h" #include "exceptions.h" #include "cli/cli-utils.h" +#include "probe.h" /* readline include files */ #include "readline/readline.h" @@ -1717,6 +1718,7 @@ start_tracing (char *notes) for (ix = 0; VEC_iterate (breakpoint_p, tp_vec, ix, b); ix++) { struct tracepoint *t = (struct tracepoint *) b; + struct bp_location *loc; if (b->enable_state == bp_enabled) any_enabled = 1; @@ -1779,6 +1781,9 @@ start_tracing (char *notes) } t->number_on_target = b->number; + + for (loc = b->loc; loc; loc = loc->next) + loc->probe->pops->set_semaphore (loc->probe, loc->gdbarch); } VEC_free (breakpoint_p, tp_vec); @@ -1851,9 +1856,34 @@ void stop_tracing (char *note) { int ret; + VEC(breakpoint_p) *tp_vec = NULL; + int ix; + struct breakpoint *t; target_trace_stop (); + tp_vec = all_tracepoints (); + for (ix = 0; VEC_iterate (breakpoint_p, tp_vec, ix, t); ix++) + { + struct bp_location *loc; + + if ((t->type == bp_fast_tracepoint + ? !may_insert_fast_tracepoints + : !may_insert_tracepoints)) + continue; + + for (loc = t->loc; loc; loc = loc->next) + { + /* GDB can be totally absent in some disconnected trace scenarios, + but we don't really care if this semaphore goes out of sync. + That's why we are decrementing it here, but not taking care + in other places. */ + loc->probe->pops->clear_semaphore (loc->probe, loc->gdbarch); + } + } + + VEC_free (breakpoint_p, tp_vec); + if (!note) note = trace_stop_notes; ret = target_set_trace_notes (NULL, NULL, note); diff --git a/gdb/xcoffread.c b/gdb/xcoffread.c index 1a1b5de..2d2e0bb 100644 --- a/gdb/xcoffread.c +++ b/gdb/xcoffread.c @@ -3136,6 +3136,7 @@ static const struct sym_fns xcoff_sym_fns = default_symfile_segments, /* Get segment information from a file. */ aix_process_linenos, default_symfile_relocate, /* Relocate a debug section. */ + NULL, /* sym_probe_fns */ &psym_functions }; |