2011-11-14 Stan Shebs <stan@codesourcery.com>
authorStan Shebs <shebs@codesourcery.com>
Mon, 14 Nov 2011 20:07:25 +0000 (20:07 +0000)
committerStan Shebs <shebs@codesourcery.com>
Mon, 14 Nov 2011 20:07:25 +0000 (20:07 +0000)
    Kwok Cheung Yeung  <kcy@codesourcery.com>

* NEWS: Document shorter fast tracepoints and qTMinFTPILen packet.
* i386-tdep.c (i386_fast_tracepoint_valid_at): Query target for
the minimum instruction size for fast tracepoints.
* target.h (struct target_ops): Add new method
to_get_min_fast_tracepoint_insn_len.
(target_get_min_fast_tracepoint_insn_len): New.
* target.c (update_current_target): Set up new target operation.
* remote.c (remote_write_bytes_aux): Fix typo.
(remote_get_min_fast_tracepoint_insn_len): New.
(init_remote_ops): Initialize new field.

* gdb.texinfo (Create and Delete Tracepoints): Describe what is
needed to get shorter fast tracepoints.
(Tracepoint Packets): Document new qTMinFTPILen packet.

* linux-x86-low.c (small_jump_insn): New.
(i386_install_fast_tracepoint_jump_pad): Add arguments for
trampoline and error message, build a trampoline and issue a small
jump instruction to it.
(x86_install_fast_tracepoint_jump_pad): Add arguments for
trampoline and error message.
(x86_get_min_fast_tracepoint_insn_len): New.
(the_low_target): Add call to x86_get_min_fast_tracepoint_insn_len.
* linux-low.h (struct linux_target_ops): Add arguments to
install_fast_tracepoint_jump_pad operation, add new operation.
* linux-low.c (linux_install_fast_tracepoint_jump_pad): Add
arguments.
(linux_get_min_fast_tracepoint_insn_len): New function.
(linux_target_op): Add new operation.
* tracepoint.c (gdb_trampoline_buffer): New IPA variable.
(gdb_trampoline_buffer_end): Ditto.
(gdb_trampoline_buffer_error): Ditto.
(struct ipa_sym_addresses): Add fields for new IPA variables.
(symbol_list): Add entries for new IPA variables.
(struct tracepoint): Add fields to hold the address range of the
trampoline used by the tracepoint.
(trampoline_buffer_head): New static variable.
(trampoline_buffer_tail): Ditto.
(claim_trampoline_space): New function.
(have_fast_tracepoint_trampoline_buffer): New function.
(clone_fast_tracepoint): Fill in trampoline fields of tracepoint
structure.
(install_fast_tracepoint): Ditto, also add error buffer argument.
(cmd_qtminftpilen): New function.
(handle_tracepoint_query): Add response to qTMinFTPILen packet.
(fast_tracepoint_from_trampoline_address): New function.
(fast_tracepoint_collecting): Handle trampoline as part of jump
pad space.
(set_trampoline_buffer_space): New function.
(initialize_tracepoint): Initialize new IPA variables.
* target.h (struct target_ops): Add arguments to
install_fast_tracepoint_jump_pad operation, add new
get_min_fast_tracepoint_insn_len operation.
(target_get_min_fast_tracepoint_insn_len): New.
(install_fast_tracepoint_jump_pad): Add arguments.
* server.h (IPA_BUFSIZ): Define.
* linux-i386-ipa.c: Include extra header files.
(initialize_fast_tracepoint_trampoline_buffer): New function.
(initialize_low_tracepoint): Call it.
* server.h (set_trampoline_buffer_space): Declare.
(claim_trampoline_space): Ditto.
(have_fast_tracepoint_trampoline_buffer): Ditto.

* gdb.trace/ftrace.c: New.
* gdb.trace/ftrace.exp: New.

19 files changed:
gdb/ChangeLog
gdb/NEWS
gdb/doc/ChangeLog
gdb/doc/gdb.texinfo
gdb/gdbserver/ChangeLog
gdb/gdbserver/linux-i386-ipa.c
gdb/gdbserver/linux-low.c
gdb/gdbserver/linux-low.h
gdb/gdbserver/linux-x86-low.c
gdb/gdbserver/server.h
gdb/gdbserver/target.h
gdb/gdbserver/tracepoint.c
gdb/i386-tdep.c
gdb/remote.c
gdb/target.c
gdb/target.h
gdb/testsuite/ChangeLog
gdb/testsuite/gdb.trace/ftrace.c [new file with mode: 0644]
gdb/testsuite/gdb.trace/ftrace.exp [new file with mode: 0644]

index bfeecc7faeacfbd7a9736cdd654cde3d7c89c8d5..72f5a62ba48f253477cb7ff22f4abacdfaf283da 100644 (file)
@@ -1,3 +1,17 @@
+2011-11-14  Stan Shebs  <stan@codesourcery.com>
+           Kwok Cheung Yeung  <kcy@codesourcery.com>
+
+       * NEWS: Document shorter fast tracepoints and qTMinFTPILen packet.
+       * i386-tdep.c (i386_fast_tracepoint_valid_at): Query target for
+       the minimum instruction size for fast tracepoints.
+       * target.h (struct target_ops): Add new method
+       to_get_min_fast_tracepoint_insn_len.
+       (target_get_min_fast_tracepoint_insn_len): New.
+       * target.c (update_current_target): Set up new target operation.
+       * remote.c (remote_write_bytes_aux): Fix typo.
+       (remote_get_min_fast_tracepoint_insn_len): New.
+       (init_remote_ops): Initialize new field.
+
 2011-11-14  Tom Tromey  <tromey@redhat.com>
 
        * tracepoint.c (encode_actions_1): Use the location's gdbarch.
index 52366f86fd60a0eac431d251111fc335778bb5e1..dc16a4bc07ad6ba676fed226b15daae27e914f41 100644 (file)
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -134,6 +134,10 @@ collect[/s] EXPRESSIONS
   begin, assuming that tracepoints will be enabled as needed while the trace
   is running.
 
+* Fast tracepoints on 32-bit x86-architectures can now be placed at
+  locations with 4-byte instructions, when they were previously
+  limited to locations with instructions of 5 bytes or longer.
+
 * New options
 
 set extended-prompt
@@ -165,6 +169,11 @@ QTDisable
 
   Dynamically disable a tracepoint in a started trace experiment.
 
+qTMinFTPILen
+
+  Query the minimum length of instruction at which a fast tracepoint may
+  be placed.
+
 * Dcache size (number of lines) and line-size are now runtime-configurable
   via "set dcache line" and "set dcache line-size" commands.
 
index ac38897619bebdb96d92e5fa47f91372dae16ca1..02ef07b13ddec47f20426f505a070eaaa55581ee 100644 (file)
@@ -1,3 +1,10 @@
+2011-11-14  Stan Shebs  <stan@codesourcery.com>
+           Kwok Cheung Yeung  <kcy@codesourcery.com>
+
+       * gdb.texinfo (Create and Delete Tracepoints): Describe what is
+       needed to get shorter fast tracepoints.
+       (Tracepoint Packets): Document new qTMinFTPILen packet.
+
 2011-11-14  Yao Qi  <yao@codesourcery.com>
 
        * gdb.texinfo (Create and Delete Tracepoints): Describe changed
index f17c80061d51e2f9cbd1484a8efa8c074bba2da7..0cadc9681d39a4d15e2c0b83321b3be0595c29d9 100644 (file)
@@ -10378,6 +10378,23 @@ message.
 @value{GDBN} handles arguments to @code{ftrace} exactly as for
 @code{trace}.
 
