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 v2] Implement new features needed for handling SystemTap probes


Hi,

This is the second and largest patch.  It received lots of comments in
the first round, and I addressed all of them (at least I think so).

Please, I would like you to take a look and see what you think of this
version.  As I said in the first e-mail, I regtested the changes here
and everything is passing.

The last version can be found here:

    http://sourceware.org/ml/gdb-patches/2012-03/msg00355.html

In this new version, I created the new file `probe.c', which contains
definitions for a new command called `info probes'.  This command can
take 2 arguments: `stap' and `all'.  I also renamed the breakpoint
command to `break -probe-stap', or `break -pstap' as an alias.  This
way, we can at least have a way to extend these SDT probes if needed (by
converting UST probes to use the same set of commands, for example).

I have also put arch-specific bits in ${arch}-tdep.[ch] files, instead
of ${arch}-linux-tdep.[ch].  Note, however, that for PPC and ARM
architectures I just used the *-linux-tdep.c files, since there is no
*-tdep.c file (and I didn't feel confident to create a new one just for that).

As a last note, I splitted the patch series in 4 patches (instead of 3),
so the documentation and testsuite bits are in patch #4 now.

Another minor comments were also addressed.

Is this OK to check-in?

gdb/ChangeLog
2012-07-04  Sergio Durigan Junior  <sergiodj@redhat.com>
	    Tom Tromey  <tromey@redhat.com>

	* Makefile.in (SFILES): Add `stap-probe'.
	(COMMON_OBS): Likewise.
	(HFILES_NO_SRCDIR): Likewise.
	* NEWS: Mention support for 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 `stap-probe.h'.
	(bkpt_stap_probe_breakpoint_ops): New variable.
	(modify_semaphore): New function.
	(insert_bp_location): Set SystemTap probe semaphore when successfully
	inserting the breakpoint.
	(remove_breakpoint_1): Clear SystemTap probe semaphore when removing
	the breakpoint.
	(momentary_breakpoint_from_master): Use the `semaphore' value.
	(add_location_to_breakpoint): Likewise.
	(break_command_1): Using proper breakpoint_ops according to the
	argument passed by the user in the command line.
	(bkpt_stap_probe_create_sals_from_address): New function.
	(bkpt_stap_probe_decode_linespec): Likewise.
	(initialize_breakpoint_ops): Initializing breakpoint_ops from
	SystemTap probes.
	* breakpoint.h (struct bp_location) <semaphore>: 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.
	* dbxread.c (aout_sym_fns): Likewise.
	* elfread.c: Include `stap-probe.h' and `arch-utils.h'.
	(STAP_BASE_SECTION_NAME): New define.
	(stap_probe_key): New variable.
	(struct stap_probe_per_objfile): New struct.
	(handle_probe): New function.
	(get_base_address_1): New function.
	(get_base_address): Likewise.
	(elf_get_probes): Likewise.
	(elf_get_probe_argument_count): Likewise.
	(elf_evaluate_probe_argument): Likewise.
	(elf_compile_to_ax): Likewise.
	(elf_symfile_relocate_probe): Likewise.
	(stap_probe_key_free): Likewise.
	(elf_probe_fns): New variable.
	(elf_sym_fns): Add `sym_probe_fns' value.
	(elf_sym_fns_lazy_psyms): Likewise.
	(elf_sym_fns_gdb_index): Likewise.
	(_initialize_elfread): Initialize objfile cache for SystemTap
	probes.
	* gdbarch.c: Regenerate.
	* gdbarch.h: Regenerate.
	* gdbarch.sh (stap_integer_prefix): New variable.
	(stap_integer_suffix): Likewise.
	(stap_register_prefix): Likewise.
	(stap_register_suffix): Likewise.
	(stap_register_indirection_prefix): Likewise.
	(stap_register_indirection_suffix): Likewise.
	(stap_gdb_register_prefix): Likewise.
	(stap_gdb_register_suffix): Likewise.
	(stap_is_single_operand): New function.
	(stap_parse_special_token): Likewise.
	(struct stap_parse_info): Forward declaration.
	* i386-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 SystemTap
	probes.
	* parse.c (prefixify_expression): Remove static declaration.
	(initialize_expout): Likewise.
	(reallocate_expout): Likewise.
	* parser-defs.h (initialize_expout): Declare function.
	(reallocate_expout): Likewise.
	(prefixify_expression): Likewise.
	* ppc-linux-tdep.c: Including headers needed to perform the parsing
	of SystemTap probes' arguments.
	(ppc_stap_is_single_operand): New function.
	(ppc_stap_parse_special_token): Likewise.
	(ppc_linux_init_abi): Initializing proper fields used by SystemTap
	probes' arguments parser.
	* probe.c: New file, for generic statically defined probe support.
	* s390-tdep.c: Including headers needed to perform the parsing of
	SystemTap probes' arguments.
	(s390_stap_is_single_operand): New function.
	(s390_gdbarch_init): Initializing proper fields used by SystemTap
	probes' arguments parser.
	* somread.c (som_sym_fns): Add `sym_probe_fns' value.
	* stap-probe.c: New file, for SystemTap probe support.
	* stap-probe.h: Likewise.
	* symfile.h (struct sym_probe_fns): New struct.
	(struct sym_fns) <sym_probe_fns>: New field.
	* symtab.c (init_sal): Initialize `semaphore' field.
	* symtab.h (struct symtab_and_line) <semaphore>: 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             |    6 +
 gdb/amd64-tdep.c     |   10 +
 gdb/arm-linux-tdep.c |  132 ++++
 gdb/ax-gdb.c         |    8 +-
 gdb/ax-gdb.h         |    5 +
 gdb/breakpoint.c     |   59 ++-
 gdb/breakpoint.h     |   14 +
 gdb/cli/cli-utils.c  |   41 ++
 gdb/cli/cli-utils.h  |   11 +
 gdb/coffread.c       |    1 +
 gdb/dbxread.c        |    1 +
 gdb/elfread.c        |  334 ++++++++++
 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          |   65 ++
 gdb/s390-tdep.c      |   26 +-
 gdb/somread.c        |    1 +
 gdb/stap-probe.c     | 1790 ++++++++++++++++++++++++++++++++++++++++++++++++++
 gdb/stap-probe.h     |  161 +++++
 gdb/symfile.h        |   58 ++
 gdb/symtab.c         |    1 +
 gdb/symtab.h         |    4 +
 gdb/tracepoint.c     |   30 +
 gdb/xcoffread.c      |    1 +
 34 files changed, 3690 insertions(+), 31 deletions(-)
 create mode 100644 gdb/probe.c
 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 44d76f2..392405c 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -724,8 +724,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 \
@@ -821,7 +821,7 @@ osdata.h procfs.h python/py-event.h python/py-events.h python/py-stopevent.h \
 python/python-internal.h python/python.h ravenscar-thread.h record.h \
 solib-darwin.h solib-ia64-hpux.h solib-spu.h windows-nat.h xcoffread.h \
 gnulib/extra/arg-nonnull.h gnulib/extra/c++defs.h gnulib/extra/warn-on-use.h \
-gnulib/stddef.in.h inline-frame.h skip.h \
+gnulib/stddef.in.h inline-frame.h skip.h stap-probe.h \
 common/common-utils.h common/xml-utils.h common/buffer.h common/ptid.h \
 common/linux-osdata.h gdb-dlfcn.h
 
@@ -910,7 +910,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 stap-probe.o \
 	common-utils.o buffer.o ptid.o gdb-dlfcn.o common-agent.o
 
 TSOBS = inflow.o
diff --git a/gdb/NEWS b/gdb/NEWS
index 5885d32..fd95c05 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -3,6 +3,12 @@
 
 *** Changes since GDB 7.4
 
+* GDB now has support for SystemTap <sys/sdt.h> probes.  You can set a
+  breakpoint using the new "-p" or "-probe" options and inspect the probe
+  arguments using the new $_probe_arg family of convenience variables.
+  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 a193099..70bcdc7 100644
--- a/gdb/amd64-tdep.c
+++ b/gdb/amd64-tdep.c
@@ -2688,6 +2688,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..43d5b29 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -60,6 +60,8 @@
 #include "jit.h"
 #include "xml-syscall.h"
 #include "parser-defs.h"
+#include "gdb_regex.h"
+#include "stap-probe.h"
 #include "cli/cli-utils.h"
 #include "continuations.h"
 #include "stack.h"
@@ -290,6 +292,9 @@ static struct breakpoint_ops momentary_breakpoint_ops;
    breakpoints.  */
 struct breakpoint_ops bkpt_breakpoint_ops;
 
+/* Breakpoints set on SystemTap probes.  */
+static struct breakpoint_ops bkpt_stap_probe_breakpoint_ops;
+
 /* A reference-counted struct command_line.  This lets multiple
    breakpoints share a single command list.  */
 struct counted_command_line
@@ -2185,6 +2190,13 @@ insert_bp_location (struct bp_location *bl,
 	  /* No overlay handling: just set the breakpoint.  */
 
 	  val = bl->owner->ops->insert_location (bl);
+
+	  if (val == 0)
+	    {
+	      /* Set the semaphore, since the breakpoint insertion has
+		 succeeded.  */
+	      stap_semaphore_up (bl->semaphore, bl->gdbarch);
+	    }
 	}
       else
 	{
@@ -3168,6 +3180,8 @@ remove_breakpoint_1 (struct bp_location *bl, insertion_state_t is)
 	  || bl->section == NULL
 	  || !(section_is_overlay (bl->section)))
 	{
+	  stap_semaphore_down (bl->semaphore, bl->gdbarch);
+
 	  /* No overlay handling: just remove the breakpoint.  */
 	  val = bl->owner->ops->remove_location (bl);
 	}
@@ -8138,6 +8152,7 @@ momentary_breakpoint_from_master (struct breakpoint *orig,
   copy->loc->address = orig->loc->address;
   copy->loc->section = orig->loc->section;
   copy->loc->pspace = orig->loc->pspace;
+  copy->loc->semaphore = orig->loc->semaphore;
 
   if (orig->loc->source_file != NULL)
     copy->loc->source_file = xstrdup (orig->loc->source_file);
@@ -8223,6 +8238,7 @@ add_location_to_breakpoint (struct breakpoint *b,
   loc->requested_address = sal->pc;
   loc->address = adjusted_address;
   loc->pspace = sal->pspace;
+  loc->semaphore = sal->semaphore;
   gdb_assert (loc->pspace != NULL);
   loc->section = sal->section;
   loc->gdbarch = loc_gdbarch;
@@ -8962,6 +8978,14 @@ break_command_1 (char *arg, int flag, int from_tty)
   enum bptype type_wanted = (flag & BP_HARDWAREFLAG
 			     ? bp_hardware_breakpoint
 			     : bp_breakpoint);
+  struct breakpoint_ops *ops;
+
+  /* Matching breakpoints on SystemTap probes (`-pstap' or `-probe-stap').  */
+  if (arg && ((strncmp (arg, "-probe-stap", 11) == 0 && isspace (arg[11]))
+	      || (strncmp (arg, "-pstap", 6) == 0 && isspace (arg[6]))))
+    ops = &bkpt_stap_probe_breakpoint_ops;
+  else
+    ops = &bkpt_breakpoint_ops;
 
   create_breakpoint (get_current_arch (),
 		     arg,
@@ -8969,7 +8993,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 +12467,33 @@ momentary_bkpt_print_mention (struct breakpoint *b)
   /* Nothing to mention.  These breakpoints are internal.  */
 }
 
