+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.
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
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.
+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
@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
@itemx qTfP
@itemx qTfV
@itemx QTFrame
+@itemx qTMinFTPILen
+
@xref{Tracepoint Packets}.
@item qThreadExtraInfo,@var{thread-id}
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
+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.
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. */
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 ();
}
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 *
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,
linux_install_fast_tracepoint_jump_pad,
linux_emit_ops,
linux_supports_disable_randomization,
+ linux_get_min_fast_tracepoint_insn_len,
};
static void
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;
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. */
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;
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;
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;
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;
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
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,
};
/* 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);
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. */
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. */
/* 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;
#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)
#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)
# 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
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;
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),
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;
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__)
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
{
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;
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;
{
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);
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
{
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)
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. */
cmd_qtstmat (packet);
return 1;
}
+ else if (strcmp ("qTMinFTPILen", packet) == 0)
+ {
+ cmd_qtminftpilen (packet);
+ return 1;
+ }
return 0;
}
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. */
{
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;
&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)
{
&& 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;
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)
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
}
};
/* 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. */
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)
/* 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
len, jumplen);
return 0;
}
-
- if (isize)
- *isize = len;
- if (msg)
- *msg = NULL;
- return 1;
+ else
+ {
+ if (msg)
+ *msg = NULL;
+ return 1;
+ }
}
static int
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. */
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)
{
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;
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);
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);
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);
#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)
+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.
--- /dev/null
+/* 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;
+}
--- /dev/null
+# 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