+On 32-bit x86-architecture systems, fast tracepoints normally need to
+be placed at an instruction that is 5 bytes or longer, but can be
+placed at 4-byte instructions if the low 64K of memory of the target
+program is available to install trampolines.  Some Unix-type systems,
+such as @sc{gnu}/Linux, exclude low addresses from the program's
+address space; but for instance with the Linux kernel it is possible
+to let @value{GDBN} use this area by doing a @command{sysctl} command
+to set the @code{mmap_min_addr} kernel parameter, as in
+
+@example
+sudo sysctl -w vm.mmap_min_addr=32768
+@end example
+
+@noindent
+which sets the low address to 32K, which leaves plenty of room for
+trampolines.  The minimum address should be set to a page boundary.
+
 @item strace @var{location} [ if @var{cond} ]
 @cindex set static tracepoint
 @cindex static tracepoints, setting
@@ -34915,6 +34932,8 @@ encoded).  @value{GDBN} will continue to supply the values of symbols
 @itemx qTfP
 @itemx qTfV
 @itemx QTFrame
+@itemx qTMinFTPILen
+
 @xref{Tracepoint Packets}.
 
 @item qThreadExtraInfo,@var{thread-id}
@@ -35438,6 +35457,30 @@ numbers.
 Like @samp{QTFrame:range:@var{start}:@var{end}}, but select the first
 frame @emph{outside} the given range of addresses (exclusive).
 
+@item qTMinFTPILen
+This packet requests the minimum length of instruction at which a fast
+tracepoint (@pxref{Set Tracepoints}) may be placed.  For instance, on
+the 32-bit x86 architecture, it is possible to use a 4-byte jump, but
+it depends on the target system being able to create trampolines in
+the first 64K of memory, which might or might not be possible for that
+system.  So the reply to this packet will be 4 if it is able to
+arrange for that.
+
+Replies:
+
+@table @samp
+@item 0
+The minimum instruction length is currently unknown.
+@item @var{length}
+The minimum instruction length is @var{length}, where @var{length} is greater
+or equal to 1.  @var{length} is a hexadecimal number.  A reply of 1 means
+that a fast tracepoint may be placed on any instruction regardless of size.
+@item E
+An error has occurred.
+@item
+An empty reply indicates that the request is not supported by the stub.
+@end table
+
 @item QTStart
 Begin the tracepoint experiment.  Begin collecting data from
 tracepoint hits in the trace frame buffer.  This packet supports the
index 2a5aae92c59e09231ba9c8562764d535a06c1579..844fb1836dafa610127a810f377ded31122846c5 100644 (file)
@@ -1,3 +1,54 @@
+2011-11-14  Stan Shebs  <stan@codesourcery.com>
+           Kwok Cheung Yeung  <kcy@codesourcery.com>
+
+       * linux-x86-low.c (small_jump_insn): New.
+       (i386_install_fast_tracepoint_jump_pad): Add arguments for
+       trampoline and error message, build a trampoline and issue a small
+       jump instruction to it.
+       (x86_install_fast_tracepoint_jump_pad): Add arguments for
+       trampoline and error message.
+       (x86_get_min_fast_tracepoint_insn_len): New.
+       (the_low_target): Add call to x86_get_min_fast_tracepoint_insn_len.
+       * linux-low.h (struct linux_target_ops): Add arguments to
+       install_fast_tracepoint_jump_pad operation, add new operation.
+       * linux-low.c (linux_install_fast_tracepoint_jump_pad): Add
+       arguments.
+       (linux_get_min_fast_tracepoint_insn_len): New function.
+       (linux_target_op): Add new operation.
+       * tracepoint.c (gdb_trampoline_buffer): New IPA variable.
+       (gdb_trampoline_buffer_end): Ditto.
+       (gdb_trampoline_buffer_error): Ditto.
+       (struct ipa_sym_addresses): Add fields for new IPA variables.
+       (symbol_list): Add entries for new IPA variables.
+       (struct tracepoint): Add fields to hold the address range of the
+       trampoline used by the tracepoint.
+       (trampoline_buffer_head): New static variable.
+       (trampoline_buffer_tail): Ditto.
+       (claim_trampoline_space): New function.
+       (have_fast_tracepoint_trampoline_buffer): New function.
+       (clone_fast_tracepoint): Fill in trampoline fields of tracepoint
+       structure.
+       (install_fast_tracepoint): Ditto, also add error buffer argument.
+       (cmd_qtminftpilen): New function.
+       (handle_tracepoint_query): Add response to qTMinFTPILen packet.
+       (fast_tracepoint_from_trampoline_address): New function.
+       (fast_tracepoint_collecting): Handle trampoline as part of jump
+       pad space.
+       (set_trampoline_buffer_space): New function.
+       (initialize_tracepoint): Initialize new IPA variables.
+       * target.h (struct target_ops): Add arguments to
+       install_fast_tracepoint_jump_pad operation, add new
+       get_min_fast_tracepoint_insn_len operation.
+       (target_get_min_fast_tracepoint_insn_len): New.
+       (install_fast_tracepoint_jump_pad): Add arguments.
+       * server.h (IPA_BUFSIZ): Define.
+       * linux-i386-ipa.c: Include extra header files.
+       (initialize_fast_tracepoint_trampoline_buffer): New function.
+       (initialize_low_tracepoint): Call it.
+       * server.h (set_trampoline_buffer_space): Declare.
+       (claim_trampoline_space): Ditto.
+       (have_fast_tracepoint_trampoline_buffer): Ditto.
+
 2011-11-14  Yao Qi  <yao@codesourcery.com>
 
        * server.c (handle_query): Handle InstallInTrace for qSupported.
index f0eeb4af54df00043d2968b4572984f9a2dbbb8a..59064640494d918bc47442f9bf2726513f4ee165 100644 (file)
@@ -19,6 +19,8 @@
    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 #include "server.h"
+#include <stdint.h>
+#include <sys/mman.h>
 
 /* GDB register numbers.  */
 
@@ -191,8 +193,62 @@ supply_static_tracepoint_registers (struct regcache *regcache,
    may use it proper at some point.  */
 const char *gdbserver_xmltarget;
 
+/* Attempt to allocate memory for trampolines in the first 64 KiB of
+   memory to enable smaller jump patches.  */
+
+static void
+initialize_fast_tracepoint_trampoline_buffer (void)
+{
+  const CORE_ADDR buffer_end = 64 * 1024;
+  /* Ensure that the buffer will be at least 1 KiB in size, which is
+     enough space for over 200 fast tracepoints.  */
+  const int min_buffer_size = 1024;
+  char buf[IPA_BUFSIZ];
+  CORE_ADDR mmap_min_addr = buffer_end + 1;
+  ULONGEST buffer_size;
+  FILE *f = fopen ("/proc/sys/vm/mmap_min_addr", "r");
+
+  if (!f)
+    {    
+      snprintf (buf, sizeof (buf), "mmap_min_addr open failed: %s",
+               strerror (errno));
+      set_trampoline_buffer_space (0, 0, buf);
+      return;
+    }
+
+  if (fgets (buf, IPA_BUFSIZ, f))
+    sscanf (buf, "%llu", &mmap_min_addr);
+      
+  fclose (f);
+      
+  buffer_size = buffer_end - mmap_min_addr;
+
+  if (buffer_size >= min_buffer_size)
+    {
+      if (mmap ((void *) (uintptr_t) mmap_min_addr, buffer_size,
+               PROT_READ | PROT_EXEC | PROT_WRITE,
+               MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS,
+               -1, 0)
+         != MAP_FAILED)
+       set_trampoline_buffer_space (mmap_min_addr, buffer_end, NULL);
+      else
+       {
+         snprintf (buf, IPA_BUFSIZ, "low-64K-buffer mmap() failed: %s",
+                   strerror (errno));
+         set_trampoline_buffer_space (0, 0, buf);
+       }
+    }
+  else
+    {
+      snprintf (buf, IPA_BUFSIZ, "mmap_min_addr is %d, must be %d or less",
+               (int) mmap_min_addr, (int) buffer_end - min_buffer_size);
+      set_trampoline_buffer_space (0, 0, buf);
+    }
+}
+
 void
 initialize_low_tracepoint (void)
 {
   init_registers_i386_linux ();
+  initialize_fast_tracepoint_trampoline_buffer ();
 }
index e8cf3746352eba0d03d966397b4d8f3fc14f563d..2b973c69c43d060501515439a66ad2a53fc1abfa 100644 (file)
@@ -4933,15 +4933,20 @@ linux_install_fast_tracepoint_jump_pad (CORE_ADDR tpoint, CORE_ADDR tpaddr,
                                        CORE_ADDR lockaddr,
                                        ULONGEST orig_size,
                                        CORE_ADDR *jump_entry,
+                                       CORE_ADDR *trampoline,
+                                       ULONGEST *trampoline_size,
                                        unsigned char *jjump_pad_insn,
                                        ULONGEST *jjump_pad_insn_size,
                                        CORE_ADDR *adjusted_insn_addr,
-                                       CORE_ADDR *adjusted_insn_addr_end)
+                                       CORE_ADDR *adjusted_insn_addr_end,
+                                       char *err)
 {
   return (*the_low_target.install_fast_tracepoint_jump_pad)
     (tpoint, tpaddr, collector, lockaddr, orig_size,
-     jump_entry, jjump_pad_insn, jjump_pad_insn_size,
-     adjusted_insn_addr, adjusted_insn_addr_end);
+     jump_entry, trampoline, trampoline_size,
+     jjump_pad_insn, jjump_pad_insn_size,
+     adjusted_insn_addr, adjusted_insn_addr_end,
+     err);
 }
 
 static struct emit_ops *
