diff --git a/gdb/ChangeLog b/gdb/ChangeLog index bfeecc7fae..72f5a62ba4 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,17 @@ +2011-11-14 Stan Shebs + Kwok Cheung Yeung + + * 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 * tracepoint.c (encode_actions_1): Use the location's gdbarch. diff --git a/gdb/NEWS b/gdb/NEWS index 52366f86fd..dc16a4bc07 100644 --- 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. diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog index ac38897619..02ef07b13d 100644 --- a/gdb/doc/ChangeLog +++ b/gdb/doc/ChangeLog @@ -1,3 +1,10 @@ +2011-11-14 Stan Shebs + Kwok Cheung Yeung + + * 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 * gdb.texinfo (Create and Delete Tracepoints): Describe changed diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index f17c80061d..0cadc9681d 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -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 diff --git a/gdb/gdbserver/ChangeLog b/gdb/gdbserver/ChangeLog index 2a5aae92c5..844fb1836d 100644 --- a/gdb/gdbserver/ChangeLog +++ b/gdb/gdbserver/ChangeLog @@ -1,3 +1,54 @@ +2011-11-14 Stan Shebs + Kwok Cheung Yeung + + * 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 * server.c (handle_query): Handle InstallInTrace for qSupported. diff --git a/gdb/gdbserver/linux-i386-ipa.c b/gdb/gdbserver/linux-i386-ipa.c index f0eeb4af54..5906464049 100644 --- a/gdb/gdbserver/linux-i386-ipa.c +++ b/gdb/gdbserver/linux-i386-ipa.c @@ -19,6 +19,8 @@ along with this program. If not, see . */ #include "server.h" +#include +#include /* 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 (); } diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c index e8cf374635..2b973c69c4 100644 --- a/gdb/gdbserver/linux-low.c +++ b/gdb/gdbserver/linux-low.c @@ -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 diff --git a/gdb/gdbserver/linux-low.h b/gdb/gdbserver/linux-low.h index 6635bc656f..a4e3d5ac6b 100644 --- a/gdb/gdbserver/linux-low.h +++ b/gdb/gdbserver/linux-low.h @@ -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; diff --git a/gdb/gdbserver/linux-x86-low.c b/gdb/gdbserver/linux-x86-low.c index 4cc3feb956..d1c760e9e6 100644 --- a/gdb/gdbserver/linux-x86-low.c +++ b/gdb/gdbserver/linux-x86-low.c @@ -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, }; diff --git a/gdb/gdbserver/server.h b/gdb/gdbserver/server.h index e53c852f77..207cd7453a 100644 --- a/gdb/gdbserver/server.h +++ b/gdb/gdbserver/server.h @@ -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. */ diff --git a/gdb/gdbserver/target.h b/gdb/gdbserver/target.h index f60e0a601d..8a25476983 100644 --- a/gdb/gdbserver/target.h +++ b/gdb/gdbserver/target.h @@ -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) diff --git a/gdb/gdbserver/tracepoint.c b/gdb/gdbserver/tracepoint.c index f000f63777..b00df0575c 100644 --- a/gdb/gdbserver/tracepoint.c +++ b/gdb/gdbserver/tracepoint.c @@ -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 } diff --git a/gdb/i386-tdep.c b/gdb/i386-tdep.c index 2f0b6f5e92..a4e3a22055 100644 --- a/gdb/i386-tdep.c +++ b/gdb/i386-tdep.c @@ -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 diff --git a/gdb/remote.c b/gdb/remote.c index a6d5e5f72a..6e9e242eeb 100644 --- a/gdb/remote.c +++ b/gdb/remote.c @@ -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; diff --git a/gdb/target.c b/gdb/target.c index 16b2128e9e..09be1ba266 100644 --- a/gdb/target.c +++ b/gdb/target.c @@ -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); diff --git a/gdb/target.h b/gdb/target.h index c8941a4058..87f0bb9d89 100644 --- a/gdb/target.h +++ b/gdb/target.h @@ -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) diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index 8a6825f3bc..a53f52f331 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2011-11-14 Stan Shebs + + * gdb.trace/ftrace.c: New. + * gdb.trace/ftrace.exp: New. + 2011-11-14 Yao Qi * 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 index 0000000000..a7d008d020 --- /dev/null +++ b/gdb/testsuite/gdb.trace/ftrace.c @@ -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 . */ + +#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 index 0000000000..f45c6da111 --- /dev/null +++ b/gdb/testsuite/gdb.trace/ftrace.exp @@ -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 . + +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