diff options
Diffstat (limited to 'gdb/gdbserver')
-rw-r--r-- | gdb/gdbserver/ChangeLog | 51 | ||||
-rw-r--r-- | gdb/gdbserver/linux-i386-ipa.c | 56 | ||||
-rw-r--r-- | gdb/gdbserver/linux-low.c | 18 | ||||
-rw-r--r-- | gdb/gdbserver/linux-low.h | 10 | ||||
-rw-r--r-- | gdb/gdbserver/linux-x86-low.c | 115 | ||||
-rw-r--r-- | gdb/gdbserver/server.h | 9 | ||||
-rw-r--r-- | gdb/gdbserver/target.h | 38 | ||||
-rw-r--r-- | gdb/gdbserver/tracepoint.c | 218 |
8 files changed, 483 insertions, 32 deletions
diff --git a/gdb/gdbserver/ChangeLog b/gdb/gdbserver/ChangeLog index 2a5aae9..844fb18 100644 --- a/gdb/gdbserver/ChangeLog +++ b/gdb/gdbserver/ChangeLog @@ -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. diff --git a/gdb/gdbserver/linux-i386-ipa.c b/gdb/gdbserver/linux-i386-ipa.c index f0eeb4a..5906464 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 <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 (); } diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c index e8cf374..2b973c6 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 6635bc6..a4e3d5a 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 4cc3feb..d1c760e 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 e53c852..207cd74 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 f60e0a6..8a25476 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 f000f63..b00df05 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 } |