@@ -4953,6 +4958,12 @@ linux_emit_ops (void)
     return NULL;
 }
 
+static int
+linux_get_min_fast_tracepoint_insn_len (void)
+{
+  return (*the_low_target.get_min_fast_tracepoint_insn_len) ();
+}
+
 static struct target_ops linux_target_ops = {
   linux_create_inferior,
   linux_attach,
@@ -5014,6 +5025,7 @@ static struct target_ops linux_target_ops = {
   linux_install_fast_tracepoint_jump_pad,
   linux_emit_ops,
   linux_supports_disable_randomization,
+  linux_get_min_fast_tracepoint_insn_len,
 };
 
 static void
index 6635bc656f96c055072638e06158896c672783d2..a4e3d5ac6bfc6a74425ad83cc7fae55fffd464ec 100644 (file)
@@ -132,14 +132,22 @@ struct linux_target_ops
                                           CORE_ADDR lockaddr,
                                           ULONGEST orig_size,
                                           CORE_ADDR *jump_entry,
+                                          CORE_ADDR *trampoline,
+                                          ULONGEST *trampoline_size,
                                           unsigned char *jjump_pad_insn,
                                           ULONGEST *jjump_pad_insn_size,
                                           CORE_ADDR *adjusted_insn_addr,
-                                          CORE_ADDR *adjusted_insn_addr_end);
+                                          CORE_ADDR *adjusted_insn_addr_end,
+                                          char *err);
 
   /* Return the bytecode operations vector for the current inferior.
      Returns NULL if bytecode compilation is not supported.  */
   struct emit_ops *(*emit_ops) (void);
+
+  /* Return the minimum length of an instruction that can be safely overwritten
+     for use as a fast tracepoint.  */
+  int (*get_min_fast_tracepoint_insn_len) (void);
+
 };
 
 extern struct linux_target_ops the_low_target;
index 4cc3feb956b8ec47e9d4bfcfb92c2bef124d8420..d1c760e9e6cef1a9e8f48088b7040eaa35bf9b1a 100644 (file)
@@ -42,6 +42,7 @@ void init_registers_amd64_avx_linux (void);
 void init_registers_i386_mmx_linux (void);
 
 static unsigned char jump_insn[] = { 0xe9, 0, 0, 0, 0 };
+static unsigned char small_jump_insn[] = { 0x66, 0xe9, 0, 0 };
 
 /* Backward compatibility for gdb without XML support.  */
 
@@ -1182,10 +1183,13 @@ amd64_install_fast_tracepoint_jump_pad (CORE_ADDR tpoint, CORE_ADDR tpaddr,
                                        CORE_ADDR lockaddr,
                                        ULONGEST orig_size,
                                        CORE_ADDR *jump_entry,
+                                       CORE_ADDR *trampoline,
+                                       ULONGEST *trampoline_size,
                                        unsigned char *jjump_pad_insn,
                                        ULONGEST *jjump_pad_insn_size,
                                        CORE_ADDR *adjusted_insn_addr,
-                                       CORE_ADDR *adjusted_insn_addr_end)
+                                       CORE_ADDR *adjusted_insn_addr_end,
+                                       char *err)
 {
   unsigned char buf[40];
   int i, offset;
@@ -1346,10 +1350,13 @@ i386_install_fast_tracepoint_jump_pad (CORE_ADDR tpoint, CORE_ADDR tpaddr,
                                       CORE_ADDR lockaddr,
                                       ULONGEST orig_size,
                                       CORE_ADDR *jump_entry,
+                                      CORE_ADDR *trampoline,
+                                      ULONGEST *trampoline_size,
                                       unsigned char *jjump_pad_insn,
                                       ULONGEST *jjump_pad_insn_size,
                                       CORE_ADDR *adjusted_insn_addr,
-                                      CORE_ADDR *adjusted_insn_addr_end)
+                                      CORE_ADDR *adjusted_insn_addr_end,
+                                      char *err)
 {
   unsigned char buf[0x100];
   int i, offset;
@@ -1455,7 +1462,7 @@ i386_install_fast_tracepoint_jump_pad (CORE_ADDR tpoint, CORE_ADDR tpaddr,
   buf[i++] = 0x0f; /* pop %fs */
   buf[i++] = 0xa1;
   buf[i++] = 0x07; /* pop %es */
-  buf[i++] = 0x1f; /* pop %de */
+  buf[i++] = 0x1f; /* pop %ds */
   buf[i++] = 0x9d; /* popf */
   buf[i++] = 0x83; /* add $0x4,%esp (pop of tpaddr aka $pc) */
   buf[i++] = 0xc4;
@@ -1479,11 +1486,40 @@ i386_install_fast_tracepoint_jump_pad (CORE_ADDR tpoint, CORE_ADDR tpaddr,
      is always done last (by our caller actually), so that we can
      install fast tracepoints with threads running.  This relies on
      the agent's atomic write support.  */
-  offset = *jump_entry - (tpaddr + sizeof (jump_insn));
-  memcpy (buf, jump_insn, sizeof (jump_insn));
-  memcpy (buf + 1, &offset, 4);
-  memcpy (jjump_pad_insn, buf, sizeof (jump_insn));
-  *jjump_pad_insn_size = sizeof (jump_insn);
+  if (orig_size == 4)
+    {
+      /* Create a trampoline.  */
+      *trampoline_size = sizeof (jump_insn);
+      if (!claim_trampoline_space (*trampoline_size, trampoline))
+       {
+         /* No trampoline space available.  */
+         strcpy (err,
+                 "E.Cannot allocate trampoline space needed for fast "
+                 "tracepoints on 4-byte instructions.");
+         return 1;
+       }
+
+      offset = *jump_entry - (*trampoline + sizeof (jump_insn));
+      memcpy (buf, jump_insn, sizeof (jump_insn));
+      memcpy (buf + 1, &offset, 4);
+      write_inferior_memory (*trampoline, buf, sizeof (jump_insn));
+
+      /* Use a 16-bit relative jump instruction to jump to the trampoline.  */
+      offset = (*trampoline - (tpaddr + sizeof (small_jump_insn))) & 0xffff;
+      memcpy (buf, small_jump_insn, sizeof (small_jump_insn));
+      memcpy (buf + 2, &offset, 2);
+      memcpy (jjump_pad_insn, buf, sizeof (small_jump_insn));
+      *jjump_pad_insn_size = sizeof (small_jump_insn);
+    }
+  else
+    {
+      /* Else use a 32-bit relative jump instruction.  */
+      offset = *jump_entry - (tpaddr + sizeof (jump_insn));
+      memcpy (buf, jump_insn, sizeof (jump_insn));
+      memcpy (buf + 1, &offset, 4);
+      memcpy (jjump_pad_insn, buf, sizeof (jump_insn));
+      *jjump_pad_insn_size = sizeof (jump_insn);
+    }
 
   /* Return the end address of our pad.  */
   *jump_entry = buildaddr;
@@ -1497,29 +1533,83 @@ x86_install_fast_tracepoint_jump_pad (CORE_ADDR tpoint, CORE_ADDR tpaddr,
                                      CORE_ADDR lockaddr,
                                      ULONGEST orig_size,
                                      CORE_ADDR *jump_entry,
+                                     CORE_ADDR *trampoline,
+                                     ULONGEST *trampoline_size,
                                      unsigned char *jjump_pad_insn,
                                      ULONGEST *jjump_pad_insn_size,
                                      CORE_ADDR *adjusted_insn_addr,
-                                     CORE_ADDR *adjusted_insn_addr_end)
+                                     CORE_ADDR *adjusted_insn_addr_end,
+                                     char *err)
 {
 #ifdef __x86_64__
   if (register_size (0) == 8)
     return amd64_install_fast_tracepoint_jump_pad (tpoint, tpaddr,
                                                   collector, lockaddr,
                                                   orig_size, jump_entry,
+                                                  trampoline, trampoline_size,
                                                   jjump_pad_insn,
                                                   jjump_pad_insn_size,
                                                   adjusted_insn_addr,
-                                                  adjusted_insn_addr_end);
+                                                  adjusted_insn_addr_end,
+                                                  err);
 #endif
 
   return i386_install_fast_tracepoint_jump_pad (tpoint, tpaddr,
                                                collector, lockaddr,
                                                orig_size, jump_entry,
+                                               trampoline, trampoline_size,
                                                jjump_pad_insn,
                                                jjump_pad_insn_size,
                                                adjusted_insn_addr,
-                                               adjusted_insn_addr_end);
+                                               adjusted_insn_addr_end,
+                                               err);
+}
+
+/* Return the minimum instruction length for fast tracepoints on x86/x86-64
+   architectures.  */
+
+static int
+x86_get_min_fast_tracepoint_insn_len (void)
+{
+  static int warned_about_fast_tracepoints = 0;
+
+#ifdef __x86_64__
+  /*  On x86-64, 5-byte jump instructions with a 4-byte offset are always
+      used for fast tracepoints.  */
+  if (register_size (0) == 8)
+    return 5;
+#endif
+
+  if (in_process_agent_loaded ())
+    {
+      char errbuf[IPA_BUFSIZ];
+
+      errbuf[0] = '\0';
+
+      /* On x86, if trampolines are available, then 4-byte jump instructions
+        with a 2-byte offset may be used, otherwise 5-byte jump instructions
+        with a 4-byte offset are used instead.  */
+      if (have_fast_tracepoint_trampoline_buffer (errbuf))
+       return 4;
+      else
+       {
+         /* GDB has no channel to explain to user why a shorter fast
+            tracepoint is not possible, but at least make GDBserver
+            mention that something has gone awry.  */
+         if (!warned_about_fast_tracepoints)
+           {
+             warning ("4-byte fast tracepoints not available; %s\n", errbuf);
+             warned_about_fast_tracepoints = 1;
+           }
+         return 5;
+       }
+    }
+  else
+    {
+      /* Indicate that the minimum length is currently unknown since the IPA
+        has not loaded yet.  */
+      return 0;
+    }
 }
 
 static void
