From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 5787 invoked by alias); 9 Mar 2012 20:34:36 -0000 Received: (qmail 2807 invoked by uid 22791); 9 Mar 2012 20:33:58 -0000 X-SWARE-Spam-Status: No, hits=-2.4 required=5.0 tests=AWL,BAYES_50,KAM_STOCKTIP,RCVD_IN_DNSWL_HI,SPF_HELO_PASS,TW_BJ,TW_CP,TW_EG,TW_FN,TW_YM,T_RP_MATCHES_RCVD X-Spam-Check-By: sourceware.org Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Fri, 09 Mar 2012 20:33:30 +0000 Received: from int-mx01.intmail.prod.int.phx2.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id q29KXTKm015217 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK) for ; Fri, 9 Mar 2012 15:33:29 -0500 Received: from psique (ovpn-112-58.phx2.redhat.com [10.3.112.58]) by int-mx01.intmail.prod.int.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id q29KXMRG005791; Fri, 9 Mar 2012 15:33:25 -0500 From: Sergio Durigan Junior To: gdb-patches@sourceware.org Cc: Tom Tromey Subject: [PATCH 2/3] Implement new features needed for handling SystemTap probes References: X-URL: http://www.redhat.com Date: Fri, 09 Mar 2012 20:34:00 -0000 In-Reply-To: (Sergio Durigan Junior's message of "Fri, 09 Mar 2012 17:28:51 -0300") Message-ID: User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/23.3 (gnu/linux) MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii X-IsSubscribed: yes Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org X-SW-Source: 2012-03/txt/msg00355.txt.bz2 Hello, This is the second patch. It implements a bunch of new things needed to support SystemTap probes, for both arch-dependent and independent bits. There are new variables and methods for gdbarch, new files (stap-probe.[ch]), new methods for reading and handling SystemTap probes on elfread.c, adaptations on breakpoint.c to make the `break' command understand the `-p' and `-probe' arguments, and so on. It also includes documentation and testcases inclusions. Is this OK? gdb/ChangeLog 2012-09-03 Sergio Durigan Junior Tom Tromey * Makefile.in (SFILES): Add `stap-probe'. (COMMON_OBS): Likewise. (HFILES_NO_SRCDIR): Likewise. * NEWS: Mention support for SystemTap probes. * amd64-linux-tdep.c (amd64_linux_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 `stap-probe.h'. (bkpt_stap_probe_breakpoint_ops): New variable. (modify_semaphore): New function. (insert_bp_location): Call `modify_semaphore'. (remove_breakpoint_1): Likewise. (momentary_breakpoint_from_master): Use the `semaphore' value. (add_location_to_breakpoint): Likewise. (break_command_1): Using proper breakpoint_ops according to the argument passed by user in the command line. (bkpt_stap_probe_create_sals_from_address): New function. (bkpt_stap_probe_decode_linespec): Likewise. (initialize_breakpoint_ops): Initializing breakpoint_ops from SystemTap probes. * breakpoint.h (struct bp_location) : New field. (modify_semaphore): New function. * 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. * dbxread.c (aout_sym_fns): Likewise. * elfread.c: Include `stap-probe.h' and `arch-utils.h'. (stap_probe_key): New variable. (struct stap_probe_per_objfile): New struct. (handle_probe): New function. (STAP_BASE_SECTION_NAME): New define. (get_base_address_1): New function. (get_base_address): Likewise. (elf_get_probes): Likewise. (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 SystemTap probes. * 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-linux-tdep.c (i386_linux_init_abi): Initializing proper fields used by SystemTap probes' arguments parser. * 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-tdep.h (i386_stap_is_single_operand): Likewise. (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 SystemTap 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. * 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 (struct sym_probe_fns): New struct. (struct sym_fns) : New field. * symtab.c (init_sal): Initialize `semaphore' field. * symtab.h (struct symtab_and_line) : New field. * tracepoint.c (start_tracing): Adjust semaphore on breakpoints locations. (stop_tracing): Likewise. * xcoffread.c (xcoff_sym_fns): Add `sym_probe_fns' value. gdb/doc/ChangeLog 2012-09-03 Sergio Durigan Junior Tom Tromey * gdb.texinfo (Static Probe Points): New entry, explaining SystemTap probe support on GDB. gdb/testsuite/ChangeLog 2012-09-03 Sergio Durigan Junior Tom Tromey * gdb.base/default.exp: Add `$_probe_arg*' convenience variables. * gdb.base/stap-probe.c: New file. * gdb.base/stap-probe.exp: New file. * gdb.cp/nextoverthrow.exp: Add check for SystemTap probe in libgcc's unwinder. * gdb.trace/stap-trace.c: New file. * gdb.trace/stap-trace.exp: New file. --- gdb/Makefile.in | 8 +- gdb/NEWS | 4 + gdb/amd64-linux-tdep.c | 9 + gdb/arm-linux-tdep.c | 131 +++ gdb/ax-gdb.c | 8 +- gdb/ax-gdb.h | 5 + gdb/breakpoint.c | 88 ++- gdb/breakpoint.h | 12 + gdb/cli/cli-utils.c | 41 + gdb/cli/cli-utils.h | 11 + gdb/coffread.c | 1 + gdb/dbxread.c | 1 + gdb/doc/gdb.texinfo | 82 ++ gdb/elfread.c | 288 ++++++ gdb/gdbarch.c | 250 +++++ gdb/gdbarch.h | 120 +++ gdb/gdbarch.sh | 96 ++ gdb/i386-linux-tdep.c | 9 + gdb/i386-tdep.c | 312 ++++++ gdb/i386-tdep.h | 9 + gdb/machoread.c | 1 + gdb/mipsread.c | 1 + gdb/objfiles.c | 5 + gdb/parse.c | 18 +- gdb/parser-defs.h | 17 + gdb/ppc-linux-tdep.c | 76 ++ gdb/s390-tdep.c | 21 + gdb/somread.c | 1 + gdb/stap-probe.c | 1689 ++++++++++++++++++++++++++++++++ gdb/stap-probe.h | 144 +++ gdb/symfile.h | 55 + gdb/symtab.c | 1 + gdb/symtab.h | 4 + gdb/testsuite/gdb.base/default.exp | 13 + gdb/testsuite/gdb.base/stap-probe.c | 108 ++ gdb/testsuite/gdb.base/stap-probe.exp | 187 ++++ gdb/testsuite/gdb.cp/nextoverthrow.exp | 11 + gdb/testsuite/gdb.trace/stap-trace.c | 71 ++ gdb/testsuite/gdb.trace/stap-trace.exp | 129 +++ gdb/tracepoint.c | 23 + gdb/xcoffread.c | 1 + 41 files changed, 4037 insertions(+), 24 deletions(-) create mode 100644 gdb/stap-probe.c create mode 100644 gdb/stap-probe.h create mode 100644 gdb/testsuite/gdb.base/stap-probe.c create mode 100644 gdb/testsuite/gdb.base/stap-probe.exp create mode 100644 gdb/testsuite/gdb.trace/stap-trace.c create mode 100644 gdb/testsuite/gdb.trace/stap-trace.exp diff --git a/gdb/Makefile.in b/gdb/Makefile.in index 84eda7d..050aacd 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -723,8 +723,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 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 \ @@ -820,7 +820,7 @@ osdata.h procfs.h python/py-event.h python/py-events.h python/py-stopevent.h \ python/python-internal.h python/python.h ravenscar-thread.h record.h \ solib-darwin.h solib-ia64-hpux.h solib-spu.h windows-nat.h xcoffread.h \ gnulib/extra/arg-nonnull.h gnulib/extra/c++defs.h gnulib/extra/warn-on-use.h \ -gnulib/stddef.in.h inline-frame.h skip.h \ +gnulib/stddef.in.h inline-frame.h skip.h stap-probe.h \ common/common-utils.h common/xml-utils.h common/buffer.h common/ptid.h \ common/linux-osdata.h gdb-dlfcn.h @@ -909,7 +909,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 stap-probe.o \ common-utils.o buffer.o ptid.o gdb-dlfcn.o common-agent.o TSOBS = inflow.o diff --git a/gdb/NEWS b/gdb/NEWS index 9c89346..1439945 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -3,6 +3,10 @@ *** Changes since GDB 7.4 +* GDB now has support for SystemTap probes. You can set a + breakpoint using the new "-p" or "-probe" options and inspect the probe + arguments using the new $_probe_arg family of convenience variables. + * Python scripting ** GDB commands implemented in Python can now be put in command class diff --git a/gdb/amd64-linux-tdep.c b/gdb/amd64-linux-tdep.c index da22c1c..05f7f5d 100644 --- a/gdb/amd64-linux-tdep.c +++ b/gdb/amd64-linux-tdep.c @@ -1362,6 +1362,15 @@ amd64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) set_gdbarch_process_record (gdbarch, i386_process_record); set_gdbarch_process_record_signal (gdbarch, amd64_linux_record_signal); + /* 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); + /* Initialize the amd64_linux_record_tdep. */ /* These values are the size of the type that will be used in a system call. They are obtained from Linux Kernel source. */ diff --git a/gdb/arm-linux-tdep.c b/gdb/arm-linux-tdep.c index e41205b..f49c0cf 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 + #include "gdb_string.h" /* This is defined in 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) @@ -1155,6 +1277,15 @@ arm_linux_init_abi (struct gdbarch_info info, simple_displaced_step_free_closure); set_gdbarch_displaced_step_location (gdbarch, displaced_step_at_entry_point); + /* 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; } diff --git a/gdb/ax-gdb.c b/gdb/ax-gdb.c index b163681..0d796ac 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, @@ -788,7 +784,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 @@ -1806,7 +1802,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 1158f0e..a8f3a42 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 "stap-probe.h" #include "cli/cli-utils.h" #include "continuations.h" #include "stack.h" @@ -290,6 +292,9 @@ static struct breakpoint_ops momentary_breakpoint_ops; breakpoints. */ struct breakpoint_ops bkpt_breakpoint_ops; +/* Breakpoints set on SystemTap probes. */ +static struct breakpoint_ops bkpt_stap_probe_breakpoint_ops; + /* A reference-counted struct command_line. This lets multiple breakpoints share a single command list. */ struct counted_command_line @@ -1926,6 +1931,40 @@ unduplicated_should_be_inserted (struct bp_location *bl) return result; } +/* See the comment in breakpoint.h. */ + +void +modify_semaphore (struct bp_location *loc, int set) +{ + struct gdbarch *arch = loc->gdbarch; + gdb_byte bytes[sizeof (LONGEST)]; + /* The ABI specifies "unsigned short". */ + struct type *type = builtin_type (arch)->builtin_unsigned_short; + CORE_ADDR address = loc->semaphore; + ULONGEST value; + + if (address == 0) + return; + + /* Swallow errors. */ + if (target_read_memory (address, bytes, TYPE_LENGTH (type)) != 0) + return; + + value = extract_unsigned_integer (bytes, TYPE_LENGTH (type), + gdbarch_byte_order (arch)); + /* 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 (arch), value); + + target_write_memory (address, bytes, TYPE_LENGTH (type)); +} + /* Parses a conditional described by an expression COND into an agent expression bytecode suitable for evaluation by the bytecode interpreter. Return NULL if there was @@ -2168,6 +2207,8 @@ insert_bp_location (struct bp_location *bl, /* No overlay handling: just set the breakpoint. */ val = bl->owner->ops->insert_location (bl); + + modify_semaphore (bl, 1); } else { @@ -3153,6 +3194,8 @@ remove_breakpoint_1 (struct bp_location *bl, insertion_state_t is) { /* No overlay handling: just remove the breakpoint. */ val = bl->owner->ops->remove_location (bl); + + modify_semaphore (bl, 0); } else { @@ -8070,6 +8113,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->semaphore = orig->loc->semaphore; if (orig->loc->source_file != NULL) copy->loc->source_file = xstrdup (orig->loc->source_file); @@ -8155,6 +8199,7 @@ add_location_to_breakpoint (struct breakpoint *b, loc->requested_address = sal->pc; loc->address = adjusted_address; loc->pspace = sal->pspace; + loc->semaphore = sal->semaphore; gdb_assert (loc->pspace != NULL); loc->section = sal->section; loc->gdbarch = loc_gdbarch; @@ -8885,6 +8930,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; + + /* Matching breakpoints on SystemTap probes (`-p' or `-probe'). */ + if (arg && ((strncmp (arg, "-p", 2) == 0 && isspace (arg[2])) + || (strncmp (arg, "-probe", 6) == 0 && isspace (arg[6])))) + ops = &bkpt_stap_probe_breakpoint_ops; + else + ops = &bkpt_breakpoint_ops; create_breakpoint (get_current_arch (), arg, @@ -8892,7 +8945,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 */); @@ -12364,6 +12417,33 @@ momentary_bkpt_print_mention (struct breakpoint *b) /* Nothing to mention. These breakpoints are internal. */ } +/* Specific methods for SystemTap probe breakpoints. */ + +static void +bkpt_stap_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_stap_probe (arg, canonical); + + *copy_arg = xstrdup (canonical->addr_string); + lsal.canonical = xstrdup (*copy_arg); + + VEC_safe_push (linespec_sals, canonical->sals, &lsal); +} + +static void +bkpt_stap_probe_decode_linespec (struct breakpoint *b, char **s, + struct symtabs_and_lines *sals) +{ + *sals = parse_stap_probe (s, NULL); + if (!sals->sals) + error (_("probe not found")); +} + /* The breakpoint_ops structure to be used in tracepoints. */ static void @@ -14855,6 +14935,12 @@ initialize_breakpoint_ops (void) ops->print_it = momentary_bkpt_print_it; ops->print_mention = momentary_bkpt_print_mention; + /* SystemTap probe breakpoints. */ + ops = &bkpt_stap_probe_breakpoint_ops; + *ops = bkpt_breakpoint_ops; + ops->create_sals_from_address = bkpt_stap_probe_create_sals_from_address; + ops->decode_linespec = bkpt_stap_probe_decode_linespec; + /* GNU v3 exception catchpoints. */ ops = &gnu_v3_exception_catchpoint_ops; *ops = bkpt_breakpoint_ops; diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h index 5b5172e..11ea6c8 100644 --- a/gdb/breakpoint.h +++ b/gdb/breakpoint.h @@ -418,6 +418,11 @@ struct bp_location processor's architectual constraints. */ CORE_ADDR requested_address; + /* If the location comes from a SystemTap probe point, and the probe + has an associated semaphore variable, then this is the address of + the semaphore. Otherwise, this is zero. */ + CORE_ADDR semaphore; + char *function_name; /* Details of the placed breakpoint, when inserted. */ @@ -1471,6 +1476,13 @@ extern int user_breakpoint_p (struct breakpoint *); /* Attempt to determine architecture of location identified by SAL. */ extern struct gdbarch *get_sal_arch (struct symtab_and_line sal); +/* Set or clear a SystemTap semaphore. LOC is the location which may + hold a semaphore. SET is non-zero if the semaphore should be set, + or zero if the semaphore should be cleared. Semaphores act as + reference counters, so calls to this function must be paired. */ + +extern void modify_semaphore (struct bp_location *location, int set); + extern void handle_solib_event (void); #endif /* !defined (BREAKPOINT_H) */ diff --git a/gdb/cli/cli-utils.c b/gdb/cli/cli-utils.c index a7b2718..a1e5161 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 30bf3f7..f5b9fe0 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/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/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index e8bbded..16c7a4d 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -3303,6 +3303,7 @@ all breakpoints in that range are operated on. * Conditions:: Break conditions * Break Commands:: Breakpoint command lists * Save Breakpoints:: How to save breakpoints in a file +* Static Probe Points:: Listing static probe points * Error in Breakpoints:: ``Cannot insert breakpoints'' * Breakpoint-related Warnings:: ``Breakpoint address adjusted...'' @end menu @@ -4613,6 +4614,50 @@ and remove the breakpoint definitions you're not interested in, or that can no longer be recreated. @end table +@node Static Probe Points +@subsection Static Probe Points + +@cindex SystemTap static probe point +@cindex sdt-probe +The @sc{gnu}/Linux tool @code{SystemTap} provides a way for +applications to embed static probes, using @file{sys/sdt.h}. @value{GDBN} +can list the available probes, and you can put breakpoints at the +probe points (@pxref{Specify Location}). + +You can examine the available @code{SystemTap} static probes using +@code{info probes}: + +@table @code +@kindex info probes +@item info probes [@var{provider} [@var{name} [@var{objfile}]]] +List the available @code{SystemTap} static probes. + +If given, @var{provider} is a regular expression used to select which +providers to list. If omitted, all providers are listed. + +If given, @var{name} is a regular expression used to select which +probes to list. If omitted, all probes are listed. + +If given, @var{objfile} is a regular expression used to select which +object files (executable or shared libraries) to examine. If not +given, all object files are considered. +@end table + +@vindex $_probe_arg@r{, convenience variable} +A probe may specify up to twelve arguments. These are available at the +point at which the probe is defined---that is, when the current PC is +at the probe's location. The arguments are available using the +convenience variables (@pxref{Convenience Vars}) +@code{$_probe_arg0}@dots{}@code{$_probe_arg11}. Each probe argument is +an integer of the appropriate size; types are not preserved. The +convenience variable @code{$_probe_argc} holds the number of arguments +at the current probe point. + +These variables are always available, but attempts to access them at +any location other than a probe point will cause @value{GDBN} to give +an error. + + @c @ifclear BARETARGET @node Error in Breakpoints @subsection ``Cannot insert breakpoints'' @@ -6628,6 +6673,29 @@ specify the function unambiguously, e.g., if there are several functions with identical names in different source files. @end table +@cindex SystemTap static probe point +@item -p|-probe @r{[}@var{objfile}:@r{]}@r{[}@var{provider}:@r{]}@var{name} +The @sc{gnu}/Linux tool @code{SystemTap} provides a way for +applications to embed static probes. This form of linespec specifies +the location of such a static probe. See +@uref{http://sourceware.org/systemtap/wiki/AddingUserSpaceProbingToApps} +for more information on static probes. + +If @var{objfile} is given, only probes coming from that shared library +or executable are considered. If @var{provider} is given, then only +probes from that provider are considered. + +@xref{Static Probe Points}, for more information on finding and using +static probes. + +Some probes have an associated semaphore variable; for instance, this +happens automatically if you defined your probe using a DTrace-style +@file{.d} file. If your probe has a semaphore, @value{GDBN} will +automatically enable it when you specify a breakpoint using the +@samp{-p} notation. But, if you put a breakpoint at a probe's +location by some other method (e.g., @code{break file:line}), then +@value{GDBN} will not automatically set the semaphore. + @end table @@ -8884,6 +8952,10 @@ to match the format in which the data was printed. The variable @code{$_exitcode} is automatically set to the exit code when the program being debugged terminates. +@item $_probe_argc +@itemx $_probe_arg0@dots{}$_probe_arg11 +Arguments to a SystemTap static probe. @xref{Static Probe Points}. + @item $_sdata @vindex $_sdata@r{, inspect, convenience variable} The variable @code{$_sdata} contains extra collected static tracepoint @@ -10836,6 +10908,16 @@ Collect all local variables. Collect the return address. This is helpful if you want to see more of a backtrace. +@item $_probe_argc +Collects the number of arguments from the @code{SystemTap} probe at +which the tracepoint is located. +@xref{Static Probe Points,,Static Probe Points}. + +@item $_probe_arg@var{N} +Where @var{N} varies from 0 to 11. Collects the @var{N}th argument +from the @code{SystemTap} probe at which the tracepoint is located. +@xref{Static Probe Points,,Static Probe Points}. + @item $_sdata @vindex $_sdata@r{, collect} Collect static tracepoint marker specific data. Only available for diff --git a/gdb/elfread.c b/gdb/elfread.c index 9f8a7e8..5dd4d38 100644 --- a/gdb/elfread.c +++ b/gdb/elfread.c @@ -36,6 +36,8 @@ #include "demangle.h" #include "psympriv.h" #include "filenames.h" +#include "stap-probe.h" +#include "arch-utils.h" #include "gdbtypes.h" #include "value.h" #include "infcall.h" @@ -60,6 +62,21 @@ struct elfinfo asection *mdebugsect; /* Section pointer for .mdebug section */ }; +/* Per-objfile data for SystemTap probe info. */ + +static const struct objfile_data *stap_probe_key = NULL; + +/* Per-objfile data about SystemTap probes. */ + +struct stap_probe_per_objfile + { + /* The number of probes in this objfile. */ + int stap_num_probes; + + /* The probes themselves. */ + struct stap_probe *probes; + }; + static void free_elfinfo (void *); /* Minimal symbols located at the GOT entries for .plt - that is the real @@ -1579,7 +1596,273 @@ elfstab_offset_sections (struct objfile *objfile, struct partial_symtab *pst) complaint (&symfile_complaints, _("elf/stab section information missing for %s"), filename); } + +/* 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. */ + +static void +handle_probe (struct objfile *objfile, struct sdt_note *el, + struct stap_probe *ret, 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; + + ret->gdbarch = gdbarch; + + /* Provider and the name of the probe. */ + ret->provider = &el->data[3 * size]; + ret->name = memchr (ret->provider, '\0', + (char *) el->data + el->size - ret->provider); + /* Making sure there is a name. */ + if (!ret->name) + { + complaint (&symfile_complaints, _("corrupt probe when reading `%s'"), + objfile->name); + ret->provider = NULL; + ret->name = NULL; + } + else + ++ret->name; + + /* Retrieving the probe's address. */ + ret->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->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. */ + if (ret->name) + { + ret->args = memchr (ret->name, '\0', + (char *) el->data + el->size - ret->name); + + if (ret->args != NULL) + ++ret->args; + if (ret->args == NULL + || (memchr (ret->args, '\0', + (char *) el->data + el->size - ret->name) + != el->data + el->size - 1)) + { + complaint (&symfile_complaints, _("corrupt probe when reading `%s'"), + objfile->name); + ret->args = NULL; + } + } + else + ret->args = NULL; +} + +/* The name of the SystemTap section where we will find information about + the probes. */ + +#define STAP_BASE_SECTION_NAME ".stapsdt.base" + +/* Helper function which tries to find the base address of the SystemTap + base section named STAP_BASE_SECTION_NAME. */ + +static void +get_base_address_1 (bfd *abfd, asection *sect, void *obj) +{ + bfd_vma *base = (bfd_vma *) obj; + + if (*base == (bfd_vma) -1 + && (sect->flags & (SEC_DATA | SEC_ALLOC | SEC_HAS_CONTENTS)) + && sect->name && !strcmp (sect->name, STAP_BASE_SECTION_NAME)) + *base = sect->vma; +} + +/* Helper function which iterates over every section in the BFD file, + trying to find the base address of the SystemTap base section. + Returns the section address if found, or -1 otherwise. */ + +static bfd_vma +get_base_address (bfd *obfd) +{ + bfd_vma base = (bfd_vma) -1; + + bfd_map_over_sections (obfd, get_base_address_1, (void *) &base); + + return base; +} + +/* Implementation of `sym_get_probes', as documented in symfile.h. */ + +static struct stap_probe * +elf_get_probes (struct objfile *objfile, int *num_probes) +{ + struct stap_probe *ret = NULL; + struct stap_probe_per_objfile *probes_per_objfile; + + /* Initially, no probes. */ + *num_probes = 0; + + /* Have we parsed this objfile's probes already? */ + probes_per_objfile + = (struct stap_probe_per_objfile *) objfile_data (objfile, + stap_probe_key); + + if (!probes_per_objfile) + { + /* If we are here, then this is the first time we are parsing the + 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 = get_base_address (obfd); + struct sdt_note *iter; + int i; + int n = 0; + + if (! elf_tdata (obfd)->sdt_note_head) + /* There isn't any probe here. */ + return NULL; + + /* Allocating space for probe info. */ + for (iter = elf_tdata (obfd)->sdt_note_head; + iter; + iter = iter->next, ++n); + + ret = xcalloc (n, sizeof (struct stap_probe)); + + /* Parsing each probe's information. */ + for (iter = elf_tdata (obfd)->sdt_note_head, i = 0; + iter; + iter = iter->next, i++) + /* We first have to handle all the information about the + probe which is present in the section. */ + handle_probe (objfile, iter, &ret[i], base); + + /* Creating a cache for these probes in the objfile's registry. */ + probes_per_objfile = xmalloc (sizeof (struct stap_probe_per_objfile)); + + probes_per_objfile->stap_num_probes = n; + probes_per_objfile->probes = ret; + + set_objfile_data (objfile, stap_probe_key, probes_per_objfile); + } + else + ret = probes_per_objfile->probes; + + *num_probes = probes_per_objfile->stap_num_probes; + + return ret; +} + +/* Implementation of `sym_get_probe_argument_count', as documented in + symfile.h. */ + +static int +elf_get_probe_argument_count (struct objfile *objfile, + struct stap_probe *probe) +{ + const char *pargs = probe->args; + + if (!pargs || !*pargs || *pargs == ':') + /* No arguments. */ + return 0; + + return stap_get_probe_argument_count (probe); +} + +/* Implementation of `sym_evaluate_probe_argument', as documented in + symfile.h. */ + +static struct value * +elf_evaluate_probe_argument (struct objfile *objfile, + struct stap_probe *probe, + struct frame_info *frame, + int n) +{ + return stap_evaluate_probe_argument (objfile, probe, frame, n); +} + +/* Implementation of `sym_compile_to_ax', as documented in symfile.h. */ + +static void +elf_compile_to_ax (struct objfile *objfile, + struct stap_probe *probe, + struct agent_expr *expr, + struct axs_value *value, + int n) +{ + stap_compile_to_ax (objfile, probe, 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 i; + struct stap_probe_per_objfile *p + = (struct stap_probe_per_objfile *) objfile_data (objfile, + stap_probe_key); + + if (!p) + /* No probe to relocate. */ + return; + + for (i = 0; i < p->stap_num_probes; i++) + { + p->probes[i].address += ANOFFSET (delta, SECT_OFF_TEXT (objfile)); + if (p->probes[i].sem_addr) + p->probes[i].sem_addr += ANOFFSET (delta, SECT_OFF_DATA (objfile)); + } +} + +/* Helper function used to free the space allocated for storing SystemTap + probe information. */ + +static void +stap_probe_key_free (struct objfile *objfile, void *d) +{ + int i; + struct stap_probe_per_objfile *data = (struct stap_probe_per_objfile *) d; + + for (i = 0; i < data->stap_num_probes; i++) + stap_free_parsed_args (data->probes[i].parsed_args); + xfree (data->probes); + xfree (data); +} + + +/* 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 = @@ -1594,6 +1877,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 }; @@ -1612,6 +1896,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 }; @@ -1629,6 +1914,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 }; @@ -1645,6 +1931,8 @@ static const struct gnu_ifunc_fns elf_gnu_ifunc_fns = void _initialize_elfread (void) { + stap_probe_key + = register_objfile_data_with_cleanup (NULL, stap_probe_key_free); add_symtab_fns (&elf_sym_fns); elf_objfile_gnu_ifunc_cache_data = register_objfile_data (); 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: + + + + 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: +# +# +# +# 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-linux-tdep.c b/gdb/i386-linux-tdep.c index bee4a6d..533b451 100644 --- a/gdb/i386-linux-tdep.c +++ b/gdb/i386-linux-tdep.c @@ -965,6 +965,15 @@ i386_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) i386_linux_get_syscall_number); set_gdbarch_get_siginfo_type (gdbarch, linux_get_siginfo_type); + + /* 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/i386-tdep.c b/gdb/i386-tdep.c index d18aa99..9eaa772 100644 --- a/gdb/i386-tdep.c +++ b/gdb/i386-tdep.c @@ -58,8 +58,14 @@ #include "features/i386/i386-avx.c" #include "features/i386/i386-mmx.c" +#include "stap-probe.h" #include "ax.h" #include "ax-gdb.h" +#include "user-regs.h" +#include "cli/cli-utils.h" +#include "expression.h" +#include "parser-defs.h" +#include /* Register names. */ @@ -7251,6 +7257,312 @@ i386_validate_tdesc_p (struct gdbarch_tdep *tdep, return valid_p; } +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. */ +} + +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; +} + static struct gdbarch * i386_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) diff --git a/gdb/i386-tdep.h b/gdb/i386-tdep.h index 870054f..9ffbe44 100644 --- a/gdb/i386-tdep.h +++ b/gdb/i386-tdep.h @@ -379,6 +379,15 @@ 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); + +/* 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); + /* Functions and variables exported from i386bsd-tdep.c. */ diff --git a/gdb/machoread.c b/gdb/machoread.c index 88ce612..4e83874 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..72deabf 100644 --- a/gdb/objfiles.c +++ b/gdb/objfiles.c @@ -795,6 +795,11 @@ objfile_relocate1 (struct objfile *objfile, obj_section_addr (s)); } + /* Relocating SystemTap 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..1ebfd2e 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 @@ -811,7 +803,7 @@ copy_name (struct stoken token) return the index of the subexpression which is the left-hand-side of the struct operation at EXPOUT_LAST_STRUCT. */ -static int +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..442ca03 100644 --- a/gdb/parser-defs.h +++ b/gdb/parser-defs.h @@ -130,6 +130,23 @@ 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); + +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..2edc4fd 100644 --- a/gdb/ppc-linux-tdep.c +++ b/gdb/ppc-linux-tdep.c @@ -66,6 +66,14 @@ #include "features/rs6000/powerpc-isa205-vsx64l.c" #include "features/rs6000/powerpc-e500l.c" +#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 + /* Shared library operations for PowerPC-Linux. */ static struct target_so_ops powerpc_so_ops; @@ -1276,6 +1284,65 @@ ppc_linux_core_read_description (struct gdbarch *gdbarch, } } +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. */ +} + +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 +1660,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/s390-tdep.c b/gdb/s390-tdep.c index ac0c526..661d90c 100644 --- a/gdb/s390-tdep.c +++ b/gdb/s390-tdep.c @@ -55,6 +55,12 @@ #include "features/s390x-linux64v1.c" #include "features/s390x-linux64v2.c" +#include "stap-probe.h" +#include "ax.h" +#include "ax-gdb.h" +#include "user-regs.h" +#include "cli/cli-utils.h" +#include /* The tdep structure. */ @@ -2953,6 +2959,15 @@ s390_address_class_name_to_type_flags (struct gdbarch *gdbarch, return 0; } +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 +3298,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..c1e60d0 --- /dev/null +++ b/gdb/stap-probe.c @@ -0,0 +1,1689 @@ +/* SystemTap probe support for GDB. + + Copyright (C) 2011 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 . */ + +#include "defs.h" +#include "stap-probe.h" +#include "vec.h" +#include "ui-out.h" +#include "gdb_regex.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 + +/* The maximum number of arguments that a probe can have, + as defined in . */ + +#define STAP_MAX_ARGS 12 + +/* 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; +}; + +/* Structure that holds information about all arguments of a probe. */ + +struct stap_args_info +{ + /* The number of valid parsed arguments. */ + int n_args; + + /* The probe to which these arguments belong. */ + struct stap_probe *probe; + + /* Information about each argument. This is an array of `stap_probe_arg', + with each entry representing one argument. */ + struct stap_probe_arg *args; +}; + +/* 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 + , 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 +}; + +/* This dummy variable is used when parsing a probe's argument fails. + In this case, the number of arguments for this probe is zero, so that's + why this variable is useful. */ + +static struct stap_args_info dummy_stap_args_info = + { 0, NULL, NULL }; + +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; + } +} + +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; + } +} + +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; + /* 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 (isdigit (*tmp)) + { + int number = strtol (tmp, (char **) &tmp, 10); + + if (p->inside_paren_p) + tmp = skip_spaces_const (tmp); + if (!reg_ind_prefix + || strncmp (tmp, reg_ind_prefix, reg_ind_prefix_len) != 0) + goto not_displacement; + + /* 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 +not_displacement: + { + p->arg = tmp; + stap_parse_argument_conditionally (p); + if (c == '-') + write_exp_elt_opcode (UNOP_NEG); + else if (c == '~') + write_exp_elt_opcode (UNOP_COMPLEMENT); + } + } + 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); +} + +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); +} + +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 int +stap_parse_argument (const char **arg, struct type *atype, + struct gdbarch *gdbarch) +{ + struct stap_parse_info p; + volatile struct gdb_exception e; + + /* 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); + + p.saved_arg = *arg; + p.arg = *arg; + p.arg_type = atype; + p.gdbarch = gdbarch; + p.inside_paren_p = 0; + + TRY_CATCH (e, RETURN_MASK_ERROR) + { + stap_parse_argument_1 (&p, 0, STAP_OPERAND_PREC_NONE); + } + if (e.reason < 0) + { + xfree (expout); + return 0; + } + + 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 1; +} + +/* Helper function which is responsible for freeing the space allocated to + hold information about a probe's arguments. */ + +static void +stap_free_args_info (void *args_info_ptr) +{ + struct stap_args_info *a = (struct stap_args_info *) args_info_ptr; + int i; + + for (i = 0; i < a->n_args; i++) + xfree (a->args[i].aexpr); + + xfree (a->args); + xfree (a); +} + +/* 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 stap_args_info *args_info; + struct cleanup *back_to; + const char *cur = probe->args; + int current_arg = -1; + /* This is a state-machine parser, which means we will always be + in a known state when parsing an argument. The state could be + either `NEW_ARG' if we are parsing a new argument, `BITNESS' if + we are parsing the bitness-definition part (i.e., `4@'), or + `PARSE_ARG' if we are actually parsing the argument part. */ + enum + { + NEW_ARG, + BITNESS, + PARSE_ARG, + } current_state; + + /* For now, we assume everything is not going to work. */ + probe->parsed_args = &dummy_stap_args_info; + + if (!cur || !*cur || *cur == ':') + return; + + args_info = xmalloc (sizeof (struct stap_args_info)); + args_info->n_args = 0; + back_to = make_cleanup (stap_free_args_info, args_info); + args_info->args = xcalloc (STAP_MAX_ARGS, sizeof (struct stap_probe_arg)); + + /* Ok, let's start. */ + current_state = NEW_ARG; + + while (*cur) + { + switch (current_state) + { + case NEW_ARG: + ++current_arg; + + if (current_arg >= STAP_MAX_ARGS) + { + complaint (&symfile_complaints, + _("probe `%s' has more arguments than the maximum " + "allowed"), probe->name); + do_cleanups (back_to); + return; + } + + current_state = BITNESS; + break; + + case BITNESS: + { + enum stap_arg_bitness b; + int got_minus = 0; + + /* 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] != '@') + { + current_state = PARSE_ARG; + args_info->args[current_arg].bitness + = STAP_ARG_BITNESS_UNDEFINED; + break; + } + + 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->name); + do_cleanups (back_to); + return; + } + + args_info->args[current_arg].bitness = b; + args_info->args[current_arg].atype + = stap_get_expected_argument_type (probe->gdbarch, b); + /* Discard the number and the `@' sign. */ + cur += 2; + /* Move on. */ + current_state = PARSE_ARG; + } + break; + + case PARSE_ARG: + { + if (!stap_parse_argument (&cur, + args_info->args[current_arg].atype, + probe->gdbarch)) + { + /* We have tried to parse this argument, but it's + malformed. This is an error. */ + complaint (&symfile_complaints, + _("malformed argument for probe `%s'"), + probe->name); + do_cleanups (back_to); + return; + } + + if (stap_expression_debug) + dump_raw_expression (expout, gdb_stdlog, + "before conversion to prefix form"); + + prefixify_expression (expout); + + if (stap_expression_debug) + dump_prefix_expression (expout, gdb_stdlog); + + args_info->args[current_arg].aexpr = expout; + expout = NULL; + + ++args_info->n_args; + /* Start it over again. */ + cur = skip_spaces_const (cur); + current_state = NEW_ARG; + } + break; + } + + if (!*cur && current_state != NEW_ARG) + { + /* We reached the end of the argument string, but we're + still in the middle of the process of parsing an argument. + It means the argument string is malformed. */ + complaint (&symfile_complaints, + _("malformed argument for probe `%s'"), + probe->name); + do_cleanups (back_to); + return; + } + } + + args_info->args = xrealloc (args_info->args, + args_info->n_args + * sizeof (struct stap_probe_arg)); + args_info->probe = probe; + + probe->parsed_args = args_info; + + discard_cleanups (back_to); +} + +/* See definition in stap-probe.h. */ + +int +stap_get_probe_argument_count (struct stap_probe *probe) +{ + if (!probe->parsed_args) + stap_parse_probe_arguments (probe); + + return probe->parsed_args->n_args; +} + +/* 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 == '='); +} + +/* See definition in stap-probe.h. */ + +struct value * +stap_evaluate_probe_argument (struct objfile *objfile, + struct stap_probe *probe, + struct frame_info *frame, + int n) +{ + int pos = 0; + + if (!probe->parsed_args) + stap_parse_probe_arguments (probe); + + if (!probe->parsed_args->args + || n >= probe->parsed_args->n_args) + return NULL; + + /* This is needed because on some architectures (e.g., ARM) we need + the frame's gdbarch in order to compute the value of the frame + pointer. */ + probe->parsed_args->args[n].aexpr->gdbarch = get_frame_arch (frame); + + return evaluate_subexp_standard (probe->parsed_args->args[n].atype, + probe->parsed_args->args[n].aexpr, + &pos, EVAL_NORMAL); +} + +/* See definition in stap-probe.h. */ + +void +stap_compile_to_ax (struct objfile *objfile, + struct stap_probe *probe, + struct agent_expr *expr, + struct axs_value *value, + int n) +{ + union exp_element *pc; + + if (!probe->parsed_args) + stap_parse_probe_arguments (probe); + + if (!probe->parsed_args->args + || n >= probe->parsed_args->n_args) + return; + + pc = probe->parsed_args->args[n].aexpr->elts; + gen_expr (probe->parsed_args->args[n].aexpr, &pc, expr, value); + + require_rvalue (expr, value); + value->type = probe->parsed_args->args[n].atype; +} + +struct value * +stap_safe_evaluate_at_pc (struct frame_info *frame, int n) +{ + struct stap_probe *probe; + struct objfile *objfile; + int 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, + frame, + n); +} + +/* This function frees the space allocated to hold information about + the probe's parsed arguments. */ + +void +stap_free_parsed_args (struct stap_args_info *parsed_args) +{ + int i; + + if (!parsed_args + || parsed_args == &dummy_stap_args_info + || parsed_args->n_args == 0) + return; + + for (i = 0; i < parsed_args->n_args; i++) + xfree (parsed_args->args[i].aexpr); + + xfree (parsed_args->args); + xfree (parsed_args); +} + +/* A utility structure. A VEC of these is built when handling "info + probes". */ + +struct stap_probe_and_objfile +{ + /* The probe. */ + struct stap_probe *probe; + /* The probe's objfile. */ + struct objfile *objfile; +}; + +typedef struct stap_probe_and_objfile stap_entry; +DEF_VEC_O (stap_entry); + +/* 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. + Each argument is a regexp, or NULL, which matches anything. */ + +static VEC (stap_entry) * +collect_probes (char *objname, char *provider, char *probe) +{ + struct objfile *objfile; + VEC (stap_entry) *result = NULL; + struct cleanup *cleanup; + regex_t obj_pat, prov_pat, probe_pat; + + cleanup = make_cleanup (VEC_cleanup (stap_entry), &result); + + compile_rx_or_error (&prov_pat, provider, _("Invalid provider regexp")); + compile_rx_or_error (&probe_pat, probe, _("Invalid probe regexp")); + compile_rx_or_error (&obj_pat, objname, _("Invalid object file regexp")); + + ALL_OBJFILES (objfile) + { + struct stap_probe *probes; + int i, num_probes; + + 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, &num_probes); + for (i = 0; i < num_probes; ++i) + { + stap_entry entry; + + if (provider) + { + if (regexec (&prov_pat, probes[i].provider, 0, NULL, 0) != 0) + continue; + } + + if (probe) + { + if (regexec (&probe_pat, probes[i].name, 0, NULL, 0) != 0) + continue; + } + + entry.probe = &probes[i]; + entry.objfile = objfile; + VEC_safe_push (stap_entry, result, &entry); + } + } + + discard_cleanups (cleanup); + return result; +} + +/* A qsort comparison function for stap_entry objects. */ + +static int +compare_entries (const void *a, const void *b) +{ + const stap_entry *ea = a; + const stap_entry *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); +} + +/* Implementation of the "info probes" command. */ + +static void +info_probes_command (char *arg, int from_tty) +{ + char *provider, *probe = NULL, *objname = NULL; + struct cleanup *cleanup = make_cleanup (null_cleanup, NULL); + VEC (stap_entry) *items; + int i, addr_width, any_found; + stap_entry *entry; + + 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); + } + } + + items = collect_probes (objname, provider, probe); + make_cleanup (VEC_cleanup (stap_entry), &items); + make_cleanup_ui_out_table_begin_end (current_uiout, 5, + VEC_length (stap_entry, items), + "SystemTapProbes"); + + if (! VEC_empty (stap_entry, items)) + qsort (VEC_address (stap_entry, items), + VEC_length (stap_entry, items), + sizeof (stap_entry), + compare_entries); + + addr_width = 4 + (gdbarch_ptr_bit (get_current_arch ()) / 4); + + ui_out_table_header (current_uiout, 10, ui_left, "provider", _("Provider")); + ui_out_table_header (current_uiout, 10, ui_left, "name", _("Name")); + ui_out_table_header (current_uiout, addr_width - 1, ui_left, "addr", _("Where")); + ui_out_table_header (current_uiout, addr_width - 1, ui_left, "semaphore", + _("Semaphore")); + ui_out_table_header (current_uiout, 30, ui_left, "object", _("Object")); + ui_out_table_body (current_uiout); + + for (i = 0; VEC_iterate (stap_entry, 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_current_arch (), + entry->probe->address); + if (entry->probe->sem_addr == 0) + ui_out_field_skip (current_uiout, "semaphore"); + else + ui_out_field_core_addr (current_uiout, "semaphore", get_current_arch (), + entry->probe->sem_addr); + ui_out_field_string (current_uiout, "object", entry->objfile->name); + ui_out_text (current_uiout, "\n"); + + do_cleanups (inner); + } + + any_found = ! VEC_empty (stap_entry, items); + do_cleanups (cleanup); + + if (! any_found) + ui_out_message (current_uiout, 0, _("No probes matched.\n")); +} + + + +/* See definition in stap-probe.h. */ + +VEC (stap_probe_p) * +find_probes_in_objfile (struct objfile *objfile, + const char *provider, + const char *name) +{ + struct stap_probe *probes; + int i, num_probes; + VEC (stap_probe_p) *result = NULL; + + if (! objfile->sf || ! objfile->sf->sym_probe_fns) + return NULL; + + probes = objfile->sf->sym_probe_fns->sym_get_probes (objfile, &num_probes); + for (i = 0; i < num_probes; ++i) + { + if (strcmp (probes[i].provider, provider) != 0) + continue; + + if (strcmp (probes[i].name, name) != 0) + continue; + + VEC_safe_push (stap_probe_p, result, &probes[i]); + } + + return result; +} + +/* See definition in stap-probe.h. */ + +struct symtabs_and_lines +parse_stap_probe (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; + + result.sals = NULL; + result.nelts = 0; + + arg_start = *argptr; + /* The caller ensured that this starts with `-p' or `-probe'. */ + gdb_assert (arg_start + && ((strncmp (arg_start, "-p", 2) == 0 + && isspace (arg_start[2])) + || (strncmp (arg_start, "-probe", 6) == 0 + && isspace (arg_start[6])))); + + if (strncmp (arg_start, "-probe", 6) == 0) + arg_end = arg_start + 6; + else + arg_end = arg_start + 2; + arg_end = skip_spaces (arg_end); + + if (!*arg_end) + error (_("argument to `%s' missing"), + strncmp (arg_start, "-probe", 6) == 0 ? "-probe" : "-p"); + + arg = arg_end; + arg_end = skip_to_space (arg_end); + + /* 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) + { + struct stap_probe *probes; + int i, num_probes; + + 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; + + probes = objfile->sf->sym_probe_fns->sym_get_probes (objfile, + &num_probes); + for (i = 0; i < num_probes; ++i) + { + struct symtab_and_line *sal; + + if (provider && strcmp (probes[i].provider, provider) != 0) + continue; + + if (strcmp (probes[i].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 = probes[i].address; + sal->explicit_pc = 1; + sal->section = find_pc_overlay (sal->pc); + sal->pspace = current_program_space; + sal->semaphore = probes[i].sem_addr; + } + } + } + + if (result.nelts == 0) + { + throw_error (NOT_FOUND_ERROR, + _("No probe matching objfile=`%s', provider=`%s', name=`%s'"), + objfile_name ? objfile_name : _(""), + provider ? provider : _(""), + 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 stap-probe.h. */ + +struct stap_probe * +find_probe_by_pc (CORE_ADDR pc, struct objfile **objfile_out) +{ + struct objfile *objfile; + + ALL_OBJFILES (objfile) + { + struct stap_probe *probes; + int i, num_probes; + stap_entry entry; + + 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, &num_probes); + for (i = 0; i < num_probes; ++i) + { + if (probes[i].address == pc) + { + *objfile_out = objfile; + return &probes[i]; + } + } + } + + return NULL; +} + +/* 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 stap_probe *pc_probe; + int n_probes; + + /* SEL==-1 means "_probe_argc". */ + gdb_assert (sel >= -1 && sel <= STAP_MAX_ARGS); + + 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) + return value_from_longest (builtin_type (arch)->builtin_int, n_probes); + + if (sel >= n_probes) + error (_("Invalid probe argument %d -- probe has %d arguments available"), + sel, n_probes); + + return objfile->sf->sym_probe_fns->sym_evaluate_probe_argument (objfile, + pc_probe, + frame, 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 stap_probe *pc_probe; + int n_probes; + + /* SEL==-1 means "_probe_argc". */ + gdb_assert (sel >= -1 && sel <= STAP_MAX_ARGS); + + 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); +} + + + +/* Implementation of `$_probe_arg*' set of variables. */ + +static const struct internalvar_funcs probe_funcs = +{ + compute_probe_arg, + compile_probe_arg, + NULL +}; + +void _initialize_stap_probe (void); + +void +_initialize_stap_probe (void) +{ + add_info ("probes", info_probes_command, _("\ +Show available static probes.\n\ +Usage: info probes [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 match the executable or shared library name.")); + + 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); +} diff --git a/gdb/stap-probe.h b/gdb/stap-probe.h new file mode 100644 index 0000000..9b6dc7a --- /dev/null +++ b/gdb/stap-probe.h @@ -0,0 +1,144 @@ +/* SystemTap probe support for GDB. + + Copyright (C) 2011 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 . */ + +#if !defined (STAP_PROBE_H) +#define STAP_PROBE_H 1 + +#include "vec.h" + +struct stap_args_info; +struct axs_value; +struct linespec_result; + +/* Main structure which holds information about a SystemTap probe. */ + +struct stap_probe +{ + /* The provider of this probe. */ + const char *provider; + + /* The name of the probe. */ + const char *name; + + /* The address where the probe is inserted. */ + CORE_ADDR address; + + /* The address of the probe's semaphore, or 0 if this probe does not + have an associated semaphore. */ + CORE_ADDR sem_addr; + + /* Probe's arguments. Users should generally not examine this, but + should instead extract information about the arguments using the + methods provided in sym_probe_fns. */ + const char *args; + + /* Probe's arguments after parsing. This is an opaque structure that + will hold information about the arguments pointed by ARGS. */ + struct stap_args_info *parsed_args; + + /* gdbarch structure associated with this probe. */ + struct gdbarch *gdbarch; +}; + +/* 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; +}; + +typedef struct stap_probe *stap_probe_p; +DEF_VEC_P (stap_probe_p); + +/* A helper for linespec that decodes a stap probe specification. It + returns a symtabs_and_lines object and updates *ARGPTR or throws an + error. */ + +extern struct symtabs_and_lines parse_stap_probe (char **argptr, + struct linespec_result *canon); + +/* Search OBJFILE for a probe with the given PROVIDER and NAME. + 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 (stap_probe_p) *find_probes_in_objfile (struct objfile *objfile, + const char *provider, + const char *name); + +/* Given a PC, find an associated SystemTap probe. 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 stap_probe *find_probe_by_pc (CORE_ADDR pc, + struct objfile **objfile_out); + +/* Given PROBE, returns the number of arguments present in that probe's + argument string. */ + +extern int stap_get_probe_argument_count (struct stap_probe *probe); + +/* Given PARSED_ARGS, frees the space allocated to hold information about + the probe's parsed arguments. */ + +extern void stap_free_parsed_args (struct stap_args_info *parsed_args); + +/* Evaluate the probe's argument N, returning a value corresponding + to it. */ + +extern struct value *stap_evaluate_probe_argument (struct objfile *objfile, + struct stap_probe *probe, + struct frame_info *frame, + int n); + +/* Compile the probe's argument N to agent expression. */ + +extern void stap_compile_to_ax (struct objfile *objfile, + struct stap_probe *probe, + struct agent_expr *expr, + struct axs_value *value, + int n); + +/* A convenience function that finds a probe at the PC in FRAME and + evaluates argument N. If there is no probe at that location, or if + the probe does not have enough arguments, this returns NULL. */ + +extern struct value *stap_safe_evaluate_at_pc (struct frame_info *frame, + int n); + +#endif /* !defined (STAP_PROBE_H) */ diff --git a/gdb/symfile.h b/gdb/symfile.h index d4f3fd9..544d465 100644 --- a/gdb/symfile.h +++ b/gdb/symfile.h @@ -29,6 +29,11 @@ struct objfile; struct obj_section; struct obstack; struct block; +struct stap_probe; +struct value; +struct frame_info; +struct agent_expr; +struct axs_value; /* Comparison function for symbol look ups. */ @@ -297,6 +302,52 @@ struct quick_symbol_functions int need_fullname); }; +/* Structure of functions used for SystemTap probe support. If one of + these functions is provided, all must be. */ + +struct sym_probe_fns +{ + /* If non-NULL, return an array of SystemTap probe objects. The + number of objects is returned in *NUM_PROBES. */ + struct stap_probe *(*sym_get_probes) (struct objfile *, + int *num_probes); + + /* 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. */ + int (*sym_get_probe_argument_count) (struct objfile *objfile, + struct stap_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 stap_probe *probe, + struct frame_info *frame, + int 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 stap_probe *probe, + struct agent_expr *expr, + struct axs_value *value, + int 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 +418,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 c2580a1..c83dd96 100644 --- a/gdb/symtab.c +++ b/gdb/symtab.c @@ -877,6 +877,7 @@ init_sal (struct symtab_and_line *sal) sal->end = 0; sal->explicit_pc = 0; sal->explicit_line = 0; + sal->semaphore = 0; } diff --git a/gdb/symtab.h b/gdb/symtab.h index d9e5f4a..cb6206d 100644 --- a/gdb/symtab.h +++ b/gdb/symtab.h @@ -1045,6 +1045,10 @@ struct symtab_and_line CORE_ADDR end; int explicit_pc; int explicit_line; + + /* If non-zero, the semaphore location associated with a SystemTap + probe. */ + CORE_ADDR semaphore; }; extern void init_sal (struct symtab_and_line *sal); diff --git a/gdb/testsuite/gdb.base/default.exp b/gdb/testsuite/gdb.base/default.exp index 89426c2..6ec3c80 100644 --- a/gdb/testsuite/gdb.base/default.exp +++ b/gdb/testsuite/gdb.base/default.exp @@ -606,6 +606,19 @@ gdb_test_list_exact "show convenience" "show convenience" \ {$_sdata = void} \ {$_siginfo = void} \ {$_thread = 0} \ + {$_probe_argc = } \ + {$_probe_arg0 = } \ + {$_probe_arg1 = } \ + {$_probe_arg2 = } \ + {$_probe_arg3 = } \ + {$_probe_arg4 = } \ + {$_probe_arg5 = } \ + {$_probe_arg6 = } \ + {$_probe_arg7 = } \ + {$_probe_arg8 = } \ + {$_probe_arg9 = } \ + {$_probe_arg10 = } \ + {$_probe_arg11 = } \ } #test show directories diff --git a/gdb/testsuite/gdb.base/stap-probe.c b/gdb/testsuite/gdb.base/stap-probe.c new file mode 100644 index 0000000..236da96 --- /dev/null +++ b/gdb/testsuite/gdb.base/stap-probe.c @@ -0,0 +1,108 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2011 Free Software Foundation, Inc. + + 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 . */ + +#if USE_PROBES + +#define _SDT_HAS_SEMAPHORES +__extension__ unsigned short teste_user_semaphore __attribute__ ((unused)) __attribute__ ((section (".probes"))); +#define TEST teste_user_semaphore + +__extension__ unsigned short teste_two_semaphore __attribute__ ((unused)) __attribute__ ((section (".probes"))); +#define TEST2 teste_two_semaphore + +__extension__ unsigned short teste_m4_semaphore __attribute__ ((unused)) __attribute__ ((section (".probes"))); + +__extension__ unsigned short teste_pstr_semaphore __attribute__ ((unused)) __attribute__ ((section (".probes"))); + +__extension__ unsigned short teste_ps_semaphore __attribute__ ((unused)) __attribute__ ((section (".probes"))); +#else + +#define TEST 1 +#define TEST2 1 + +#endif + +#include + +/* We only support SystemTap and only the v3 form. */ +#if _SDT_NOTE_TYPE != 3 +#error "not using SystemTap v3 probes" +#endif + +struct funcs +{ + int val; + + const char *(*ps) (int); +}; + +static void +m1 (void) +{ + if (TEST2) + STAP_PROBE (teste, two); +} + +static void +m2 (void) +{ + if (TEST2) + STAP_PROBE (teste, two); +} + +static int +f (int x) +{ + if (TEST) + STAP_PROBE1 (teste, user, x); + return x+5; +} + +static const char * +pstr (int val) +{ + const char *a = "This is a test message."; + const char *b = "This is another test message."; + + STAP_PROBE3 (teste, ps, a, b, val); + + return val == 0 ? a : b; +} + +static void +m4 (const struct funcs *fs, int v) +{ + STAP_PROBE3 (teste, m4, fs->val, fs->ps (v), v); +} + +int +main() +{ + struct funcs fs; + + fs.val = 42; + fs.ps = pstr; + + f (f (23)); + m1 (); + m2 (); + + m4 (&fs, 0); + m4 (&fs, 1); + + return 0; /* last break here */ +} diff --git a/gdb/testsuite/gdb.base/stap-probe.exp b/gdb/testsuite/gdb.base/stap-probe.exp new file mode 100644 index 0000000..468efb9 --- /dev/null +++ b/gdb/testsuite/gdb.base/stap-probe.exp @@ -0,0 +1,187 @@ +# Copyright (C) 2011 Free Software Foundation, Inc. + +# 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 . + +set testfile stap-probe + +# Run the tests. We run the tests two different ways: once with a +# plain probe, and once with a probe that has an associated semaphore. +# This returns -1 on failure to compile or start, 0 otherwise. +proc stap_test {{arg ""}} { + global testfile hex + + if {$arg != ""} { + set arg "additional_flags=$arg" + set addendum ", with semaphore, not optimized" + } else { + set addendum ", no semaphore, not optimized" + } + + if {[prepare_for_testing ${testfile}.exp ${testfile} ${testfile}.c \ + [concat $arg debug]]} { + return -1 + } + + if ![runto_main] { + return -1 + } + + gdb_test "print \$_probe_argc" "No SystemTap probe at PC $hex" \ + "check argument not at probe point$addendum" + + gdb_test "info probes" \ + "teste *user *$hex .*" \ + "info probes$addendum" + + if {[runto "-p teste:user"]} { + pass "run to -p teste:user$addendum" + } else { + fail "run to -p teste:user$addendum" + } + + # Test probe arguments. + gdb_test "print \$_probe_argc" " = 1" \ + "print \$_probe_argc for probe user$addendum" + gdb_test "print \$_probe_arg0 == x" " = 1" \ + "check \$_probe_arg0 for probe user$addendum" + gdb_test "print \$_probe_arg1" \ + "Invalid probe argument 1 -- probe has 1 arguments available" \ + "check \$_probe_arg1 for probe user$addendum" + + # Set a breakpoint with multiple probe locations. + gdb_test "break -p teste:two" \ + "Breakpoint \[0-9\]+ at $hex.*2 locations.*" \ + "set multi-location probe breakpoint (probe two)$addendum" + + # Reinit GDB, set a breakpoint on probe m4. + delete_breakpoints + rerun_to_main + if {[runto "-p teste:m4"]} { + pass "run to -p teste:m4$addendum" + } else { + fail "run to -p teste:m4$addendum" + } + + # Testing probe arguments. + gdb_test "print \$_probe_argc" " = 3" \ + "print \$_probe_argc for probe m4$addendum" + gdb_test "print \$_probe_arg0" " = 42" \ + "check \$_probe_arg0 for probe m4$addendum" + gdb_test "print (const char *) \$_probe_arg1" \ + " = $hex .This is a test message.*" \ + "check \$_probe_arg1 for probe m4$addendum" + gdb_test "print \$_probe_arg2 == v" " = 1" \ + "check \$_probe_arg2 for probe m4$addendum" + + # Reinit GDB, set a breakpoint on probe ps. + delete_breakpoints + rerun_to_main + if {[runto "-p teste:ps"]} { + pass "run to -p teste:m4$addendum" + } else { + fail "run to -p teste:m4$addendum" + } + + gdb_test "print \$_probe_argc" " = 3" \ + "print \$_probe_argc for probe ps$addendum" + gdb_test "print (const char *) \$_probe_arg1" \ + " = $hex .This is another test message.*" \ + "print \$_probe_arg1 for probe ps$addendum" + + return 0 +} + +proc stap_test_no_debuginfo {{ arg "" }} { + global testfile hex + + if {$arg != ""} { + set arg "additional_flags=$arg" + set addendum ", with semaphore, optimized" + } else { + set addendum ", no semaphore, optimized" + } + + if {[prepare_for_testing ${testfile}.exp ${testfile} ${testfile}.c \ + {$arg nodebug optimize=-O2}]} { + return -1 + } + + if {[runto "-p teste:user"]} { + pass "run to -p teste:user$addendum" + } else { + fail "run to -p teste:user$addendum" + } + + # Test probe arguments. + gdb_test "print \$_probe_argc" " = 1" \ + "print \$_probe_argc for probe user$addendum" + gdb_test "print \$_probe_arg0 == 23" " = 1" \ + "check \$_probe_arg0 for probe user$addendum" + gdb_test "print \$_probe_arg1" \ + "Invalid probe argument 1 -- probe has 1 arguments available" \ + "check \$_probe_arg1 for probe user$addendum" + + # Set a breakpoint with multiple probe locations. + # In this scenario, we may expect more than 2 locations because of + # the optimizations (inlining, loop unrolling, etc). + gdb_test "break -p teste:two" \ + "Breakpoint .* at $hex.*\[0-9\]+ locations.*" \ + "set multi-location probe breakpoint (probe two)$addendum" + + # Reinit GDB, set a breakpoint on probe m4. + delete_breakpoints + rerun_to_main + if {[runto "-p teste:m4"]} { + pass "run to -p teste:m4$addendum" + } else { + fail "run to -p teste:m4$addendum" + } + + # Testing probe arguments. + gdb_test "print \$_probe_argc" " = 3" \ + "print \$_probe_argc for probe m4$addendum" + gdb_test "print \$_probe_arg0" " = 42" \ + "check \$_probe_arg0 for probe m4$addendum" + gdb_test "print (const char *) \$_probe_arg1" \ + " = $hex .This is a test message.*" \ + "check \$_probe_arg1 for probe m4$addendum" + gdb_test "print \$_probe_arg2 == 0" " = 1" \ + "check \$_probe_arg2 for probe m4$addendum" + + # Reinit GDB, set a breakpoint on probe ps. + delete_breakpoints + rerun_to_main + if {[runto "-p teste:ps"]} { + pass "run to -p teste:m4$addendum" + } else { + fail "run to -p teste:m4$addendum" + } + + gdb_test "print \$_probe_argc" " = 3" \ + "print \$_probe_argc for probe ps$addendum" + gdb_test "print (const char *) \$_probe_arg1" \ + " = $hex .This is another test message.*" \ + "print \$_probe_arg1 for probe ps$addendum" + + return 0 +} + +if {[stap_test] == -1} { + untested stap-probe.exp + return -1 +} + +stap_test "-DUSE_PROBES" +stap_test_no_debuginfo +stap_test_no_debuginfo "-DUSE_PROBES" diff --git a/gdb/testsuite/gdb.cp/nextoverthrow.exp b/gdb/testsuite/gdb.cp/nextoverthrow.exp index 7d4a0c5..2dafcab 100644 --- a/gdb/testsuite/gdb.cp/nextoverthrow.exp +++ b/gdb/testsuite/gdb.cp/nextoverthrow.exp @@ -54,6 +54,17 @@ gdb_test_multiple "print _Unwind_DebugHook" "check for unwinder hook" { } } if {!$ok} { + gdb_test_multiple "info probe" "check for stap probe in unwinder" { + -re ".*libgcc.*unwind.*\r\n$gdb_prompt $" { + pass "check for stap probe in unwinder" + set ok 1 + } + -re "\r\n$gdb_prompt $" { + } + } +} + +if {!$ok} { unsupported "nextoverthrow.exp could not find _Unwind_DebugHook" return -1 } diff --git a/gdb/testsuite/gdb.trace/stap-trace.c b/gdb/testsuite/gdb.trace/stap-trace.c new file mode 100644 index 0000000..27f317e --- /dev/null +++ b/gdb/testsuite/gdb.trace/stap-trace.c @@ -0,0 +1,71 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2011 Free Software Foundation, Inc. + + 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 . */ + +#if USE_PROBES + +#define _SDT_HAS_SEMAPHORES +__extension__ unsigned short teste_user_semaphore __attribute__ ((unused)) __attribute__ ((section (".probes"))); +#define TEST teste_user_semaphore + +__extension__ unsigned short teste_two_semaphore __attribute__ ((unused)) __attribute__ ((section (".probes"))); +#define TEST2 teste_two_semaphore + +#else + +#define TEST 1 +#define TEST2 1 + +#endif /* USE_PROBES */ + +#include + +/* We only support SystemTap and only the v3 form. */ +#if _SDT_NOTE_TYPE != 3 +#error "not using SystemTap v3 probes" +#endif + +void +m1 (int x) +{ + if (TEST2) + STAP_PROBE1 (teste, two, x); +} + +int +f (int x) +{ + if (TEST) + STAP_PROBE1(teste, user, x); + return x+5; +} + +void +nothing (void) +{ + int a = 1 + 1; + return; +} + +int +main() +{ + f (f (23)); + m1 (46); + nothing (); /* end-here */ + + return 0; +} diff --git a/gdb/testsuite/gdb.trace/stap-trace.exp b/gdb/testsuite/gdb.trace/stap-trace.exp new file mode 100644 index 0000000..b0f1d7d --- /dev/null +++ b/gdb/testsuite/gdb.trace/stap-trace.exp @@ -0,0 +1,129 @@ +# Copyright 2011 +# Free Software Foundation, Inc. + +# 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 . + +load_lib "trace-support.exp" + +if $tracelevel then { + strace $tracelevel +} + +set testfile "stap-trace" +set srcfile ${testfile}.c +set executable $testfile +set binfile $objdir/$subdir/$executable + +set ws "\[\r\n\t \]+" +set cr "\[\r\n\]+" + +# Only x86 and x86_64 targets are supported for now. + +if { ![istarget "x86_64-*"] && ![istarget "i?86-*"] } { + continue +} + +proc compile_stap_bin {{ arg "" }} { + global srcfile + global binfile + global srcdir + global subdir + + if { $arg != "" } { + set arg "additional_flags=$arg" + } + + if { [gdb_compile "$srcdir/$subdir/$srcfile" $binfile \ + executable [concat $arg debug nowarnings]] != "" } { + untested "Could not compile ${srcfile}" + return -1 + } +} + +proc prepare_for_trace_test {} { + global executable + + clean_restart $executable + + if { ![runto_main] } { + perror "Could not run to `main'." + continue + } + + gdb_breakpoint [gdb_get_line_number "end-here"] +} + +proc run_trace_experiment { test_probe msg } { + global gdb_prompt + + set test "collect $msg: start trace experiment" + gdb_test_multiple "tstart" "$test" { + -re "^tstart\r\n$gdb_prompt $" { + pass "$test" + } + } + + gdb_test "continue" \ + "Continuing.*Breakpoint \[0-9\]+.*" \ + "collect $msg: run trace experiment" + gdb_test "tstop" \ + "\[\r\n\]+" \ + "collect $msg: stop trace experiment" + gdb_test "tfind start" \ + "#0 .*" \ + "collect $msg: tfind test frame" +} + +proc gdb_collect_probe_arg { msg probe val_arg0 } { + global gdb_prompt + global cr + + prepare_for_trace_test + + gdb_test "trace $probe" \ + "Tracepoint \[0-9\]+ at .*" \ + "collect $msg: set tracepoint" + gdb_trace_setactions "collect $msg: define actions" \ + "" \ + "collect \$_probe_arg0" "^$" + + # Begin the test. + run_trace_experiment $msg $probe + + gdb_test "print \$_probe_arg0" \ + "\\$\[0-9\]+ = $val_arg0$cr" \ + "collect $msg: collected probe arg0" +} + +compile_stap_bin "" + +clean_restart $executable +if { ![runto_main] } { + perror "Could not run to `main'." + continue +} + +if { ![gdb_target_supports_trace] } { + # Test cannot run on this target. + return 1; +} + +gdb_collect_probe_arg "probe args without semaphore" "-p user" "23" +gdb_exit + +compile_stap_bin "-DUSE_PROBES" +gdb_collect_probe_arg "probe args with semaphore" "-p two" "46" + +# Finished! +gdb_test "tfind none" ".*" "" diff --git a/gdb/tracepoint.c b/gdb/tracepoint.c index ac8fade..a615ef2 100644 --- a/gdb/tracepoint.c +++ b/gdb/tracepoint.c @@ -1717,6 +1717,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 +1780,9 @@ start_tracing (char *notes) } t->number_on_target = b->number; + + for (loc = b->loc; loc; loc = loc->next) + modify_semaphore (loc, 1); } VEC_free (breakpoint_p, tp_vec); @@ -1851,9 +1855,28 @@ 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) + modify_semaphore (loc, 0); + } + + 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 86ae8fb..288709e 100644 --- a/gdb/xcoffread.c +++ b/gdb/xcoffread.c @@ -3112,6 +3112,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 }; -- Sergio