+/* Specific methods for SystemTap probe breakpoints.  */
+
+static void
+bkpt_stap_probe_create_sals_from_address (char **arg,
+					  struct linespec_result *canonical,
+					  enum bptype type_wanted,
+					  char *addr_start, char **copy_arg)
+{
+  struct linespec_sals lsal;
+
+  lsal.sals = parse_stap_probe (arg, canonical);
+
+  *copy_arg = xstrdup (canonical->addr_string);
+  lsal.canonical = xstrdup (*copy_arg);
+
+  VEC_safe_push (linespec_sals, canonical->sals, &lsal);
+}
+
+static void
+bkpt_stap_probe_decode_linespec (struct breakpoint *b, char **s,
+				 struct symtabs_and_lines *sals)
+{
+  *sals = parse_stap_probe (s, NULL);
+  if (!sals->sals)
+    error (_("probe not found"));
+}
+
 /* The breakpoint_ops structure to be used in tracepoints.  */
 
 static void
@@ -14939,6 +14990,12 @@ initialize_breakpoint_ops (void)
   ops->print_it = momentary_bkpt_print_it;
   ops->print_mention = momentary_bkpt_print_mention;
 
+  /* SystemTap probe breakpoints.  */
+  ops = &bkpt_stap_probe_breakpoint_ops;
+  *ops = bkpt_breakpoint_ops;
+  ops->create_sals_from_address = bkpt_stap_probe_create_sals_from_address;
+  ops->decode_linespec = bkpt_stap_probe_decode_linespec;
+
   /* GNU v3 exception catchpoints.  */
   ops = &gnu_v3_exception_catchpoint_ops;
   *ops = bkpt_breakpoint_ops;
diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h
index e0eeeaa..2e6b953 100644
--- a/gdb/breakpoint.h
+++ b/gdb/breakpoint.h
@@ -418,6 +418,20 @@ struct bp_location
      processor's architectual constraints.  */
   CORE_ADDR requested_address;
 
+  /* If the location comes from a SystemTap probe point, and the probe
+     has an associated semaphore variable, then this is the address of
+     the semaphore.  Otherwise, this is zero.
+
+     A SystemTap semaphore is useful when the computation of a probe's
+     argument is too expensive: it may gate invocations of a probe, and must
+     be set to a non-zero value to guarantee that the probe will be hit.
+     Semaphores are treated as a counter, and the tool (GDB, in this case)
+     should increment it to enable, and decrement the semaphore when finished.
+     Overflow is currently ignored, so a large number of attaches could
+     actually cause the probe to be disabled.  This is not likely to happen
+     in practice, however.  */
+  CORE_ADDR semaphore;
+
   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 30bf3f7..f5b9fe0 100644
--- a/gdb/coffread.c
+++ b/gdb/coffread.c
@@ -2196,6 +2196,7 @@ static const struct sym_fns coff_sym_fns =
 
   default_symfile_relocate,	/* sym_relocate: Relocate a debug
 				   section.  */
+  NULL,				/* sym_probe_fns */
   &psym_functions
 };
 
diff --git a/gdb/dbxread.c b/gdb/dbxread.c
index 1725112..2d47407 100644
--- a/gdb/dbxread.c
+++ b/gdb/dbxread.c
@@ -3589,6 +3589,7 @@ static const struct sym_fns aout_sym_fns =
   default_symfile_segments,	/* Get segment information from a file.  */
   NULL,
   default_symfile_relocate,	/* Relocate a debug section.  */
+  NULL,				/* sym_probe_fns */
   &psym_functions
 };
 
diff --git a/gdb/elfread.c b/gdb/elfread.c
index 9f8a7e8..d7fa725 100644
--- a/gdb/elfread.c
+++ b/gdb/elfread.c
@@ -36,6 +36,8 @@
 #include "demangle.h"
 #include "psympriv.h"
 #include "filenames.h"
+#include "stap-probe.h"
+#include "arch-utils.h"
 #include "gdbtypes.h"
 #include "value.h"
 #include "infcall.h"
@@ -43,6 +45,11 @@
 #include "regcache.h"
 #include "bcache.h"
 
+/* The name of the SystemTap section where we will find information about
+   the probes.  */
+
+#define STAP_BASE_SECTION_NAME ".stapsdt.base"
+
 extern void _initialize_elfread (void);
 
 /* Forward declarations.  */
@@ -60,6 +67,21 @@ struct elfinfo
     asection *mdebugsect;	/* Section pointer for .mdebug section */
   };
 
+/* Per-objfile data for SystemTap probe info.  */
+
+static const struct objfile_data *stap_probe_key = NULL;
+
+/* Per-objfile data about SystemTap probes.  */
+
+struct stap_probe_per_objfile
+  {
+    /* The number of probes in this objfile.  */
+    int stap_num_probes;
+
+    /* Pointer to array of STAP_NUM_PROBES elements with the probes.  */
+    struct stap_probe *probes;
+  };
+
 static void free_elfinfo (void *);
 
 /* Minimal symbols located at the GOT entries for .plt - that is the real
@@ -1579,7 +1601,314 @@ elfstab_offset_sections (struct objfile *objfile, struct partial_symtab *pst)
     complaint (&symfile_complaints,
 	       _("elf/stab section information missing for %s"), filename);
 }
+
+/* Helper function that parses the information contained in a
+   SystemTap's probe.  Basically, the information consists in:
+
+   - Probe's PC address;
+   - Link-time section address of `.stapsdt.base' section;
+   - Link-time address of the semaphore variable, or ZERO if the
+     probe doesn't have an associated semaphore;
+   - Probe's provider name;
+   - Probe's name;
+   - Probe's argument format
+   
+   This function returns 1 if the handling was successful, and zero
+   otherwise.  */
+
+static int
+handle_probe (struct objfile *objfile, struct sdt_note *el,
+	      struct stap_probe *ret, CORE_ADDR base)
+{
+  bfd *abfd = objfile->obfd;
+  int size = bfd_get_arch_size (abfd) / 8;
+  struct gdbarch *gdbarch = get_objfile_arch (objfile);
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+  struct type *ptr_type = builtin_type (gdbarch)->builtin_data_ptr;
+  CORE_ADDR base_ref;
+
+  ret->gdbarch = gdbarch;
+
+  /* Provider and the name of the probe.  */
+  ret->provider = &el->data[3 * size];
+  ret->name = memchr (ret->provider, '\0',
+		      (char *) el->data + el->size - ret->provider);
+  /* Making sure there is a name.  */
+  if (!ret->name)
+    {
+      complaint (&symfile_complaints, _("corrupt probe 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 0;
+    }
+  else
+    ++ret->name;
+
+  /* Retrieving the probe's address.  */
+  ret->address = extract_typed_address (&el->data[0], ptr_type);
+
+  /* Link-time sh_addr of `.stapsdt.base' section.  */
+  base_ref = extract_typed_address (&el->data[size], ptr_type);
+
+  /* Semaphore address.  */
+  ret->sem_addr = extract_typed_address (&el->data[2 * size], ptr_type);
+
+  ret->address += (ANOFFSET (objfile->section_offsets,
+			     SECT_OFF_TEXT (objfile))
+		   + base - base_ref);
+  if (ret->sem_addr)
+    ret->sem_addr += (ANOFFSET (objfile->section_offsets,
+				SECT_OFF_DATA (objfile))
+		      + base - base_ref);
+
+  /* Arguments.  We can only extract the argument format if there is a valid
+     name for this probe.  */
+  ret->args = memchr (ret->name, '\0',
+		      (char *) el->data + el->size - ret->name);
+
+  if (ret->args != NULL)
+    ++ret->args;
+
+  if (ret->args == NULL || (memchr (ret->args, '\0',
+				    (char *) el->data + el->size - ret->name)
+			    != el->data + el->size - 1))
+    {
+      /* Although failing here is not good, it is still possible to use a
+	 probe without arguments.  That's why we don't return zero.  */
+      complaint (&symfile_complaints, _("corrupt probe argument when "
+					"reading `%s'"), objfile->name);
+      ret->args = NULL;
+    }
+
+  return 1;
+}
+
+/* Helper function which tries to find the base address of the SystemTap
+   base section named STAP_BASE_SECTION_NAME.  */
+
+static void
+get_base_address_1 (bfd *abfd, asection *sect, void *obj)
+{
+  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_base_address (bfd *obfd, bfd_vma *base)
+{
+  asection *ret = NULL;
+
+  bfd_map_over_sections (obfd, get_base_address_1, (void *) &ret);
+
+  if (!ret)
+    {
+      complaint (&symfile_complaints, _("could not obtain base address for "
+					"SystemTap section."));
+      return 0;
+    }
+
+  if (base)
+    *base = ret->vma;
+
+  return 1;
+}
+
+/* Implementation of `sym_get_probes', as documented in symfile.h.  */
+
+static struct stap_probe *
+elf_get_probes (struct objfile *objfile, int *num_probes)
+{
+  struct stap_probe *ret = NULL;
+  struct stap_probe_per_objfile *probes_per_objfile;
+
+  /* Initially, no probes.  */
+  *num_probes = 0;
+
+  /* Have we parsed this objfile's probes already?  */
+  probes_per_objfile = objfile_data (objfile, stap_probe_key);
+
+  if (!probes_per_objfile)
+    {
+      /* If we are here, then this is the first time we are parsing the
+	 probe's information.  We basically have to count how many probes
+	 the objfile has, and then fill in the necessary information
+	 for each one.  */
+      bfd *obfd = objfile->obfd;
+      bfd_vma base;
+      struct sdt_note *iter;
+      int cur_probe = 0;
+      int n = 0;
+
+      if (!elf_tdata (obfd)->sdt_note_head)
+	{
+	  /* There isn't any probe here.  */
+	  return NULL;
+	}
+
+      if (!get_base_address (obfd, &base))
+	{
+	  /* There was an error finding the base address for the section.
+	     Just return NULL.  */
+	  return NULL;
+	}
+
+      /* Allocating space for probe info.  */
+      for (iter = elf_tdata (obfd)->sdt_note_head;
+	   iter;
+	   iter = iter->next, ++n);
+
+      ret = xcalloc (n, sizeof (struct stap_probe));
+
+      /* Parsing each probe's information.  */
+      for (iter = elf_tdata (obfd)->sdt_note_head, cur_probe = 0;
+	   iter;
+	   iter = iter->next)
+	{
+	  /* We first have to handle all the information about the
+	     probe which is present in the section.  */
+	  if (handle_probe (objfile, iter, &ret[cur_probe], base))
+	    {
+	      /* We just increment `cur_probe' if `handle_probe' returned
+		 non-zero.  This way, we can make sure that only probes which
+		 were successfully parsed are inserted in the list of known
+		 probes.  */
+	      ++cur_probe;
+	    }
+	}
+
+      if (!cur_probe)
+	{
+	  /* If we are here, it means we have failed to parse every known
+	     probe.  */
+	  xfree (ret);
+	  complaint (&symfile_complaints, _("could not parse probe(s) "
+					    "from inferior"));
+	  return NULL;
+	}
+
+      /* Reallocating the list of probes in order to save space.  */
+      if (cur_probe < n)
+	ret = xrealloc (ret, cur_probe * sizeof (struct stap_probe));
+
+      /* Creating a cache for these probes in the objfile's registry.  */
+      probes_per_objfile = xmalloc (sizeof (struct stap_probe_per_objfile));
+
+      probes_per_objfile->stap_num_probes = cur_probe;
+      probes_per_objfile->probes = ret;
+
+      set_objfile_data (objfile, stap_probe_key, probes_per_objfile);
+    }
+  else
+    ret = probes_per_objfile->probes;
+
+  *num_probes = probes_per_objfile->stap_num_probes;
+
+  return ret;
+}
+
+/* Implementation of `sym_get_probe_argument_count', as documented in
+   symfile.h.  */
+
+static int
+elf_get_probe_argument_count (struct objfile *objfile,
+			      struct stap_probe *probe)
+{
+  const char *pargs = probe->args;
+
+  if (!pargs || !*pargs || *pargs == ':')
+    {
+      /* No arguments.  */
+      return 0;
+    }
+
+  return stap_get_probe_argument_count (probe);
+}
+
+/* Implementation of `sym_evaluate_probe_argument', as documented in
+   symfile.h.  */
+
+static struct value *
+elf_evaluate_probe_argument (struct objfile *objfile,
+			     struct stap_probe *probe,
+			     struct frame_info *frame,
+			     int n)
+{
+  return stap_evaluate_probe_argument (objfile, probe, frame, n);
+}
+
+/* Implementation of `sym_compile_to_ax', as documented in symfile.h.  */
+
+static void
+elf_compile_to_ax (struct objfile *objfile,
+		   struct stap_probe *probe,
+		   struct agent_expr *expr,
+		   struct axs_value *value,
+		   int n)
+{
+  stap_compile_to_ax (objfile, probe, expr, value, n);
+}
+
+/* Implementation of `sym_relocate_probe', as documented in symfile.h.  */
+
+static void
+elf_symfile_relocate_probe (struct objfile *objfile,
+			    struct section_offsets *new_offsets,
+			    struct section_offsets *delta)
+{
+  int i;
+  struct stap_probe_per_objfile *p = objfile_data (objfile, stap_probe_key);
+
+  if (!p)
+    {
+      /* No probe to relocate.  */
+      return;
+    }
+
+  for (i = 0; i < p->stap_num_probes; i++)
+    {
+      p->probes[i].address += ANOFFSET (delta, SECT_OFF_TEXT (objfile));
+      if (p->probes[i].sem_addr)
+	p->probes[i].sem_addr += ANOFFSET (delta, SECT_OFF_DATA (objfile));
+    }
+}
+
+/* Helper function used to free the space allocated for storing SystemTap
+   probe information.  */
+
+static void
+stap_probe_key_free (struct objfile *objfile, void *d)
+{
+  int i;
+  struct stap_probe_per_objfile *data = d;
+
+  for (i = 0; i < data->stap_num_probes; i++)
+    stap_free_parsed_args (data->probes[i].parsed_args);
+  xfree (data->probes);
+  xfree (data);
+}
+
 
+
+/* Implementation `sym_probe_fns', as documented in symfile.h.  */
+
+static const struct sym_probe_fns elf_probe_fns =
+{
+  elf_get_probes,		/* sym_get_probes */
+  elf_get_probe_argument_count,	/* sym_get_probe_argument_count */
+  elf_evaluate_probe_argument,	/* sym_evaluate_probe_argument */
+  elf_compile_to_ax,		/* sym_compile_to_ax */
+  elf_symfile_relocate_probe,	/* sym_relocate_probe */
+};
+
 /* Register that we are able to handle ELF object file formats.  */
 
 static const struct sym_fns elf_sym_fns =
@@ -1594,6 +1923,7 @@ static const struct sym_fns elf_sym_fns =
   elf_symfile_segments,		/* Get segment information from a file.  */
   NULL,
   default_symfile_relocate,	/* Relocate a debug section.  */
+  &elf_probe_fns,		/* sym_probe_fns */
   &psym_functions
 };
 
@@ -1612,6 +1942,7 @@ static const struct sym_fns elf_sym_fns_lazy_psyms =
   elf_symfile_segments,		/* Get segment information from a file.  */
   NULL,
   default_symfile_relocate,	/* Relocate a debug section.  */
+  &elf_probe_fns,		/* sym_probe_fns */
   &psym_functions
 };
 
@@ -1629,6 +1960,7 @@ static const struct sym_fns elf_sym_fns_gdb_index =
   elf_symfile_segments,		/* Get segment information from a file.  */
   NULL,
   default_symfile_relocate,	/* Relocate a debug section.  */
+  &elf_probe_fns,		/* sym_probe_fns */
   &dwarf2_gdb_index_functions
 };
 