@@ -2873,5 +2963,6 @@ struct linux_target_ops the_low_target =
   x86_supports_tracepoints,
   x86_get_thread_area,
   x86_install_fast_tracepoint_jump_pad,
-  x86_emit_ops
+  x86_emit_ops,
+  x86_get_min_fast_tracepoint_insn_len,
 };
index e53c852f77ea65b995a947ef87816eea58ad8ce6..207cd7453a7abde5d957288b683e64afb4b52660 100644 (file)
@@ -429,6 +429,10 @@ char *pfildes (gdb_fildes_t fd);
 
 /* Functions from tracepoint.c */
 
+/* Size for a small buffer to report problems from the in-process
+   agent back to GDBserver.  */
+#define IPA_BUFSIZ 100
+
 int in_process_agent_loaded (void);
 
 void initialize_tracepoint (void);
@@ -494,8 +498,13 @@ void supply_fast_tracepoint_registers (struct regcache *regcache,
 void supply_static_tracepoint_registers (struct regcache *regcache,
                                         const unsigned char *regs,
                                         CORE_ADDR pc);
+void set_trampoline_buffer_space (CORE_ADDR begin, CORE_ADDR end,
+                                 char *errmsg);
 #else
 void stop_tracing (void);
+
+int claim_trampoline_space (ULONGEST used, CORE_ADDR *trampoline);
+int have_fast_tracepoint_trampoline_buffer (char *msgbuf);
 #endif
 
 /* Bytecode compilation function vector.  */
index f60e0a601d30853c55d5419c590dc28dbd8af4d1..8a254769837044666e6f3fd4daf89eb2fbd52a9c 100644 (file)
@@ -359,20 +359,26 @@ struct target_ops
      pad lock object.  ORIG_SIZE is the size in bytes of the
      instruction at TPADDR.  JUMP_ENTRY points to the address of the
      jump pad entry, and on return holds the address past the end of
-     the created jump pad. JJUMP_PAD_INSN is a buffer containing a
-     copy of the instruction at TPADDR.  ADJUST_INSN_ADDR and
-     ADJUST_INSN_ADDR_END are output parameters that return the
-     address range where the instruction at TPADDR was relocated
-     to.  */
+     the created jump pad.  If a trampoline is created by the function,
+     then TRAMPOLINE and TRAMPOLINE_SIZE return the address and size of
+     the trampoline, else they remain unchanged.  JJUMP_PAD_INSN is a
+     buffer containing a copy of the instruction at TPADDR.
+     ADJUST_INSN_ADDR and ADJUST_INSN_ADDR_END are output parameters that
+     return the address range where the instruction at TPADDR was relocated
+     to.  If an error occurs, the ERR may be used to pass on an error
+     message.  */
   int (*install_fast_tracepoint_jump_pad) (CORE_ADDR tpoint, CORE_ADDR tpaddr,
                                           CORE_ADDR collector,
                                           CORE_ADDR lockaddr,
                                           ULONGEST orig_size,
                                           CORE_ADDR *jump_entry,
+                                          CORE_ADDR *trampoline,
+                                          ULONGEST *trampoline_size,
                                           unsigned char *jjump_pad_insn,
                                           ULONGEST *jjump_pad_insn_size,
                                           CORE_ADDR *adjusted_insn_addr,
-                                          CORE_ADDR *adjusted_insn_addr_end);
+                                          CORE_ADDR *adjusted_insn_addr_end,
+                                          char *err);
 
   /* Return the bytecode operations vector for the current inferior.
      Returns NULL if bytecode compilation is not supported.  */
@@ -380,6 +386,10 @@ struct target_ops
 
   /* Returns true if the target supports disabling randomization.  */
   int (*supports_disable_randomization) (void);
+
+  /* Return the minimum length of an instruction that can be safely overwritten
+     for use as a fast tracepoint.  */
+  int (*get_min_fast_tracepoint_insn_len) (void);
 };
 
 extern struct target_ops *the_target;
@@ -437,6 +447,10 @@ void set_target_ops (struct target_ops *);
 #define target_supports_fast_tracepoints()             \
   (the_target->install_fast_tracepoint_jump_pad != NULL)
 
+#define target_get_min_fast_tracepoint_insn_len()      \
+  (the_target->get_min_fast_tracepoint_insn_len                \
+   ? (*the_target->get_min_fast_tracepoint_insn_len) () : 0)
+
 #define thread_stopped(thread) \
   (*the_target->thread_stopped) (thread)
 
@@ -471,17 +485,23 @@ void set_target_ops (struct target_ops *);
 #define install_fast_tracepoint_jump_pad(tpoint, tpaddr,               \
                                         collector, lockaddr,           \
                                         orig_size,                     \
