This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH 2/4 v3] Implement support for static and SystemTap probes


Hi,

This is the largest patch.  It contains the fully abstracted API for
creating static probes, and also the code for SystemTap probe.

OK to check-in?

Thanks.

2012-04-23  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.

---
 gdb/Makefile.in      |    8 +-
 gdb/NEWS             |    7 +
 gdb/amd64-tdep.c     |   10 +
 gdb/arm-linux-tdep.c |  132 +++++
 gdb/ax-gdb.c         |    8 +-
 gdb/ax-gdb.h         |    5 +
 gdb/breakpoint.c     |  119 ++++-
 gdb/breakpoint.h     |    4 +
 gdb/cli/cli-utils.c  |   41 ++
 gdb/cli/cli-utils.h  |   11 +
 gdb/coffread.c       |    1 +
 gdb/configure        |    2 +-
 gdb/configure.ac     |    2 +-
 gdb/dbxread.c        |    1 +
 gdb/elfread.c        |  120 ++++
 gdb/gdb_vecs.h       |    5 +
 gdb/gdbarch.c        |  250 ++++++++
 gdb/gdbarch.h        |  120 ++++
 gdb/gdbarch.sh       |   96 +++
 gdb/i386-tdep.c      |  336 +++++++++++
 gdb/i386-tdep.h      |    9 +
 gdb/machoread.c      |    1 +
 gdb/mipsread.c       |    1 +
 gdb/objfiles.c       |    5 +
 gdb/parse.c          |   26 +-
 gdb/parser-defs.h    |   24 +
 gdb/ppc-linux-tdep.c |   86 +++
 gdb/probe.c          |  788 +++++++++++++++++++++++++
 gdb/probe.h          |  221 +++++++
 gdb/s390-tdep.c      |   26 +-
 gdb/somread.c        |    1 +
 gdb/stap-probe.c     | 1558 ++++++++++++++++++++++++++++++++++++++++++++++++++
 gdb/stap-probe.h     |   52 ++
 gdb/symfile.h        |   56 ++
 gdb/symtab.c         |    1 +
 gdb/symtab.h         |    4 +
 gdb/tracepoint.c     |   30 +
 gdb/xcoffread.c      |    1 +
 38 files changed, 4133 insertions(+), 35 deletions(-)
 create mode 100644 gdb/probe.c
 create mode 100644 gdb/probe.h
 create mode 100644 gdb/stap-probe.c
 create mode 100644 gdb/stap-probe.h

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 0e87eeb..df6a0f8 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -725,8 +725,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 \
@@ -825,7 +825,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.
 
@@ -912,7 +912,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 72b4d90..a19424a 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 be536bc..ce0ee62 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"
@@ -290,6 +292,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
@@ -8138,6 +8143,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);
@@ -8223,6 +8229,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;
@@ -8962,6 +8969,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,
@@ -8969,7 +8984,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 */,
@@ -12443,6 +12458,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
@@ -12566,6 +12632,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').  */
 
@@ -14197,6 +14287,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 */,
@@ -14204,7 +14302,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))
@@ -14939,6 +15037,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;
@@ -14986,6 +15092,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;
@@ -15064,7 +15176,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 e0eeeaa..b22eb66 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 54c2399..7370309 100755
--- a/gdb/configure
+++ b/gdb/configure
@@ -12447,7 +12447,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 a40c2e5..9089f4d 100644
--- a/gdb/configure.ac
+++ b/gdb/configure.ac
@@ -1947,7 +1947,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 af115cd..aa89fac 100644
--- a/gdb/symtab.c
+++ b/gdb/symtab.c
@@ -877,6 +877,7 @@ init_sal (struct symtab_and_line *sal)
   sal->end = 0;
   sal->explicit_pc = 0;
   sal->explicit_line = 0;
+  sal->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
 };
 
-- 
1.7.7.6

-- 
Sergio


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]