@@ -1645,6 +1977,8 @@ static const struct gnu_ifunc_fns elf_gnu_ifunc_fns =
 void
 _initialize_elfread (void)
 {
+  stap_probe_key
+    = register_objfile_data_with_cleanup (NULL, stap_probe_key_free);
   add_symtab_fns (&elf_sym_fns);
 
   elf_objfile_gnu_ifunc_cache_data = register_objfile_data ();
diff --git a/gdb/gdbarch.c b/gdb/gdbarch.c
index c079932..056dd5a 100644
--- a/gdb/gdbarch.c
+++ b/gdb/gdbarch.c
@@ -265,6 +265,16 @@ struct gdbarch
   gdbarch_get_siginfo_type_ftype *get_siginfo_type;
   gdbarch_record_special_symbol_ftype *record_special_symbol;
   gdbarch_get_syscall_number_ftype *get_syscall_number;
+  const char * stap_integer_prefix;
+  const char * stap_integer_suffix;
+  const char * stap_register_prefix;
+  const char * stap_register_suffix;
+  const char * stap_register_indirection_prefix;
+  const char * stap_register_indirection_suffix;
+  const char * stap_gdb_register_prefix;
+  const char * stap_gdb_register_suffix;
+  gdbarch_stap_is_single_operand_ftype *stap_is_single_operand;
+  gdbarch_stap_parse_special_token_ftype *stap_parse_special_token;
   int has_global_solist;
   int has_global_breakpoints;
   gdbarch_has_shared_address_space_ftype *has_shared_address_space;
@@ -423,6 +433,16 @@ struct gdbarch startup_gdbarch =
   0,  /* get_siginfo_type */
   0,  /* record_special_symbol */
   0,  /* get_syscall_number */
+  0,  /* stap_integer_prefix */
+  0,  /* stap_integer_suffix */
+  0,  /* stap_register_prefix */
+  0,  /* stap_register_suffix */
+  0,  /* stap_register_indirection_prefix */
+  0,  /* stap_register_indirection_suffix */
+  0,  /* stap_gdb_register_prefix */
+  0,  /* stap_gdb_register_suffix */
+  0,  /* stap_is_single_operand */
+  0,  /* stap_parse_special_token */
   0,  /* has_global_solist */
   0,  /* has_global_breakpoints */
   default_has_shared_address_space,  /* has_shared_address_space */
@@ -715,6 +735,16 @@ verify_gdbarch (struct gdbarch *gdbarch)
   /* Skip verify of get_siginfo_type, has predicate.  */
   /* Skip verify of record_special_symbol, has predicate.  */
   /* Skip verify of get_syscall_number, has predicate.  */
+  /* Skip verify of stap_integer_prefix, invalid_p == 0 */
+  /* Skip verify of stap_integer_suffix, invalid_p == 0 */
+  /* Skip verify of stap_register_prefix, invalid_p == 0 */
+  /* Skip verify of stap_register_suffix, invalid_p == 0 */
+  /* Skip verify of stap_register_indirection_prefix, invalid_p == 0 */
+  /* Skip verify of stap_register_indirection_suffix, invalid_p == 0 */
+  /* Skip verify of stap_gdb_register_prefix, invalid_p == 0 */
+  /* Skip verify of stap_gdb_register_suffix, invalid_p == 0 */
+  /* Skip verify of stap_is_single_operand, has predicate.  */
+  /* Skip verify of stap_parse_special_token, has predicate.  */
   /* Skip verify of has_global_solist, invalid_p == 0 */
   /* Skip verify of has_global_breakpoints, invalid_p == 0 */
   /* Skip verify of has_shared_address_space, invalid_p == 0 */
@@ -1267,6 +1297,42 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file)
                       "gdbarch_dump: stabs_argument_has_addr = <%s>\n",
                       host_address_to_string (gdbarch->stabs_argument_has_addr));
   fprintf_unfiltered (file,
+                      "gdbarch_dump: stap_gdb_register_prefix = %s\n",
+                      gdbarch->stap_gdb_register_prefix);
+  fprintf_unfiltered (file,
+                      "gdbarch_dump: stap_gdb_register_suffix = %s\n",
+                      gdbarch->stap_gdb_register_suffix);
+  fprintf_unfiltered (file,
+                      "gdbarch_dump: stap_integer_prefix = %s\n",
+                      gdbarch->stap_integer_prefix);
+  fprintf_unfiltered (file,
+                      "gdbarch_dump: stap_integer_suffix = %s\n",
+                      gdbarch->stap_integer_suffix);
+  fprintf_unfiltered (file,
+                      "gdbarch_dump: gdbarch_stap_is_single_operand_p() = %d\n",
+                      gdbarch_stap_is_single_operand_p (gdbarch));
+  fprintf_unfiltered (file,
+                      "gdbarch_dump: stap_is_single_operand = <%s>\n",
+                      host_address_to_string (gdbarch->stap_is_single_operand));
+  fprintf_unfiltered (file,
+                      "gdbarch_dump: gdbarch_stap_parse_special_token_p() = %d\n",
+                      gdbarch_stap_parse_special_token_p (gdbarch));
+  fprintf_unfiltered (file,
+                      "gdbarch_dump: stap_parse_special_token = <%s>\n",
+                      host_address_to_string (gdbarch->stap_parse_special_token));
+  fprintf_unfiltered (file,
+                      "gdbarch_dump: stap_register_indirection_prefix = %s\n",
+                      gdbarch->stap_register_indirection_prefix);
+  fprintf_unfiltered (file,
+                      "gdbarch_dump: stap_register_indirection_suffix = %s\n",
+                      gdbarch->stap_register_indirection_suffix);
+  fprintf_unfiltered (file,
+                      "gdbarch_dump: stap_register_prefix = %s\n",
+                      gdbarch->stap_register_prefix);
+  fprintf_unfiltered (file,
+                      "gdbarch_dump: stap_register_suffix = %s\n",
+                      gdbarch->stap_register_suffix);
+  fprintf_unfiltered (file,
                       "gdbarch_dump: gdbarch_static_transform_name_p() = %d\n",
                       gdbarch_static_transform_name_p (gdbarch));
   fprintf_unfiltered (file,
@@ -3834,6 +3900,190 @@ set_gdbarch_get_syscall_number (struct gdbarch *gdbarch,
   gdbarch->get_syscall_number = get_syscall_number;
 }
 
+const char *
+gdbarch_stap_integer_prefix (struct gdbarch *gdbarch)
+{
+  gdb_assert (gdbarch != NULL);
+  /* Skip verify of stap_integer_prefix, invalid_p == 0 */
+  if (gdbarch_debug >= 2)
+    fprintf_unfiltered (gdb_stdlog, "gdbarch_stap_integer_prefix called\n");
+  return gdbarch->stap_integer_prefix;
+}
+
+void
+set_gdbarch_stap_integer_prefix (struct gdbarch *gdbarch,
+                                 const char * stap_integer_prefix)
+{
+  gdbarch->stap_integer_prefix = stap_integer_prefix;
+}
+
+const char *
+gdbarch_stap_integer_suffix (struct gdbarch *gdbarch)
+{
+  gdb_assert (gdbarch != NULL);
+  /* Skip verify of stap_integer_suffix, invalid_p == 0 */
+  if (gdbarch_debug >= 2)
+    fprintf_unfiltered (gdb_stdlog, "gdbarch_stap_integer_suffix called\n");
+  return gdbarch->stap_integer_suffix;
+}
+
+void
+set_gdbarch_stap_integer_suffix (struct gdbarch *gdbarch,
+                                 const char * stap_integer_suffix)
+{
+  gdbarch->stap_integer_suffix = stap_integer_suffix;
+}
+
+const char *
+gdbarch_stap_register_prefix (struct gdbarch *gdbarch)
+{
+  gdb_assert (gdbarch != NULL);
+  /* Skip verify of stap_register_prefix, invalid_p == 0 */
+  if (gdbarch_debug >= 2)
+    fprintf_unfiltered (gdb_stdlog, "gdbarch_stap_register_prefix called\n");
+  return gdbarch->stap_register_prefix;
+}
+
+void
+set_gdbarch_stap_register_prefix (struct gdbarch *gdbarch,
+                                  const char * stap_register_prefix)
+{
+  gdbarch->stap_register_prefix = stap_register_prefix;
+}
+
+const char *
+gdbarch_stap_register_suffix (struct gdbarch *gdbarch)
+{
+  gdb_assert (gdbarch != NULL);
+  /* Skip verify of stap_register_suffix, invalid_p == 0 */
+  if (gdbarch_debug >= 2)
+    fprintf_unfiltered (gdb_stdlog, "gdbarch_stap_register_suffix called\n");
+  return gdbarch->stap_register_suffix;
+}
+
+void
+set_gdbarch_stap_register_suffix (struct gdbarch *gdbarch,
+                                  const char * stap_register_suffix)
+{
+  gdbarch->stap_register_suffix = stap_register_suffix;
+}
+
+const char *
+gdbarch_stap_register_indirection_prefix (struct gdbarch *gdbarch)
+{
+  gdb_assert (gdbarch != NULL);
+  /* Skip verify of stap_register_indirection_prefix, invalid_p == 0 */
+  if (gdbarch_debug >= 2)
+    fprintf_unfiltered (gdb_stdlog, "gdbarch_stap_register_indirection_prefix called\n");
+  return gdbarch->stap_register_indirection_prefix;
+}
+
+void
+set_gdbarch_stap_register_indirection_prefix (struct gdbarch *gdbarch,
+                                              const char * stap_register_indirection_prefix)
+{
+  gdbarch->stap_register_indirection_prefix = stap_register_indirection_prefix;
+}
+
+const char *
+gdbarch_stap_register_indirection_suffix (struct gdbarch *gdbarch)
+{
+  gdb_assert (gdbarch != NULL);
+  /* Skip verify of stap_register_indirection_suffix, invalid_p == 0 */
+  if (gdbarch_debug >= 2)
+    fprintf_unfiltered (gdb_stdlog, "gdbarch_stap_register_indirection_suffix called\n");
+  return gdbarch->stap_register_indirection_suffix;
+}
+
+void
+set_gdbarch_stap_register_indirection_suffix (struct gdbarch *gdbarch,
+                                              const char * stap_register_indirection_suffix)
+{
+  gdbarch->stap_register_indirection_suffix = stap_register_indirection_suffix;
+}
+
+const char *
+gdbarch_stap_gdb_register_prefix (struct gdbarch *gdbarch)
+{
+  gdb_assert (gdbarch != NULL);
+  /* Skip verify of stap_gdb_register_prefix, invalid_p == 0 */
+  if (gdbarch_debug >= 2)
+    fprintf_unfiltered (gdb_stdlog, "gdbarch_stap_gdb_register_prefix called\n");
+  return gdbarch->stap_gdb_register_prefix;
+}
+
+void
+set_gdbarch_stap_gdb_register_prefix (struct gdbarch *gdbarch,
+                                      const char * stap_gdb_register_prefix)
+{
+  gdbarch->stap_gdb_register_prefix = stap_gdb_register_prefix;
+}
+
+const char *
+gdbarch_stap_gdb_register_suffix (struct gdbarch *gdbarch)
+{
+  gdb_assert (gdbarch != NULL);
+  /* Skip verify of stap_gdb_register_suffix, invalid_p == 0 */
+  if (gdbarch_debug >= 2)
+    fprintf_unfiltered (gdb_stdlog, "gdbarch_stap_gdb_register_suffix called\n");
+  return gdbarch->stap_gdb_register_suffix;
+}
+
+void
+set_gdbarch_stap_gdb_register_suffix (struct gdbarch *gdbarch,
+                                      const char * stap_gdb_register_suffix)
+{
+  gdbarch->stap_gdb_register_suffix = stap_gdb_register_suffix;
+}
+
+int
+gdbarch_stap_is_single_operand_p (struct gdbarch *gdbarch)
+{
+  gdb_assert (gdbarch != NULL);
+  return gdbarch->stap_is_single_operand != NULL;
+}
+
+int
+gdbarch_stap_is_single_operand (struct gdbarch *gdbarch, const char *s)
+{
+  gdb_assert (gdbarch != NULL);
+  gdb_assert (gdbarch->stap_is_single_operand != NULL);
+  if (gdbarch_debug >= 2)
+    fprintf_unfiltered (gdb_stdlog, "gdbarch_stap_is_single_operand called\n");
+  return gdbarch->stap_is_single_operand (gdbarch, s);
+}
+
+void
+set_gdbarch_stap_is_single_operand (struct gdbarch *gdbarch,
+                                    gdbarch_stap_is_single_operand_ftype stap_is_single_operand)
+{
+  gdbarch->stap_is_single_operand = stap_is_single_operand;
+}
+
+int
+gdbarch_stap_parse_special_token_p (struct gdbarch *gdbarch)
+{
+  gdb_assert (gdbarch != NULL);
+  return gdbarch->stap_parse_special_token != NULL;
+}
+
+int
+gdbarch_stap_parse_special_token (struct gdbarch *gdbarch, struct stap_parse_info *p)
+{
+  gdb_assert (gdbarch != NULL);
+  gdb_assert (gdbarch->stap_parse_special_token != NULL);
+  if (gdbarch_debug >= 2)
+    fprintf_unfiltered (gdb_stdlog, "gdbarch_stap_parse_special_token called\n");
+  return gdbarch->stap_parse_special_token (gdbarch, p);
+}
+
+void
+set_gdbarch_stap_parse_special_token (struct gdbarch *gdbarch,
+                                      gdbarch_stap_parse_special_token_ftype stap_parse_special_token)
+{
+  gdbarch->stap_parse_special_token = stap_parse_special_token;
+}
+
 int
 gdbarch_has_global_solist (struct gdbarch *gdbarch)
 {
diff --git a/gdb/gdbarch.h b/gdb/gdbarch.h
index 84e6ff8..2d832aa 100644
--- a/gdb/gdbarch.h
+++ b/gdb/gdbarch.h
@@ -55,6 +55,7 @@ struct core_regset_section;
 struct syscall;
 struct agent_expr;
 struct axs_value;
+struct stap_parse_info;
 
 /* The architecture associated with the connection to the target.
  
@@ -979,6 +980,125 @@ typedef LONGEST (gdbarch_get_syscall_number_ftype) (struct gdbarch *gdbarch, pti
 extern LONGEST gdbarch_get_syscall_number (struct gdbarch *gdbarch, ptid_t ptid);
 extern void set_gdbarch_get_syscall_number (struct gdbarch *gdbarch, gdbarch_get_syscall_number_ftype *get_syscall_number);
 
+/* SystemTap related fields and functions.
+   Prefix used to mark an integer constant on the architecture's assembly
+   For example, on x86 integer constants are written as:
+  
+    $10 ;; integer constant 10
+  
+   in this case, this prefix would be the character `$'. */
+
+extern const char * gdbarch_stap_integer_prefix (struct gdbarch *gdbarch);
+extern void set_gdbarch_stap_integer_prefix (struct gdbarch *gdbarch, const char * stap_integer_prefix);
+
+/* Suffix used to mark an integer constant on the architecture's assembly. */
+
+extern const char * gdbarch_stap_integer_suffix (struct gdbarch *gdbarch);
+extern void set_gdbarch_stap_integer_suffix (struct gdbarch *gdbarch, const char * stap_integer_suffix);
+
+/* Prefix used to mark a register name on the architecture's assembly.
+   For example, on x86 the register name is written as:
+  
+    %eax ;; register eax
+  
+   in this case, this prefix would be the character `%'. */
+
+extern const char * gdbarch_stap_register_prefix (struct gdbarch *gdbarch);
+extern void set_gdbarch_stap_register_prefix (struct gdbarch *gdbarch, const char * stap_register_prefix);
+
+/* Suffix used to mark a register name on the architecture's assembly */
+
+extern const char * gdbarch_stap_register_suffix (struct gdbarch *gdbarch);
+extern void set_gdbarch_stap_register_suffix (struct gdbarch *gdbarch, const char * stap_register_suffix);
+
+/* Prefix used to mark a register indirection on the architecture's assembly.
+   For example, on x86 the register indirection is written as:
+  
+    (%eax) ;; indirecting eax
+  
+   in this case, this prefix would be the charater `('.
+  
+   Please note that we use the indirection prefix also for register
+   displacement, e.g., `4(%eax)' on x86. */
+
+extern const char * gdbarch_stap_register_indirection_prefix (struct gdbarch *gdbarch);
+extern void set_gdbarch_stap_register_indirection_prefix (struct gdbarch *gdbarch, const char * stap_register_indirection_prefix);
+
+/* Suffix used to mark a register indirection on the architecture's assembly.
+   For example, on x86 the register indirection is written as:
+  
+    (%eax) ;; indirecting eax
+  
+   in this case, this prefix would be the charater `)'.
+  
+   Please note that we use the indirection suffix also for register
+   displacement, e.g., `4(%eax)' on x86. */
+
+extern const char * gdbarch_stap_register_indirection_suffix (struct gdbarch *gdbarch);
+extern void set_gdbarch_stap_register_indirection_suffix (struct gdbarch *gdbarch, const char * stap_register_indirection_suffix);
+
+/* Prefix used to name a register using GDB's nomenclature.
+  
+   For example, on PPC a register is represented by a number in the assembly
+   language (e.g., `10' is the 10th general-purpose register).  However,
+   inside GDB this same register has an `r' appended to its name, so the 10th
+   register would be represented as `r10' internally. */
+
+extern const char * gdbarch_stap_gdb_register_prefix (struct gdbarch *gdbarch);
+extern void set_gdbarch_stap_gdb_register_prefix (struct gdbarch *gdbarch, const char * stap_gdb_register_prefix);
+
+/* Suffix used to name a register using GDB's nomenclature. */
+
+extern const char * gdbarch_stap_gdb_register_suffix (struct gdbarch *gdbarch);
+extern void set_gdbarch_stap_gdb_register_suffix (struct gdbarch *gdbarch, const char * stap_gdb_register_suffix);
+
+/* Check if S is a single operand.
+  
+   Single operands can be:
+    - Literal integers, e.g. `$10' on x86
+    - Register access, e.g. `%eax' on x86
+    - Register indirection, e.g. `(%eax)' on x86
+    - Register displacement, e.g. `4(%eax)' on x86
+  
+   This function should check for these patterns on the string
+   and return 1 if some were found, or zero otherwise.  Please try to match
+   as much info as you can from the string, i.e., if you have to match
+   something like `(%', do not match just the `('. */
+
+extern int gdbarch_stap_is_single_operand_p (struct gdbarch *gdbarch);
+
+typedef int (gdbarch_stap_is_single_operand_ftype) (struct gdbarch *gdbarch, const char *s);
+extern int gdbarch_stap_is_single_operand (struct gdbarch *gdbarch, const char *s);
+extern void set_gdbarch_stap_is_single_operand (struct gdbarch *gdbarch, gdbarch_stap_is_single_operand_ftype *stap_is_single_operand);
+
+/* Function used to handle a "special case" in the parser.
+  
+   A "special case" is considered to be an unknown token, i.e., a token
+   that the parser does not know how to parse.  A good example of special
+   case would be ARM's register displacement syntax:
+  
+    [R0, #4]  ;; displacing R0 by 4
+  
+   Since the parser assumes that a register displacement is of the form:
+  
+    <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..72deabf 100644
--- a/gdb/objfiles.c
+++ b/gdb/objfiles.c
@@ -795,6 +795,11 @@ objfile_relocate1 (struct objfile *objfile,
 				obj_section_addr (s));
     }
 
+  /* Relocating SystemTap probes.  */
+  if (objfile->sf && objfile->sf->sym_probe_fns)
+    objfile->sf->sym_probe_fns->sym_relocate_probe (objfile,
+						    new_offsets, delta);
+
   /* Data changed.  */
   return 1;
 }
diff --git a/gdb/parse.c b/gdb/parse.c
index 32a3bd6..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..04b123b
--- /dev/null
+++ b/gdb/probe.c
@@ -0,0 +1,65 @@
+/* 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/>.  */
+
+#include "defs.h"
+#include "stap-probe.h"
+#include "command.h"
+#include "cli/cli-cmds.h"
+
+/* Implementation of the `info probes' command.  */
+
+static void
+info_probes_command (char *arg, int from_tty)
+{
+  /* If we are here, it means the user has not specified any
+     argument, or has specified `all'.  In either case, we should
+     print information about all types of probes.  */
+  info_probes_stap_command (arg, from_tty);
+}
+
+void _initialize_probe (void);
+
+void
+_initialize_probe (void)
+{
+  static struct cmd_list_element *info_probes_cmdlist;
+
+  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);
+
+  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);
+}
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..f12af12
--- /dev/null
+++ b/gdb/stap-probe.c
@@ -0,0 +1,1790 @@
+/* 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 "vec.h"
+#include "ui-out.h"
+#include "gdb_regex.h"
+#include "objfiles.h"
+#include "arch-utils.h"
+#include "command.h"
+#include "gdbcmd.h"
+#include "filenames.h"
+#include "value.h"
+#include "exceptions.h"
+#include "ax.h"
+#include "ax-gdb.h"
+#include "complaints.h"
+#include "cli/cli-utils.h"
+#include "linespec.h"
+#include "user-regs.h"
+#include "parser-defs.h"
+#include "language.h"
+
+#include <ctype.h>
+
+/* The maximum number of arguments that a probe can have,
+   as defined in <sys/sdt.h>.  */
+
+#define STAP_MAX_ARGS 12
+
+/* Should we display debug information for the probe's argument expression
+   parsing?  */
+
+static int stap_expression_debug = 0;
+
+/* The various possibilities of bitness defined for a probe's argument.
+
+   The relationship is:
+
+   - STAP_ARG_BITNESS_UNDEFINED:  The user hasn't specified the bitness.
+   - STAP_ARG_BITNESS_32BIT_UNSIGNED:  argument string starts with `4@'.
+   - STAP_ARG_BITNESS_32BIT_SIGNED:  argument string starts with `-4@'.
+   - STAP_ARG_BITNESS_64BIT_UNSIGNED:  argument string starts with `8@'.
+   - STAP_ARG_BITNESS_64BIT_SIGNED:  argument string starts with `-8@'.  */
+
+enum stap_arg_bitness
+{
+  STAP_ARG_BITNESS_UNDEFINED,
+  STAP_ARG_BITNESS_32BIT_UNSIGNED,
+  STAP_ARG_BITNESS_32BIT_SIGNED,
+  STAP_ARG_BITNESS_64BIT_UNSIGNED,
+  STAP_ARG_BITNESS_64BIT_SIGNED,
+};
+
+/* The following structure represents a single argument for the probe.  */
+
+struct stap_probe_arg
+{
+  /* The bitness of this argument.  */
+  enum stap_arg_bitness bitness;
+
+  /* The corresponding `struct type *' to the bitness.  */
+  struct type *atype;
+
+  /* The argument converted to an internal GDB expression.  */
+  struct expression *aexpr;
+};
+
+/* Structure that holds information about all arguments of a probe.  */
+
+struct stap_args_info
+{
+  /* The number of valid parsed arguments.  */
+  int n_args;
+
+  /* The probe to which these arguments belong.  */
+  struct stap_probe *probe;
+
+  /* Information about each argument.  This is an array of `stap_probe_arg',
+     with each entry representing one argument.  */
+  struct stap_probe_arg *args;
+};
+
+/* When parsing the arguments, we have to establish different precedences
+   for the various kinds of asm operators.  This enumeration represents those
+   precedences.
+
+   This logic behind this is available at
+   <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
+};
+
+/* This dummy variable is used when parsing a probe's argument fails.
+   In this case, the number of arguments for this probe is zero, so that's
+   why this variable is useful.  */
+
+static struct stap_args_info dummy_stap_args_info =
+  { 0, NULL, NULL };
+
+static void stap_parse_argument_1 (struct stap_parse_info *p, int has_lhs,
+				   enum stap_operand_prec prec);
+
+static void stap_parse_argument_conditionally (struct stap_parse_info *p);
+
+/* Returns 1 if *S is an operator, zero otherwise.  */
+
+static int stap_is_operator (char op);
+
+static void
+show_stapexpressiondebug (struct ui_file *file, int from_tty,
+			  struct cmd_list_element *c, const char *value)
+{
+  fprintf_filtered (file, _("SystemTap Probe expression debugging is %s.\n"),
+		    value);
+}
+
+/* Returns the operator precedence level of OP, or STAP_OPERAND_PREC_NONE
+   if the operator code was not recognized.  */
+
+static enum stap_operand_prec
+stap_get_operator_prec (enum exp_opcode op)
+{
+  switch (op)
+    {
+    case BINOP_LOGICAL_OR:
+      return STAP_OPERAND_PREC_LOGICAL_OR;
+
+    case BINOP_LOGICAL_AND:
+      return STAP_OPERAND_PREC_LOGICAL_AND;
+
+    case BINOP_ADD:
+    case BINOP_SUB:
+    case BINOP_EQUAL:
+    case BINOP_NOTEQUAL:
+    case BINOP_LESS:
+    case BINOP_LEQ:
+    case BINOP_GTR:
+    case BINOP_GEQ:
+      return STAP_OPERAND_PREC_ADD_CMP;
+
+    case BINOP_BITWISE_IOR:
+    case BINOP_BITWISE_AND:
+    case BINOP_BITWISE_XOR:
+    case UNOP_LOGICAL_NOT:
+      return STAP_OPERAND_PREC_BITWISE;
+
+    case BINOP_MUL:
+    case BINOP_DIV:
+    case BINOP_REM:
+    case BINOP_LSH:
+    case BINOP_RSH:
+      return STAP_OPERAND_PREC_MUL;
+
+    default:
+      return STAP_OPERAND_PREC_NONE;
+    }
+}
+
+/* Given S, read the operator in it and fills the OP pointer with its code.
+   Return 1 on success, zero if the operator was not recognized.  */
+
+static int
+stap_get_opcode (const char **s, enum exp_opcode *op)
+{
+  const char c = **s;
+  int ret = 1;
+
+  *s += 1;
+
+  switch (c)
+    {
+    case '*':
+      *op = BINOP_MUL;
+      break;
+
+    case '/':
+      *op = BINOP_DIV;
+      break;
+
+    case '%':
+      *op = BINOP_REM;
+    break;
+
+    case '<':
+      *op = BINOP_LESS;
+      if (**s == '<')
+	{
+	  *s += 1;
+	  *op = BINOP_LSH;
+	}
+      else if (**s == '=')
+	{
+	  *s += 1;
+	  *op = BINOP_LEQ;
+	}
+      else if (**s == '>')
+	{
+	  *s += 1;
+	  *op = BINOP_NOTEQUAL;
+	}
+    break;
+
+    case '>':
+      *op = BINOP_GTR;
+      if (**s == '>')
+	{
+	  *s += 1;
+	  *op = BINOP_RSH;
+	}
+      else if (**s == '=')
+	{
+	  *s += 1;
+	  *op = BINOP_GEQ;
+	}
+    break;
+
+    case '|':
+      *op = BINOP_BITWISE_IOR;
+      if (**s == '|')
+	{
+	  *s += 1;
+	  *op = BINOP_LOGICAL_OR;
+	}
+    break;
+
+    case '&':
+      *op = BINOP_BITWISE_AND;
+      if (**s == '&')
+	{
+	  *s += 1;
+	  *op = BINOP_LOGICAL_AND;
+	}
+    break;
+
+    case '^':
+      *op = BINOP_BITWISE_XOR;
+      break;
+
+    case '!':
+      *op = UNOP_LOGICAL_NOT;
+      break;
+
+    case '+':
+      *op = BINOP_ADD;
+      break;
+
+    case '-':
+      *op = BINOP_SUB;
+      break;
+
+    case '=':
+      if (**s != '=')
+	{
+	  ret = 0;
+	  break;
+	}
+      *op = BINOP_EQUAL;
+      break;
+
+    default:
+      /* We didn't find any operator.  */
+      *s -= 1;
+      return 0;
+    }
+
+  return ret;
+}
+
+/* Given the bitness of the argument, represented by B, return the
+   corresponding `struct type *'.  */
+
+static struct type *
+stap_get_expected_argument_type (struct gdbarch *gdbarch,
+				 enum stap_arg_bitness b)
+{
+  switch (b)
+    {
+    case STAP_ARG_BITNESS_UNDEFINED:
+      if (gdbarch_addr_bit (gdbarch) == 32)
+	return builtin_type (gdbarch)->builtin_uint32;
+      else
+	return builtin_type (gdbarch)->builtin_uint64;
+
+    case STAP_ARG_BITNESS_32BIT_SIGNED:
+      return builtin_type (gdbarch)->builtin_int32;
+
+    case STAP_ARG_BITNESS_32BIT_UNSIGNED:
+      return builtin_type (gdbarch)->builtin_uint32;
+
+    case STAP_ARG_BITNESS_64BIT_SIGNED:
+      return builtin_type (gdbarch)->builtin_int64;
+
+    case STAP_ARG_BITNESS_64BIT_UNSIGNED:
+      return builtin_type (gdbarch)->builtin_uint64;
+
+    default:
+      internal_error (__FILE__, __LINE__,
+		      _("Undefined bitness for probe."));
+      break;
+    }
+}
+
+/* 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;
+}
+
+/* Helper function which is responsible for freeing the space allocated to
+   hold information about a probe's arguments.  */
+
+static void
+stap_free_args_info (void *args_info_ptr)
+{
+  struct stap_args_info *a = (struct stap_args_info *) args_info_ptr;
+  int i;
+
+  for (i = 0; i < a->n_args; i++)
+    xfree (a->args[i].aexpr);
+
+  xfree (a->args);
+  xfree (a);
+}
+
+/* Function which parses an argument string from PROBE, correctly splitting
+   the arguments and storing their information in properly ways.
+
+   Consider the following argument string (x86 syntax):
+
+   `4@%eax 4@$10'
+
+   We have two arguments, `%eax' and `$10', both with 32-bit unsigned bitness.
+   This function basically handles them, properly filling some structures with
+   this information.  */
+
+static void
+stap_parse_probe_arguments (struct stap_probe *probe)
+{
+  struct stap_args_info *args_info;
+  struct cleanup *back_to;
+  const char *cur = probe->args;
+  int current_arg = -1;
+
+  /* This is a state-machine parser, which means we will always be
+     in a known state when parsing an argument.  The state could be
+     either `NEW_ARG' if we are parsing a new argument, `BITNESS' if
+     we are parsing the bitness-definition part (i.e., `4@'), or
+     `PARSE_ARG' if we are actually parsing the argument part.  */
+  enum
+    {
+      NEW_ARG,
+      BITNESS,
+      PARSE_ARG,
+    } current_state;
+
+  /* For now, we assume everything is not going to work.  */
+  probe->parsed_args = &dummy_stap_args_info;
+
+  if (!cur || !*cur || *cur == ':')
+    return;
+
+  args_info = xmalloc (sizeof (struct stap_args_info));
+  args_info->n_args = 0;
+  back_to = make_cleanup (stap_free_args_info, args_info);
+  args_info->args = xcalloc (STAP_MAX_ARGS, sizeof (struct stap_probe_arg));
+
+  current_state = NEW_ARG;
+
+  while (*cur)
+    {
+      switch (current_state)
+	{
+	case NEW_ARG:
+	  ++current_arg;
+
+	  if (current_arg >= STAP_MAX_ARGS)
+	    {
+	      complaint (&symfile_complaints,
+			 _("probe `%s' has more arguments than the maximum "
+			   "allowed"), probe->name);
+	      do_cleanups (back_to);
+	      return;
+	    }
+
+	  current_state = BITNESS;
+	  break;
+
+	case BITNESS:
+	    {
+	      enum stap_arg_bitness b;
+	      int got_minus = 0;
+
+	      /* We expect to find something like:
+
+		 N@OP
+
+		 Where `N' can be [+,-][4,8].  This is not mandatory, so
+		 we check it here.  If we don't find it, go to the next
+		 state.  */
+	      if ((*cur == '-' && cur[1] && cur[2] != '@')
+		  && cur[1] != '@')
+		{
+		  current_state = PARSE_ARG;
+		  args_info->args[current_arg].bitness
+		    = STAP_ARG_BITNESS_UNDEFINED;
+		  break;
+		}
+
+	      if (*cur == '-')
+		{
+		  /* Discard the `-'.  */
+		  ++cur;
+		  got_minus = 1;
+		}
+
+	      if (*cur == '4')
+		b = (got_minus ? STAP_ARG_BITNESS_32BIT_SIGNED
+		     : STAP_ARG_BITNESS_32BIT_UNSIGNED);
+	      else if (*cur == '8')
+		b = (got_minus ? STAP_ARG_BITNESS_64BIT_SIGNED
+		     : STAP_ARG_BITNESS_64BIT_UNSIGNED);
+	      else
+		{
+		  /* We have an error, because we don't expect anything
+		     except 4 and 8.  */
+		  complaint (&symfile_complaints,
+			     _("unrecognized bitness `%c' for probe `%s'"),
+			     *cur, probe->name);
+		  do_cleanups (back_to);
+		  return;
+		}
+
+	      args_info->args[current_arg].bitness = b;
+	      args_info->args[current_arg].atype
+		= stap_get_expected_argument_type (probe->gdbarch, b);
+
+	      /* Discard the number and the `@' sign.  */
+	      cur += 2;
+
+	      /* Move on.  */
+	      current_state = PARSE_ARG;
+	    }
+	  break;
+
+	case PARSE_ARG:
+	    {
+	      struct expression *ret;
+
+	      ret = stap_parse_argument (&cur,
+					 args_info->args[current_arg].atype,
+					 probe->gdbarch);
+
+	      if (stap_expression_debug)
+		dump_raw_expression (ret, gdb_stdlog,
+				     "before conversion to prefix form");
+
+	      prefixify_expression (ret);
+
+	      if (stap_expression_debug)
+		dump_prefix_expression (ret, gdb_stdlog);
+
+	      args_info->args[current_arg].aexpr = ret;
+
+	      ++args_info->n_args;
+
+	      /* Start it over again.  */
+	      cur = skip_spaces_const (cur);
+	      current_state = NEW_ARG;
+	    }
+	  break;
+	}
+
+      if (!*cur && current_state != NEW_ARG)
+	{
+	  /* We reached the end of the argument string, but we're
+	     still in the middle of the process of parsing an argument.
+	     It means the argument string is malformed.  */
+	  complaint (&symfile_complaints,
+		     _("malformed argument for probe `%s'"),
+		     probe->name);
+	  do_cleanups (back_to);
+	  return;
+	}
+    }
+
+  args_info->args = xrealloc (args_info->args,
+			      args_info->n_args
+			      * sizeof (struct stap_probe_arg));
+  args_info->probe = probe;
+
+  probe->parsed_args = args_info;
+
+  discard_cleanups (back_to);
+}
+
+/* See definition in stap-probe.h.  */
+
+int
+stap_get_probe_argument_count (struct stap_probe *probe)
+{
+  if (!probe->parsed_args)
+    stap_parse_probe_arguments (probe);
+
+  return probe->parsed_args->n_args;
+}
+
+/* Return 1 if OP is a valid operator inside a probe argument, or zero
+   otherwise.  */
+
+static int
+stap_is_operator (char op)
+{
+  return (op == '+' || op == '-' || op == '*' || op == '/'
+	  || op == '>' || op == '<' || op == '!' || op == '^'
+	  || op == '|' || op == '&' || op == '%' || op == '=');
+}
+
+/* See definition in stap-probe.h.  */
+
+struct value *
+stap_evaluate_probe_argument (struct objfile *objfile,
+			      struct stap_probe *probe,
+			      struct frame_info *frame,
+			      int n)
+{
+  int pos = 0;
+
+  if (!probe->parsed_args)
+    stap_parse_probe_arguments (probe);
+
+  if (!probe->parsed_args->args
+      || n >= probe->parsed_args->n_args)
+    return NULL;
+
+  /* This is needed because on some architectures (e.g., ARM) we need
+     the frame's gdbarch in order to compute the value of the frame
+     pointer.  */
+  probe->parsed_args->args[n].aexpr->gdbarch = get_frame_arch (frame);
+
+  return evaluate_subexp_standard (probe->parsed_args->args[n].atype,
+				   probe->parsed_args->args[n].aexpr,
+				   &pos, EVAL_NORMAL);
+}
+
+/* See definition in stap-probe.h.  */
+
+void
+stap_compile_to_ax (struct objfile *objfile,
+		    struct stap_probe *probe,
+		    struct agent_expr *expr,
+		    struct axs_value *value,
+		    int n)
+{
+  union exp_element *pc;
+
+  if (!probe->parsed_args)
+    stap_parse_probe_arguments (probe);
+
+  if (!probe->parsed_args->args
+      || n >= probe->parsed_args->n_args)
+    return;
+
+  pc = probe->parsed_args->args[n].aexpr->elts;
+  gen_expr (probe->parsed_args->args[n].aexpr, &pc, expr, value);
+
+  require_rvalue (expr, value);
+  value->type = probe->parsed_args->args[n].atype;
+}
+
+/* See comments in stap-probe.h.  */
+
+struct value *
+stap_safe_evaluate_at_pc (struct frame_info *frame, int n)
+{
+  struct stap_probe *probe;
+  struct objfile *objfile;
+  int n_probes;
+
+  probe = find_probe_by_pc (get_frame_pc (frame), &objfile);
+  if (!probe)
+    return NULL;
+  gdb_assert (objfile->sf && objfile->sf->sym_probe_fns);
+
+  n_probes
+    = objfile->sf->sym_probe_fns->sym_get_probe_argument_count (objfile,
+								probe);
+  if (n >= n_probes)
+    return NULL;
+
+  return objfile->sf->sym_probe_fns->sym_evaluate_probe_argument (objfile,
+								  probe,
+								  frame,
+								  n);
+}
+
+/* This function frees the space allocated to hold information about
+   the probe's parsed arguments.  */
+
+void
+stap_free_parsed_args (struct stap_args_info *parsed_args)
+{
+  int i;
+
+  if (!parsed_args
+      || parsed_args == &dummy_stap_args_info
+      || parsed_args->n_args == 0)
+    return;
+
+  for (i = 0; i < parsed_args->n_args; i++)
+    xfree (parsed_args->args[i].aexpr);
+
+  xfree (parsed_args->args);
+  xfree (parsed_args);
+}
+
+/* A utility structure.  A VEC of these is built when handling "info
+   probes".  */
+
+struct stap_probe_and_objfile
+{
+  /* The probe.  */
+  struct stap_probe *probe;
+
+  /* The probe's objfile.  */
+  struct objfile *objfile;
+};
+
+typedef struct stap_probe_and_objfile stap_entry;
+DEF_VEC_O (stap_entry);
+
+/* A helper function for collect_probes that compiles a regexp and
+   throws an exception on error.  This installs a cleanup to free the
+   resulting pattern on success.  If RX is NULL, this does nothing.  */
+
+static void
+compile_rx_or_error (regex_t *pattern, const char *rx, const char *message)
+{
+  int code;
+
+  if (!rx)
+    return;
+
+  code = regcomp (pattern, rx, REG_NOSUB);
+  if (code == 0)
+    make_regfree_cleanup (pattern);
+  else
+    {
+      char *err = get_regcomp_error (code, pattern);
+
+      make_cleanup (xfree, err);
+      error ("%s: %s", message, err);
+    }
+}
+
+/* Make a vector of probes matching OBJNAME, PROVIDER, and PROBE.
+   Each argument is a regexp, or NULL, which matches anything.  */
+
+static VEC (stap_entry) *
+collect_probes (char *objname, char *provider, char *probe)
+{
+  struct objfile *objfile;
+  VEC (stap_entry) *result = NULL;
+  struct cleanup *cleanup, *cleanup_temps;
+  regex_t obj_pat, prov_pat, probe_pat;
+
+  cleanup = make_cleanup (VEC_cleanup (stap_entry), &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, _("Invalid probe regexp"));
+  compile_rx_or_error (&obj_pat, objname, _("Invalid object file regexp"));
+
+  ALL_OBJFILES (objfile)
+  {
+    struct stap_probe *probes;
+    int i, num_probes;
+
+    if (! objfile->sf || ! objfile->sf->sym_probe_fns)
+      continue;
+
+    if (objname)
+      {
+	if (regexec (&obj_pat, objfile->name, 0, NULL, 0) != 0)
+	  continue;
+      }
+
+    probes = objfile->sf->sym_probe_fns->sym_get_probes (objfile, &num_probes);
+    for (i = 0; i < num_probes; ++i)
+      {
+	stap_entry entry;
+
+	if (provider)
+	  {
+	    if (regexec (&prov_pat, probes[i].provider, 0, NULL, 0) != 0)
+	      continue;
+	  }
+
+	if (probe)
+	  {
+	    if (regexec (&probe_pat, probes[i].name, 0, NULL, 0) != 0)
+	      continue;
+	  }
+
+	entry.probe = &probes[i];
+	entry.objfile = objfile;
+	VEC_safe_push (stap_entry, result, &entry);
+      }
+  }
+
+  do_cleanups (cleanup_temps);
+  discard_cleanups (cleanup);
+  return result;
+}
+
+/* A qsort comparison function for stap_entry objects.  */
+
+static int
+compare_entries (const void *a, const void *b)
+{
+  const stap_entry *ea = a;
+  const stap_entry *eb = b;
+  int v;
+
+  v = strcmp (ea->probe->provider, eb->probe->provider);
+  if (v)
+    return v;
+
+  v = strcmp (ea->probe->name, eb->probe->name);
+  if (v)
+    return v;
+
+  if (ea->probe->address < eb->probe->address)
+    return -1;
+  if (ea->probe->address > eb->probe->address)
+    return 1;
+
+  return strcmp (ea->objfile->name, eb->objfile->name);
+}
+
+void
+info_probes_stap_command (char *arg, int from_tty)
+{
+  char *provider, *probe = NULL, *objname = NULL;
+  struct cleanup *cleanup = make_cleanup (null_cleanup, NULL);
+  VEC (stap_entry) *items;
+  int i, addr_width, any_found;
+  stap_entry *entry;
+
+  provider = extract_arg (&arg);
+  if (provider)
+    {
+      make_cleanup (xfree, provider);
+
+      probe = extract_arg (&arg);
+      if (probe)
+	{
+	  make_cleanup (xfree, probe);
+
+	  objname = extract_arg (&arg);
+	  if (objname)
+	    make_cleanup (xfree, objname);
+	}
+    }
+
+  items = collect_probes (objname, provider, probe);
+  make_cleanup (VEC_cleanup (stap_entry), &items);
+  make_cleanup_ui_out_table_begin_end (current_uiout, 5,
+				       VEC_length (stap_entry, items),
+				       "SystemTapProbes");
+
+  if (!VEC_empty (stap_entry, items))
+    qsort (VEC_address (stap_entry, items),
+	   VEC_length (stap_entry, items),
+	   sizeof (stap_entry),
+	   compare_entries);
+
+  addr_width = 4 + (gdbarch_ptr_bit (get_current_arch ()) / 4);
+
+  ui_out_table_header (current_uiout, 10, ui_left, "provider", _("Provider"));
+  ui_out_table_header (current_uiout, 10, ui_left, "name", _("Name"));
+  ui_out_table_header (current_uiout, addr_width - 1, ui_left, "addr", _("Where"));
+  ui_out_table_header (current_uiout, addr_width - 1, ui_left, "semaphore",
+		       _("Semaphore"));
+  ui_out_table_header (current_uiout, 30, ui_left, "object", _("Object"));
+  ui_out_table_body (current_uiout);
+
+  for (i = 0; VEC_iterate (stap_entry, items, i, entry); ++i)
+    {
+      struct cleanup *inner;
+
+      inner = make_cleanup_ui_out_tuple_begin_end (current_uiout, "probe");
+
+      ui_out_field_string (current_uiout, "provider", entry->probe->provider);
+      ui_out_field_string (current_uiout, "name", entry->probe->name);
+      ui_out_field_core_addr (current_uiout, "addr", get_current_arch (),
+			      entry->probe->address);
+      if (entry->probe->sem_addr == 0)
+	ui_out_field_skip (current_uiout, "semaphore");
+      else
+      ui_out_field_core_addr (current_uiout, "semaphore", get_current_arch (),
+			      entry->probe->sem_addr);
+      ui_out_field_string (current_uiout, "object", entry->objfile->name);
+      ui_out_text (current_uiout, "\n");
+
+      do_cleanups (inner);
+    }
+
+  any_found = !VEC_empty (stap_entry, items);
+  do_cleanups (cleanup);
+
+  if (!any_found)
+    ui_out_message (current_uiout, 0, _("No probes matched.\n"));
+}
+
+
+
+/* See definition in stap-probe.h.  */
+
+VEC (stap_probe_p) *
+find_probes_in_objfile (struct objfile *objfile,
+			const char *provider,
+			const char *name)
+{
+  struct stap_probe *probes;
+  int i, num_probes;
+  VEC (stap_probe_p) *result = NULL;
+
+  if (! objfile->sf || ! objfile->sf->sym_probe_fns)
+    return NULL;
+
+  probes = objfile->sf->sym_probe_fns->sym_get_probes (objfile, &num_probes);
+  for (i = 0; i < num_probes; ++i)
+    {
+      if (strcmp (probes[i].provider, provider) != 0)
+	continue;
+
+      if (strcmp (probes[i].name, name) != 0)
+	continue;
+
+      VEC_safe_push (stap_probe_p, result, &probes[i]);
+    }
+
+  return result;
+}
+
+/* See definition in stap-probe.h.  */
+
+struct symtabs_and_lines
+parse_stap_probe (char **argptr, struct linespec_result *canonical)
+{
+  char *arg_start, *arg_end, *arg;
+  char *objfile_name = NULL, *provider = NULL, *name, *p;
+  struct cleanup *cleanup;
+  struct symtabs_and_lines result;
+  struct objfile *objfile;
+  struct program_space *pspace;
+
+  result.sals = NULL;
+  result.nelts = 0;
+
+  arg_start = *argptr;
+
+  /* The caller ensured that this starts with `-p' or `-probe'.  */
+  gdb_assert (arg_start
+	      && ((strncmp (arg_start, "-pstap", 6) == 0
+		   && isspace (arg_start[6]))
+		  || (strncmp (arg_start, "-probe-stap", 11) == 0
+		      && isspace (arg_start[11]))));
+
+  if (strncmp (arg_start, "-pstap", 6) == 0)
+    arg_end = arg_start + 6;
+  else
+    arg_end = arg_start + 11;
+  arg_end = skip_spaces (arg_end);
+
+  if (!*arg_end)
+    error (_("argument to `%s' missing"),
+	   (strncmp (arg_start, "-probe-stap", 11) == 0
+	    ? "-probe-stap" : "-pstap"));
+
+  arg = arg_end;
+  arg_end = skip_to_space (arg_end);
+
+  /* We make a copy here so we can write over parts with impunity.  */
+  arg = savestring (arg, arg_end - arg);
+  cleanup = make_cleanup (xfree, arg);
+
+  /* Extract each word from the argument, separated by ":"s.  */
+  p = strchr (arg, ':');
+  if (p == NULL)
+    {
+      /* This is `-p name'.  */
+      name = arg;
+    }
+  else
+    {
+      char *hold = p + 1;
+
+      *p = '\0';
+      p = strchr (hold, ':');
+      if (p == NULL)
+	{
+	  /* This is `-p provider:name'.  */
+	  provider = arg;
+	  name = hold;
+	}
+      else
+	{
+	  /* This is `-p objfile:provider:name'.  */
+	  *p = '\0';
+	  objfile_name = arg;
+	  provider = hold;
+	  name = p + 1;
+	}
+    }
+
+  if (*name == '\0')
+    error (_("no probe name specified"));
+  if (provider && *provider == '\0')
+    error (_("invalid provider name"));
+  if (objfile_name && *objfile_name == '\0')
+    error (_("invalid objfile name"));
+
+  ALL_PSPACES (pspace)
+    ALL_PSPACE_OBJFILES (pspace, objfile)
+      {
+	struct stap_probe *probes;
+	int i, num_probes;
+
+	if (!objfile->sf || !objfile->sf->sym_probe_fns)
+	  continue;
+
+	if (objfile_name
+	    && FILENAME_CMP (objfile->name, objfile_name) != 0
+	    && FILENAME_CMP (lbasename (objfile->name), objfile_name) != 0)
+	  continue;
+
+	probes = objfile->sf->sym_probe_fns->sym_get_probes (objfile,
+							     &num_probes);
+	for (i = 0; i < num_probes; ++i)
+	  {
+	    struct symtab_and_line *sal;
+
+	    if (provider && strcmp (probes[i].provider, provider) != 0)
+	      continue;
+
+	    if (strcmp (probes[i].name, name) != 0)
+	      continue;
+
+	    ++result.nelts;
+	    result.sals = xrealloc (result.sals,
+				    result.nelts
+				    * sizeof (struct symtab_and_line));
+	    sal = &result.sals[result.nelts - 1];
+
+	    init_sal (sal);
+
+	    sal->pc = probes[i].address;
+	    sal->explicit_pc = 1;
+	    sal->section = find_pc_overlay (sal->pc);
+	    sal->pspace = pspace;
+	    sal->semaphore = probes[i].sem_addr;
+	  }
+      }
+
+  if (result.nelts == 0)
+    {
+      throw_error (NOT_FOUND_ERROR,
+		   _("No probe matching objfile=`%s', provider=`%s', name=`%s'"),
+		   objfile_name ? objfile_name : _("<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 stap-probe.h.  */
+
+struct stap_probe *
+find_probe_by_pc (CORE_ADDR pc, struct objfile **objfile_out)
+{
+  struct objfile *objfile;
+
+  ALL_OBJFILES (objfile)
+  {
+    struct stap_probe *probes;
+    int i, num_probes;
+    stap_entry entry;
+
+    if (!objfile->sf || !objfile->sf->sym_probe_fns)
+      continue;
+
+    /* If this proves too inefficient, we can replace with a hash.  */
+    probes = objfile->sf->sym_probe_fns->sym_get_probes (objfile, &num_probes);
+    for (i = 0; i < num_probes; ++i)
+      {
+	if (probes[i].address == pc)
+	  {
+	    *objfile_out = objfile;
+	    return &probes[i];
+	  }
+      }
+  }
+
+  return NULL;
+}
+
+/* This is called to compute the value of one of the $_probe_arg*
+   convenience variables.  */
+
+static struct value *
+compute_probe_arg (struct gdbarch *arch, struct internalvar *ivar,
+		   void *data)
+{
+  struct frame_info *frame = get_selected_frame (_("No frame selected"));
+  CORE_ADDR pc = get_frame_pc (frame);
+  int sel = (int) (uintptr_t) data;
+  struct objfile *objfile;
+  struct stap_probe *pc_probe;
+  int n_probes;
+
+  /* SEL==-1 means "_probe_argc".  */
+  gdb_assert (sel >= -1 && sel <= STAP_MAX_ARGS);
+
+  pc_probe = find_probe_by_pc (pc, &objfile);
+  if (pc_probe == NULL)
+    error (_("No SystemTap probe at PC %s"), core_addr_to_string (pc));
+
+  n_probes
+    = objfile->sf->sym_probe_fns->sym_get_probe_argument_count (objfile,
+								pc_probe);
+  if (sel == -1)
+    return value_from_longest (builtin_type (arch)->builtin_int, n_probes);
+
+  if (sel >= n_probes)
+    error (_("Invalid probe argument %d -- probe has %d arguments available"),
+	   sel, n_probes);
+
+  return objfile->sf->sym_probe_fns->sym_evaluate_probe_argument (objfile,
+								  pc_probe,
+								  frame, sel);
+}
+
+/* This is called to compile one of the $_probe_arg* convenience
+   variables into an agent expression.  */
+
+static void
+compile_probe_arg (struct internalvar *ivar, struct agent_expr *expr,
+		   struct axs_value *value, void *data)
+{
+  CORE_ADDR pc = expr->scope;
+  int sel = (int) (uintptr_t) data;
+  struct objfile *objfile;
+  struct stap_probe *pc_probe;
+  int n_probes;
+
+  /* SEL == -1 means "_probe_argc".  */
+  gdb_assert (sel >= -1 && sel <= STAP_MAX_ARGS);
+
+  pc_probe = find_probe_by_pc (pc, &objfile);
+  if (pc_probe == NULL)
+    error (_("No SystemTap probe at PC %s"), core_addr_to_string (pc));
+
+  n_probes
+    = objfile->sf->sym_probe_fns->sym_get_probe_argument_count (objfile,
+								pc_probe);
+  if (sel == -1)
+    {
+      value->kind = axs_rvalue;
+      value->type = builtin_type (expr->gdbarch)->builtin_int;
+      ax_const_l (expr, n_probes);
+      return;
+    }
+
+  gdb_assert (sel >= 0);
+  if (sel >= n_probes)
+    error (_("Invalid probe argument %d -- probe has %d arguments available"),
+	   sel, n_probes);
+
+  objfile->sf->sym_probe_fns->sym_compile_to_ax (objfile, pc_probe,
+						 expr, value, sel);
+}
+
+
+
+/* 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."));
+}
+
+/* See the comments in stap-probe.h.  */
+
+void
+stap_semaphore_up (CORE_ADDR sem, struct gdbarch *gdbarch)
+{
+  stap_modify_semaphore (sem, 1, gdbarch);
+}
+
+/* See the comments in stap-probe.h.  */
+
+void
+stap_semaphore_down (CORE_ADDR sem, struct gdbarch *gdbarch)
+{
+  stap_modify_semaphore (sem, 0, gdbarch);
+}
+
+/* Implementation of `$_probe_arg*' set of variables.  */
+
+static const struct internalvar_funcs probe_funcs =
+{
+  compute_probe_arg,
+  compile_probe_arg,
+  NULL
+};
+
+void _initialize_stap_probe (void);
+
+void
+_initialize_stap_probe (void)
+{
+  add_setshow_zinteger_cmd ("stap-expression", class_maintenance,
+			    &stap_expression_debug,
+			    _("Set SystemTap expression debugging."),
+			    _("Show SystemTap expression debugging."),
+			    _("When non-zero, the internal representation "
+			      "of SystemTap expressions will be printed."),
+			    NULL,
+			    show_stapexpressiondebug,
+			    &setdebuglist, &showdebuglist);
+
+  create_internalvar_type_lazy ("_probe_argc", &probe_funcs,
+				(void *) (uintptr_t) -1);
+  create_internalvar_type_lazy ("_probe_arg0", &probe_funcs,
+				(void *) (uintptr_t) 0);
+  create_internalvar_type_lazy ("_probe_arg1", &probe_funcs,
+				(void *) (uintptr_t) 1);
+  create_internalvar_type_lazy ("_probe_arg2", &probe_funcs,
+				(void *) (uintptr_t) 2);
+  create_internalvar_type_lazy ("_probe_arg3", &probe_funcs,
+				(void *) (uintptr_t) 3);
+  create_internalvar_type_lazy ("_probe_arg4", &probe_funcs,
+				(void *) (uintptr_t) 4);
+  create_internalvar_type_lazy ("_probe_arg5", &probe_funcs,
+				(void *) (uintptr_t) 5);
+  create_internalvar_type_lazy ("_probe_arg6", &probe_funcs,
+				(void *) (uintptr_t) 6);
+  create_internalvar_type_lazy ("_probe_arg7", &probe_funcs,
+				(void *) (uintptr_t) 7);
+  create_internalvar_type_lazy ("_probe_arg8", &probe_funcs,
+				(void *) (uintptr_t) 8);
+  create_internalvar_type_lazy ("_probe_arg9", &probe_funcs,
+				(void *) (uintptr_t) 9);
+  create_internalvar_type_lazy ("_probe_arg10", &probe_funcs,
+				(void *) (uintptr_t) 10);
+  create_internalvar_type_lazy ("_probe_arg11", &probe_funcs,
+				(void *) (uintptr_t) 11);
+}
diff --git a/gdb/stap-probe.h b/gdb/stap-probe.h
new file mode 100644
index 0000000..1cad8dc
--- /dev/null
+++ b/gdb/stap-probe.h
@@ -0,0 +1,161 @@
+/* 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 "vec.h"
+
+struct stap_args_info;
+struct axs_value;
+struct linespec_result;
+
+/* Main structure which holds information about a SystemTap probe.  */
+
+struct stap_probe
+{
+  /* The provider of this probe.  */
+  const char *provider;
+
+  /* The name of the probe.  */
+  const char *name;
+
+  /* The address where the probe is inserted.  */
+  CORE_ADDR address;
+
+  /* The address of the probe's semaphore, or 0 if this probe does not
+     have an associated semaphore.  */
+  CORE_ADDR sem_addr;
+
+  /* Probe's arguments.  Users should generally not examine this, but
+     should instead extract information about the arguments using the
+     methods provided in sym_probe_fns.  */
+  const char *args;
+
+  /* Probe's arguments after parsing.  This is an opaque structure that
+     will hold information about the arguments pointed by ARGS.  */
+  struct stap_args_info *parsed_args;
+
+  /* gdbarch structure associated with this probe.  */
+  struct gdbarch *gdbarch;
+};
+
+/* Structure which holds information about the parsing process of one probe's
+   argument.  */
+
+struct stap_parse_info
+{
+  /* The probe's argument in a string format.  */
+  const char *arg;
+
+  /* A pointer to the full chain of arguments.  This is useful for printing
+     error messages.  The parser functions should not modify this argument
+     directly; instead, they should use the ARG pointer above.  */
+  const char *saved_arg;
+
+  /* The expected argument type (bitness), as defined in the probe's
+     argument.  For instance, if the argument begins with `-8@', it means
+     the bitness is 64-bit signed.  In this case, ARG_TYPE would represent
+     the type `int64_t'.  */
+  struct type *arg_type;
+
+  /* A pointer to the current gdbarch.  */
+  struct gdbarch *gdbarch;
+
+  /* Greater than zero if we are inside a parenthesized expression.  Useful
+     for knowing when to skip spaces or not.  */
+  int inside_paren_p;
+};
+
+typedef struct stap_probe *stap_probe_p;
+DEF_VEC_P (stap_probe_p);
+
+/* A helper for linespec that decodes a stap probe specification.  It
+   returns a symtabs_and_lines object and updates *ARGPTR or throws an
+   error.  */
+
+extern struct symtabs_and_lines parse_stap_probe (char **argptr,
+						  struct linespec_result *canon);
+
+/* Search OBJFILE for a probe with the given PROVIDER and NAME.
+   Return a VEC of all probes that were found.  If no matching probe
+   is found, return NULL.  The caller must free the VEC.  */
+
+extern VEC (stap_probe_p) *find_probes_in_objfile (struct objfile *objfile,
+						   const char *provider,
+						   const char *name);
+
+/* Given a PC, find an associated SystemTap probe.  If a probe is
+   found, set *OBJFILE_OUT to the probe's objfile, and return the
+   probe.  If no probe is found, return NULL.  */
+
+extern struct stap_probe *find_probe_by_pc (CORE_ADDR pc,
+					    struct objfile **objfile_out);
+
+/* Given PROBE, returns the number of arguments present in that probe's
+   argument string.  */
+
+extern int stap_get_probe_argument_count (struct stap_probe *probe);
+
+/* Given PARSED_ARGS, frees the space allocated to hold information about
+   the probe's parsed arguments.  */
+
+extern void stap_free_parsed_args (struct stap_args_info *parsed_args);
+
+/* Evaluate the probe's argument N, returning a value corresponding
+   to it.  */
+
+extern struct value *stap_evaluate_probe_argument (struct objfile *objfile,
+						   struct stap_probe *probe,
+						   struct frame_info *frame,
+						   int n);
+
+/* Compile the probe's argument N to agent expression.  */
+
+extern void stap_compile_to_ax (struct objfile *objfile,
+				struct stap_probe *probe,
+				struct agent_expr *expr,
+				struct axs_value *value,
+				int n);
+
+/* A convenience function that finds a probe at the PC in FRAME and
+   evaluates argument N.  If there is no probe at that location, or if
+   the probe does not have enough arguments, this returns NULL.  */
+
+extern struct value *stap_safe_evaluate_at_pc (struct frame_info *frame,
+					       int n);
+
+/* Implementation of the `info probes stap' command.  */
+
+extern void info_probes_stap_command (char *arg, int from_tty);
+
+/* 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.  */
+
+extern void stap_semaphore_up (CORE_ADDR sem, struct gdbarch *gdbarch);
+
+/* Clear a SystemTap semaphore.  SEM is the semaphore's address.  */
+
+extern void stap_semaphore_down (CORE_ADDR sem, struct gdbarch *gdbarch);
+
+#endif /* !defined (STAP_PROBE_H) */
diff --git a/gdb/symfile.h b/gdb/symfile.h
index 7024ace..8406105 100644
--- a/gdb/symfile.h
+++ b/gdb/symfile.h
@@ -29,6 +29,11 @@ struct objfile;
 struct obj_section;
 struct obstack;
 struct block;
+struct stap_probe;
+struct value;
+struct frame_info;
+struct agent_expr;
+struct axs_value;
 
 /* Comparison function for symbol look ups.  */
 
@@ -297,6 +302,55 @@ struct quick_symbol_functions
 				int need_fullname);
 };
 
+/* Structure of functions used for SystemTap probe support.  If one of
+   these functions is provided, all must be.  */
+
+struct sym_probe_fns
+{
+  /* If non-NULL, return an array of SystemTap probe objects.  The
+     number of objects is returned in *NUM_PROBES.
+
+     The returned value does not have to be freed and it has lifetime of the
+     OBJFILE.  */
+  struct stap_probe *(*sym_get_probes) (struct objfile *,
+					int *num_probes);
+
+  /* Return the number of arguments available to PROBE.  PROBE will
+     have come from a call to this objfile's sym_get_probes method.
+     If you provide an implementation of sym_get_probes, you must
+     implement this method as well.  */
+  int (*sym_get_probe_argument_count) (struct objfile *objfile,
+				       struct stap_probe *probe);
+
+  /* Evaluate the Nth argument available to PROBE.  PROBE will have
+     come from a call to this objfile's sym_get_probes method.  N will
+     be between 0 and the number of arguments available to this probe.
+     FRAME is the frame in which the evaluation is done; the frame's
+     PC will match the address of the probe.  If you provide an
+     implementation of sym_get_probes, you must implement this method
+     as well.  */
+  struct value *(*sym_evaluate_probe_argument) (struct objfile *objfile,
+						struct stap_probe *probe,
+						struct frame_info *frame,
+						int n);
+
+  /* Compile the Nth probe argument to an agent expression.  PROBE
+     will have come from a call to this objfile's sym_get_probes
+     method.  N will be between 0 and the number of arguments
+     available to this probe.  EXPR and VALUE are the agent expression
+     that is being updated.  */
+  void (*sym_compile_to_ax) (struct objfile *objfile,
+			     struct stap_probe *probe,
+			     struct agent_expr *expr,
+			     struct axs_value *value,
+			     int n);
+
+  /* Relocate the probe section of OBJFILE.  */
+  void (*sym_relocate_probe) (struct objfile *objfile,
+			      struct section_offsets *new_offsets,
+			      struct section_offsets *delta);
+};
+
 /* Structure to keep track of symbol reading functions for various
    object file types.  */
 
@@ -367,6 +421,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..8dae087 100644
--- a/gdb/symtab.c
+++ b/gdb/symtab.c
@@ -877,6 +877,7 @@ init_sal (struct symtab_and_line *sal)
   sal->end = 0;
   sal->explicit_pc = 0;
   sal->explicit_line = 0;
+  sal->semaphore = 0;
 }
 
 
diff --git a/gdb/symtab.h b/gdb/symtab.h
index d9e5f4a..cb6206d 100644
--- a/gdb/symtab.h
+++ b/gdb/symtab.h
@@ -1045,6 +1045,10 @@ struct symtab_and_line
   CORE_ADDR end;
   int explicit_pc;
   int explicit_line;
+
+  /* If non-zero, the semaphore location associated with a SystemTap
+     probe.  */
+  CORE_ADDR semaphore;
 };
 
 extern void init_sal (struct symtab_and_line *sal);
diff --git a/gdb/tracepoint.c b/gdb/tracepoint.c
index fac6b44..306c443 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 "stap-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)
+	stap_semaphore_up (loc->semaphore, 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.  */
+	  stap_semaphore_down (loc->semaphore, 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 86ae8fb..288709e 100644
--- a/gdb/xcoffread.c
+++ b/gdb/xcoffread.c
@@ -3112,6 +3112,7 @@ static const struct sym_fns xcoff_sym_fns =
   default_symfile_segments,	/* Get segment information from a file.  */
   aix_process_linenos,
   default_symfile_relocate,	/* Relocate a debug section.  */
+  NULL,				/* sym_probe_fns */
   &psym_functions
 };
 
-- 
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]