-                                        jump_entry, jjump_pad_insn,    \
+                                        jump_entry,                    \
+                                        trampoline, trampoline_size,   \
+                                        jjump_pad_insn,                \
                                         jjump_pad_insn_size,           \
                                         adjusted_insn_addr,            \
-                                        adjusted_insn_addr_end)        \
+                                        adjusted_insn_addr_end,        \
+                                        err)                           \
   (*the_target->install_fast_tracepoint_jump_pad) (tpoint, tpaddr,     \
                                                   collector,lockaddr,  \
                                                   orig_size, jump_entry, \
+                                                  trampoline,          \
+                                                  trampoline_size,     \
                                                   jjump_pad_insn,      \
                                                   jjump_pad_insn_size, \
                                                   adjusted_insn_addr,  \
-                                                  adjusted_insn_addr_end)
+                                                  adjusted_insn_addr_end, \
+                                                  err)
 
 #define target_emit_ops() \
   (the_target->emit_ops ? (*the_target->emit_ops) () : NULL)
index f000f63777554a22e9c7365f498a5ebfaf490d5a..b00df0575c5476ecd60837ec4f8ba12c52b2a1dc 100644 (file)
@@ -110,6 +110,9 @@ trace_vdebug (const char *fmt, ...)
 # define gdb_tp_heap_buffer gdb_agent_gdb_tp_heap_buffer
 # define gdb_jump_pad_buffer gdb_agent_gdb_jump_pad_buffer
 # define gdb_jump_pad_buffer_end gdb_agent_gdb_jump_pad_buffer_end
+# define gdb_trampoline_buffer gdb_agent_gdb_trampoline_buffer
+# define gdb_trampoline_buffer_end gdb_agent_gdb_trampoline_buffer_end
+# define gdb_trampoline_buffer_error gdb_agent_gdb_trampoline_buffer_error
 # define collecting gdb_agent_collecting
 # define gdb_collect gdb_agent_gdb_collect
 # define stop_tracing gdb_agent_stop_tracing
@@ -148,6 +151,9 @@ struct ipa_sym_addresses
   CORE_ADDR addr_gdb_tp_heap_buffer;
   CORE_ADDR addr_gdb_jump_pad_buffer;
   CORE_ADDR addr_gdb_jump_pad_buffer_end;
+  CORE_ADDR addr_gdb_trampoline_buffer;
+  CORE_ADDR addr_gdb_trampoline_buffer_end;
+  CORE_ADDR addr_gdb_trampoline_buffer_error;
   CORE_ADDR addr_collecting;
   CORE_ADDR addr_gdb_collect;
   CORE_ADDR addr_stop_tracing;
@@ -192,6 +198,9 @@ static struct
   IPA_SYM(gdb_tp_heap_buffer),
   IPA_SYM(gdb_jump_pad_buffer),
   IPA_SYM(gdb_jump_pad_buffer_end),
+  IPA_SYM(gdb_trampoline_buffer),
+  IPA_SYM(gdb_trampoline_buffer_end),
+  IPA_SYM(gdb_trampoline_buffer_error),
   IPA_SYM(collecting),
   IPA_SYM(gdb_collect),
   IPA_SYM(stop_tracing),
@@ -658,6 +667,12 @@ struct tracepoint
   CORE_ADDR jump_pad;
   CORE_ADDR jump_pad_end;
 
+  /* The address range of the piece of the trampoline buffer that was
+     assigned to this fast tracepoint.  (_end is actually one byte
+     past the end).  */
+  CORE_ADDR trampoline;
+  CORE_ADDR trampoline_end;
+
   /* The list of actions to take while in a stepping loop.  These
      fields are only valid for patch-based tracepoints.  */
   int num_step_actions;
@@ -1248,7 +1263,7 @@ static struct tracepoint *fast_tracepoint_from_ipa_tpoint_address (CORE_ADDR);
 
 static void install_tracepoint (struct tracepoint *, char *own_buf);
 static void download_tracepoint (struct tracepoint *);
-static int install_fast_tracepoint (struct tracepoint *);
+static int install_fast_tracepoint (struct tracepoint *, char *errbuf);
 #endif
 
 #if defined(__GNUC__)
@@ -2711,6 +2726,85 @@ claim_jump_space (ULONGEST used)
   gdb_jump_pad_head += used;
 }
 
+static CORE_ADDR trampoline_buffer_head = 0;
+static CORE_ADDR trampoline_buffer_tail;
+
+/* Reserve USED bytes from the trampoline buffer and return the
+   address of the start of the reserved space in TRAMPOLINE.  Returns
+   non-zero if the space is successfully claimed.  */
+
+int
+claim_trampoline_space (ULONGEST used, CORE_ADDR *trampoline)
+{
+  if (!trampoline_buffer_head)
+    {
+      if (read_inferior_data_pointer (ipa_sym_addrs.addr_gdb_trampoline_buffer,
+                                     &trampoline_buffer_tail))
+       {
+         fatal ("error extracting trampoline_buffer");
+         return 0;
+       }
+
+      if (read_inferior_data_pointer (ipa_sym_addrs.addr_gdb_trampoline_buffer_end,
+                                     &trampoline_buffer_head))
+       {
+         fatal ("error extracting trampoline_buffer_end");
+         return 0;
+       }
+    }
+
+  /* Start claiming space from the top of the trampoline space.  If
+     the space is located at the bottom of the virtual address space,
+     this reduces the possibility that corruption will occur if a null
+     pointer is used to write to memory.  */
+  if (trampoline_buffer_head - trampoline_buffer_tail < used)
+    {
+      trace_debug ("claim_trampoline_space failed to reserve %s bytes",
+                  pulongest (used));
+      return 0;
+    }
+
+  trampoline_buffer_head -= used;
+
+  trace_debug ("claim_trampoline_space reserves %s bytes at %s",
+              pulongest (used), paddress (trampoline_buffer_head));
+
+  *trampoline = trampoline_buffer_head;
+  return 1;
+}
+
+/* Returns non-zero if there is space allocated for use in trampolines
+   for fast tracepoints.  */
+
+int
+have_fast_tracepoint_trampoline_buffer (char *buf)
+{
+  CORE_ADDR trampoline_end, errbuf;
+
+  if (read_inferior_data_pointer (ipa_sym_addrs.addr_gdb_trampoline_buffer_end,
+                                 &trampoline_end))
+    {
+      fatal ("error extracting trampoline_buffer_end");
+      return 0;
+    }
+  
+  if (buf)
+    {
+      buf[0] = '\0';
+      strcpy (buf, "was claiming");
+      if (read_inferior_data_pointer (ipa_sym_addrs.addr_gdb_trampoline_buffer_error,
+                                 &errbuf))
+       {
+         fatal ("error extracting errbuf");
+         return 0;
+       }
+
+      read_inferior_memory (errbuf, (unsigned char *) buf, 100);
+    }
+
+  return trampoline_end != 0;
+}
+
 /* Ask the IPA to probe the marker at ADDRESS.  Returns -1 if running
    the command fails, or 0 otherwise.  If the command ran
    successfully, but probing the marker failed, ERROUT will be filled
@@ -2743,6 +2837,8 @@ clone_fast_tracepoint (struct tracepoint *to, const struct tracepoint *from)
 {
   to->jump_pad = from->jump_pad;
   to->jump_pad_end = from->jump_pad_end;
+  to->trampoline = from->trampoline;
+  to->trampoline_end = from->trampoline_end;
   to->adjusted_insn_addr = from->adjusted_insn_addr;
   to->adjusted_insn_addr_end = from->adjusted_insn_addr_end;
   to->handle = from->handle;
@@ -2757,26 +2853,41 @@ clone_fast_tracepoint (struct tracepoint *to, const struct tracepoint *from)
    non-zero.  */
 
 static int
