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