-install_fast_tracepoint (struct tracepoint *tpoint)
+install_fast_tracepoint (struct tracepoint *tpoint, char *errbuf)
 {
   CORE_ADDR jentry, jump_entry;
+  CORE_ADDR trampoline;
+  ULONGEST trampoline_size;
   int err = 0;
   /* The jump to the jump pad of the last fast tracepoint
      installed.  */
   unsigned char fjump[MAX_JUMP_SIZE];
   ULONGEST fjump_size;
 
+  if (tpoint->orig_size < target_get_min_fast_tracepoint_insn_len ())
+    {
+      trace_debug ("Requested a fast tracepoint on an instruction "
+                  "that is of less than the minimum length.");
+      return 0;
+    }
+
   jentry = jump_entry = get_jump_space_head ();
 
+  trampoline = 0;
+  trampoline_size = 0;
+
   /* Install the jump pad.  */
   err = install_fast_tracepoint_jump_pad (tpoint->obj_addr_on_target,
                                          tpoint->address,
                                          ipa_sym_addrs.addr_gdb_collect,
                                          ipa_sym_addrs.addr_collecting,
                                          tpoint->orig_size,
-                                         &jentry, fjump, &fjump_size,
+                                         &jentry,
+                                         &trampoline, &trampoline_size,
+                                         fjump, &fjump_size,
                                          &tpoint->adjusted_insn_addr,
-                                         &tpoint->adjusted_insn_addr_end);
+                                         &tpoint->adjusted_insn_addr_end,
+                                         errbuf);
 
   if (err)
     return 1;
@@ -2789,6 +2900,8 @@ install_fast_tracepoint (struct tracepoint *tpoint)
     {
       tpoint->jump_pad = jump_entry;
       tpoint->jump_pad_end = jentry;
+      tpoint->trampoline = trampoline;
+      tpoint->trampoline_end = trampoline + trampoline_size;
 
       /* Pad to 8-byte alignment.  */
       jentry = ((jentry + 7) & ~0x7);
@@ -2849,7 +2962,7 @@ install_tracepoint (struct tracepoint *tpoint, char *own_buf)
          if (tp) /* TPOINT is installed at the same address as TP.  */
            clone_fast_tracepoint (tpoint, tp);
          else
-           install_fast_tracepoint (tpoint);
+           install_fast_tracepoint (tpoint, own_buf);
        }
       else
        {
@@ -2937,9 +3050,8 @@ cmd_qtstart (char *packet)
            clone_fast_tracepoint (tpoint, prev_ftpoint);
          else
            {
-             if (install_fast_tracepoint (tpoint) == 0)
+             if (install_fast_tracepoint (tpoint, packet) == 0)
                prev_ftpoint = tpoint;
-
            }
        }
       else if (tpoint->type == static_tracepoint)
@@ -3514,6 +3626,15 @@ cmd_qtstmat (char *packet)
     run_inferior_command (packet);
 }
 
+/* Return the minimum instruction size needed for fast tracepoints as a
+   hexadecimal number.  */
+
+static void
+cmd_qtminftpilen (char *packet)
+{
+  sprintf (packet, "%x", target_get_min_fast_tracepoint_insn_len ());
+}
+
 /* Respond to qTBuffer packet with a block of raw data from the trace
    buffer.  GDB may ask for a lot, but we are allowed to reply with
    only as much as will fit within packet limits or whatever.  */
@@ -3710,6 +3831,11 @@ handle_tracepoint_query (char *packet)
       cmd_qtstmat (packet);
       return 1;
     }
+  else if (strcmp ("qTMinFTPILen", packet) == 0)
+    {
+      cmd_qtminftpilen (packet);
+      return 1;
+    }
 
   return 0;
 }
@@ -5326,6 +5452,23 @@ fast_tracepoint_from_jump_pad_address (CORE_ADDR pc)
   return NULL;
 }
 
+/* Return the first fast tracepoint whose trampoline contains PC.  */
+
+static struct tracepoint *
+fast_tracepoint_from_trampoline_address (CORE_ADDR pc)
+{
+  struct tracepoint *tpoint;
+
+  for (tpoint = tracepoints; tpoint; tpoint = tpoint->next)
+    {
+      if (tpoint->type == fast_tracepoint
+         && tpoint->trampoline <= pc && pc < tpoint->trampoline_end)
+       return tpoint;
+    }
+
+  return NULL;
+}
+
 /* Return GDBserver's tracepoint that matches the IP Agent's
    tracepoint object that lives at IPA_TPOINT_OBJ in the IP Agent's
    address space.  */
@@ -5388,6 +5531,8 @@ fast_tracepoint_collecting (CORE_ADDR thread_area,
 {
   CORE_ADDR ipa_collecting;
   CORE_ADDR ipa_gdb_jump_pad_buffer, ipa_gdb_jump_pad_buffer_end;
+  CORE_ADDR ipa_gdb_trampoline_buffer;
+  CORE_ADDR ipa_gdb_trampoline_buffer_end;
   struct tracepoint *tpoint;
   int needs_breakpoint;
 
@@ -5426,6 +5571,13 @@ fast_tracepoint_collecting (CORE_ADDR thread_area,
                                  &ipa_gdb_jump_pad_buffer_end))
     fatal ("error extracting `gdb_jump_pad_buffer_end'");
 
+  if (read_inferior_data_pointer (ipa_sym_addrs.addr_gdb_trampoline_buffer,
+                                 &ipa_gdb_trampoline_buffer))
+    fatal ("error extracting `gdb_trampoline_buffer'");
+  if (read_inferior_data_pointer (ipa_sym_addrs.addr_gdb_trampoline_buffer_end,
+                                 &ipa_gdb_trampoline_buffer_end))
+    fatal ("error extracting `gdb_trampoline_buffer_end'");
+
   if (ipa_gdb_jump_pad_buffer <= stop_pc
       && stop_pc < ipa_gdb_jump_pad_buffer_end)
     {
@@ -5454,6 +5606,30 @@ fast_tracepoint_collecting (CORE_ADDR thread_area,
          && stop_pc < tpoint->adjusted_insn_addr)
        needs_breakpoint =  1;
     }
+  else if (ipa_gdb_trampoline_buffer <= stop_pc
+          && stop_pc < ipa_gdb_trampoline_buffer_end)
+    {
+      /* We can tell which tracepoint(s) the thread is collecting by
+        matching the trampoline address back to the tracepoint.  */
+      tpoint = fast_tracepoint_from_trampoline_address (stop_pc);
+      if (tpoint == NULL)
+       {
+         warning ("in trampoline, but no matching tpoint?");
+         return 0;
+       }
+      else
+       {
+         trace_debug ("in trampoline of tpoint (%d, %s); trampoline(%s, %s)",
+                      tpoint->number, paddress (tpoint->address),
+                      paddress (tpoint->trampoline),
+                      paddress (tpoint->trampoline_end));
+       }
+
+      /* Have not reached jump pad yet, but treat the trampoline as a
+        part of the jump pad that is before the adjusted original
+        instruction.  */
+      needs_breakpoint = 1;
+    }
   else
     {
       collecting_t ipa_collecting_obj;
@@ -7842,6 +8018,24 @@ gdb_ust_init (void)
 IP_AGENT_EXPORT char *gdb_tp_heap_buffer;
 IP_AGENT_EXPORT char *gdb_jump_pad_buffer;
 IP_AGENT_EXPORT char *gdb_jump_pad_buffer_end;
+IP_AGENT_EXPORT char *gdb_trampoline_buffer;
+IP_AGENT_EXPORT char *gdb_trampoline_buffer_end;
+IP_AGENT_EXPORT char *gdb_trampoline_buffer_error;
+
+/* Record the result of getting buffer space for fast tracepoint
+   trampolines.  Any error message is copied, since caller may not be
+   using persistent storage.  */
+
+void
+set_trampoline_buffer_space (CORE_ADDR begin, CORE_ADDR end, char *errmsg)
+{
+  gdb_trampoline_buffer = (char *) (uintptr_t) begin;
+  gdb_trampoline_buffer_end = (char *) (uintptr_t) end;
+  if (errmsg)
+    strncpy (gdb_trampoline_buffer_error, errmsg, 99);
+  else
+    strcpy (gdb_trampoline_buffer_error, "no buffer passed");
+}
 
 static void __attribute__ ((constructor))
 initialize_tracepoint_ftlib (void)
@@ -7903,6 +8097,16 @@ initialize_tracepoint: mprotect(%p, %d, PROT_READ|PROT_EXEC) failed with %s",
             gdb_jump_pad_buffer, pagesize * 20, strerror (errno));
   }
 
+  gdb_trampoline_buffer = gdb_trampoline_buffer_end = 0;
+
+  /* It's not a fatal error for something to go wrong with trampoline
+     buffer setup, but it can be mysterious, so create a channel to
+     report back on what went wrong, using a fixed size since we may
+     not be able to allocate space later when the problem occurs.  */
+  gdb_trampoline_buffer_error = xmalloc (IPA_BUFSIZ);
+
+  strcpy (gdb_trampoline_buffer_error, "No errors reported");
+
   initialize_low_tracepoint ();
 #endif
 }
index 2f0b6f5e9299af109bfa9737ed6f41d2b75836f7..a4e3a220554f0807a7929773470c7fef5b24dfd0 100644 (file)
@@ -7109,10 +7109,12 @@ static const int i386_record_regmap[] =
 };
 
 /* Check that the given address appears suitable for a fast
-   tracepoint, which on x86 means that we need an instruction of at
+   tracepoint, which on x86-64 means that we need an instruction of at
    least 5 bytes, so that we can overwrite it with a 4-byte-offset
    jump and not have to worry about program jumps to an address in the
-   middle of the tracepoint jump.  Returns 1 if OK, and writes a size
+   middle of the tracepoint jump.  On x86, it may be possible to use
+   4-byte jumps with a 2-byte offset to a trampoline located in the
+   bottom 64 KiB of memory.  Returns 1 if OK, and writes a size
    of instruction to replace, and 0 if not, plus an explanatory
    string.  */
 
@@ -7123,10 +7125,26 @@ i386_fast_tracepoint_valid_at (struct gdbarch *gdbarch,
   int len, jumplen;
   static struct ui_file *gdb_null = NULL;
 
-  /* This is based on the target agent using a 4-byte relative jump.
-     Alternate future possibilities include 8-byte offset for x86-84,
-     or 3-byte jumps if the program has trampoline space close by.  */
-  jumplen = 5;
+  /*  Ask the target for the minimum instruction length supported.  */
+  jumplen = target_get_min_fast_tracepoint_insn_len ();
+
+  if (jumplen < 0)
+    {
+      /* If the target does not support the get_min_fast_tracepoint_insn_len
+        operation, assume that fast tracepoints will always be implemented
+        using 4-byte relative jumps on both x86 and x86-64.  */
+      jumplen = 5;
+    }
+  else if (jumplen == 0)
+    {
+      /* If the target does support get_min_fast_tracepoint_insn_len but
+        returns zero, then the IPA has not loaded yet.  In this case,
+        we optimistically assume that truncated 2-byte relative jumps
+        will be available on x86, and compensate later if this assumption
+        turns out to be incorrect.  On x86-64 architectures, 4-byte relative
+        jumps will always be used.  */
+      jumplen = (register_size (gdbarch, 0) == 8) ? 5 : 4;
+    }
 
   /* Dummy file descriptor for the disassembler.  */
   if (!gdb_null)
@@ -7134,6 +7152,9 @@ i386_fast_tracepoint_valid_at (struct gdbarch *gdbarch,
 
   /* Check for fit.  */
   len = gdb_print_insn (gdbarch, addr, gdb_null, NULL);
+  if (isize)
+    *isize = len;
+
   if (len < jumplen)
     {
       /* Return a bit of target-specific detail to add to the caller's
@@ -7144,12 +7165,12 @@ i386_fast_tracepoint_valid_at (struct gdbarch *gdbarch,
                           len, jumplen);
       return 0;
     }
-
-  if (isize)
-    *isize = len;
-  if (msg)
-    *msg = NULL;
-  return 1;
+  else
+    {
+      if (msg)
+       *msg = NULL;
+      return 1;
+    }
 }
 
 static int
index a6d5e5f72ac0dae302f5a529d6fbc17741412f8e..6e9e242eeb8b2fdce1f895b9523ce8f493690301 100644 (file)
@@ -6370,7 +6370,7 @@ remote_write_bytes_aux (const char *header, CORE_ADDR memaddr,
 
   if (todo <= 0)
     internal_error (__FILE__, __LINE__,
-                   _("minumum packet size too small to write data"));
+                   _("minimum packet size too small to write data"));
 
   /* If we already need another packet, then try to align the end
      of this packet to a useful boundary.  */
@@ -10451,6 +10451,32 @@ remote_traceframe_info (void)
   return NULL;
 }
 
+/* Handle the qTMinFTPILen packet.  Returns the minimum length of
+   instruction on which a fast tracepoint may be placed.  Returns -1
+   if the packet is not supported, and 0 if the minimum instruction
+   length is unknown.  */
+
+static int
+remote_get_min_fast_tracepoint_insn_len (void)
+{
+  struct remote_state *rs = get_remote_state ();
+  char *reply;
+
+  sprintf (rs->buf, "qTMinFTPILen");
+  putpkt (rs->buf);
+  reply = remote_get_noisy_reply (&target_buf, &target_buf_size);
+  if (*reply == '\0')
+    return -1;
+  else
+    {
+      ULONGEST min_insn_len;
+
+      unpack_varlen_hex (reply, &min_insn_len);
+
+      return (int) min_insn_len;
+    }
+}
+
 static void
 init_remote_ops (void)
 {
@@ -10540,6 +10566,7 @@ Specify the serial device it is connected to\n\
   remote_ops.to_upload_trace_state_variables
     = remote_upload_trace_state_variables;
   remote_ops.to_get_raw_trace_data = remote_get_raw_trace_data;
+  remote_ops.to_get_min_fast_tracepoint_insn_len = remote_get_min_fast_tracepoint_insn_len;
   remote_ops.to_set_disconnected_tracing = remote_set_disconnected_tracing;
   remote_ops.to_set_circular_trace_buffer = remote_set_circular_trace_buffer;
   remote_ops.to_core_of_thread = remote_core_of_thread;
index 16b2128e9e0659af8fe2bb6dd9869075d789cb81..09be1ba2663ad7236ef93eb67c46eff6d06e5951 100644 (file)
@@ -689,6 +689,7 @@ update_current_target (void)
       INHERIT (to_upload_tracepoints, t);
       INHERIT (to_upload_trace_state_variables, t);
       INHERIT (to_get_raw_trace_data, t);
+      INHERIT (to_get_min_fast_tracepoint_insn_len, t);
       INHERIT (to_set_disconnected_tracing, t);
       INHERIT (to_set_circular_trace_buffer, t);
       INHERIT (to_get_tib_address, t);
@@ -893,6 +894,9 @@ update_current_target (void)
   de_fault (to_get_raw_trace_data,
            (LONGEST (*) (gdb_byte *, ULONGEST, LONGEST))
            tcomplain);
+  de_fault (to_get_min_fast_tracepoint_insn_len,
+           (int (*) (void))
+           return_minus_one);
   de_fault (to_set_disconnected_tracing,
            (void (*) (int))
            target_ignore);
index c8941a4058123b6569e9040594b2ff426b48e51c..87f0bb9d8994b206cf2a73523aff9d31d56c7a2a 100644 (file)
@@ -738,6 +738,12 @@ struct target_ops
     LONGEST (*to_get_raw_trace_data) (gdb_byte *buf,
                                      ULONGEST offset, LONGEST len);
 
+    /* Get the minimum length of instruction on which a fast tracepoint
+       may be set on the target.  If this operation is unsupported,
+       return -1.  If for some reason the minimum length cannot be
+       determined, return 0.  */
+    int (*to_get_min_fast_tracepoint_insn_len) (void);
+
     /* Set the target's tracing behavior in response to unexpected
        disconnection - set VAL to 1 to keep tracing, 0 to stop.  */
     void (*to_set_disconnected_tracing) (int val);
@@ -1523,6 +1529,9 @@ extern int target_search_memory (CORE_ADDR start_addr,
 #define target_get_raw_trace_data(buf,offset,len) \
   (*current_target.to_get_raw_trace_data) ((buf), (offset), (len))
 
+#define target_get_min_fast_tracepoint_insn_len() \
+  (*current_target.to_get_min_fast_tracepoint_insn_len) ()
+
 #define target_set_disconnected_tracing(val) \
   (*current_target.to_set_disconnected_tracing) (val)
 
index 8a6825f3bc15d8433732bdea284fd32de3ef86f6..a53f52f3312a4bc693de4d1da4784f8493ddc486 100644 (file)
@@ -1,3 +1,8 @@
+2011-11-14  Stan Shebs  <stan@codesourcery.com>
+
+       * gdb.trace/ftrace.c: New.
+       * gdb.trace/ftrace.exp: New.
+
 2011-11-14  Yao Qi  <yao@codesourcery.com>
 
        * gdb.trace/change-loc-1.c: New.
diff --git a/gdb/testsuite/gdb.trace/ftrace.c b/gdb/testsuite/gdb.trace/ftrace.c
new file mode 100644 (file)
index 0000000..a7d008d
--- /dev/null
@@ -0,0 +1,76 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2011 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifdef SYMBOL_PREFIX
+#define SYMBOL(str)     SYMBOL_PREFIX #str
+#else
+#define SYMBOL(str)     #str
+#endif
+
+int globvar;
+
+static void
+begin (void)
+{}
+
+/* Called from asm.  */
+static void __attribute__((used))
+func (void)
+{}
+
+static void
+marker (int anarg)
+{
+  /* `set_point' is the label at which to set a fast tracepoint.  The
+     insn at the label must be large enough to fit a fast tracepoint
+     jump.  */
+  asm ("    .global " SYMBOL(set_point) "\n"
+       SYMBOL(set_point) ":\n"
+#if (defined __x86_64__ || defined __i386__)
+       "    call " SYMBOL(func) "\n"
+#endif
+       );
+
+  ++anarg;
+
+  /* Set up a known 4-byte instruction so we can try to set a shorter
+     fast tracepoint at it.  */
+  asm ("    .global " SYMBOL(four_byter) "\n"
+       SYMBOL(four_byter) ":\n"
+#if (defined __i386__)
+       "    cmpl $0x1,0x8(%ebp) \n"
+#endif
+       );
+}
+
+static void
+end (void)
+{}
+
+int
+main ()
+{
+  begin ();
+
+  for (globvar = 1; globvar < 11; ++globvar)
+    {
+      marker (globvar * 100);
+    }
+
+  end ();
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.trace/ftrace.exp b/gdb/testsuite/gdb.trace/ftrace.exp
new file mode 100644 (file)
index 0000000..f45c6da
--- /dev/null
@@ -0,0 +1,171 @@
+# Copyright 2011 Free Software Foundation, Inc.
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+load_lib "trace-support.exp";
+
+set testfile "ftrace"
+set executable $testfile
+set srcfile $testfile.c
+set binfile $objdir/$subdir/$testfile
+set expfile $testfile.exp
+
+# Some targets have leading underscores on assembly symbols.
+set additional_flags [gdb_target_symbol_prefix_flags]
+
+if [prepare_for_testing $expfile $executable $srcfile \
+       [list debug $additional_flags]] {
+    untested "failed to prepare for trace tests"
+    return -1
+}
+
+if ![runto_main] {
+    fail "Can't run to main to check for trace support"
+    return -1
+}
+
+if ![gdb_target_supports_trace] {
+    unsupported "target does not support trace"
+    return -1
+}
+
+set libipa $objdir/../gdbserver/libinproctrace.so
+gdb_load_shlibs $libipa
+
+# Can't use prepare_for_testing, because that splits compiling into
+# building objects and then linking, and we'd fail with "linker input
+# file unused because linking not done" when building the object.
+
+if { [gdb_compile "$srcdir/$subdir/$srcfile" $binfile \
+         executable [list debug $additional_flags shlib=$libipa] ] != "" } {
+    untested "failed to compile ftrace tests"
+    return -1
+}
+clean_restart ${executable}
+
+if ![runto_main] {
+    fail "Can't run to main for ftrace tests"
+    return 0
+}
+
+proc run_trace_experiment {} {
+
+    gdb_test "continue" \
+       ".*Breakpoint \[0-9\]+, begin .*" \
+       "advance to trace begin"
+
+    gdb_test_no_output "tstart" "start trace experiment"
+
+    gdb_test "continue" \
+       ".*Breakpoint \[0-9\]+, end .*" \
+       "advance through tracing"
+
+    gdb_test "tstatus" ".*Trace .*" "check on trace status"
+
+    gdb_test "tstop" "" ""
+}
+
+proc test_fast_tracepoints {} {
+
+    set fourgood 0
+
+    gdb_test "break begin" ".*" ""
+
+    gdb_test "break end" ".*" ""
+
+    gdb_test "print gdb_agent_gdb_trampoline_buffer_error" ".*" ""
+
+    if { [is_x86_like_target] } {
+
+       gdb_test "ftrace set_point" "Fast tracepoint .*" \
+           "fast tracepoint at a long insn"
+
+       gdb_trace_setactions "collect at set_point: define actions" \
+           "" \
+           "collect globvar, anarg" "^$"
+
+       # Make a test of shorter fast tracepoints, 32-bit x86 only
+
+       if { [istarget "i?86-*-*"] } {
+
+           # A Linux target needs to be able to allocate trampolines in the
+           # 16-bit range, check mmap_min_addr so we can warn testers.
+           if { [istarget "i?86-*-linux*"] } {
+
+               set minaddr [exec sh -c "cat /proc/sys/vm/mmap_min_addr"]
+
+               if { [expr $minaddr > 64512] } {
+                   warning "mmap_min_addr > 64512, fast tracepoint will fail"
+                   warning "do \"sudo sysctl -w vm.mmap_min_addr=32768\" to adjust"
+               }
+           }
+
+           gdb_test_multiple "ftrace four_byter" "set 4-byte fast tracepoint" {
+               -re "May not have a fast tracepoint at .*" {
+                   pass "4-byte fast tracepoint could not be set"
+               }
+               -re "Fast tracepoint .*" {
+                   pass "4-byte fast tracepoint is set"
+                   set fourgood 1
+               }
+           }
+
+           if { $fourgood } {
+
+               gdb_trace_setactions "collect at four_byter: define actions" \
+               "" \
+               "collect globvar, anarg" "^$"
+           }
+       }
+
+       run_trace_experiment
+
+       gdb_test "tfind pc *set_point" "Found trace frame .*" \
+           "tfind set_point frame, first time"
+
+       gdb_test "print globvar" " = 1"
+
+       gdb_test "tfind pc *set_point" "Found trace frame .*" \
+           "tfind set_point frame, second time"
+
+       gdb_test "print anarg" " = 200"
+
+       gdb_test "tfind start" "Found trace frame .*" \
+           "reset tfinding"
+
+       if { $fourgood } {
+
+           gdb_test "tfind pc *four_byter" "Found trace frame .*" \
+               "tfind four_byter frame, first time"
+
+           gdb_test "print anarg" " = 101" \
+               "look at collected local, first time"
+
+           gdb_test "tfind pc *four_byter" "Found trace frame .*" \
+               "tfind four_byter frame, second time"
+
+           gdb_test "print anarg" " = 201" \
+               "look at collected local, second time"
+
+       }
+    }
+}
+
+gdb_reinitialize_dir $srcdir/$subdir
+
+if { [gdb_test "info sharedlibrary" ".*libinproctrace\.so.*" "IPA loaded"] != 0 } {
+    untested "Could not find IPA lib loaded"
+    return 1
+}
+
+test_fast_tracepoints