Index: NEWS =================================================================== RCS file: /cvs/src/src/gdb/NEWS,v retrieving revision 1.464 diff -u -p -r1.464 NEWS --- NEWS 2 Nov 2011 23:44:19 -0000 1.464 +++ NEWS 11 Nov 2011 02:09:28 -0000 @@ -129,6 +129,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 @@ -160,6 +164,11 @@ QTDisable Dynamically disable a tracepoint in a started trace experiment. +qTMinFTPILen + + Query the minimum length of instruction at which a fast tracepoint may + be placed. + * Dcache size (number of lines) and line-size are now runtime-configurable via "set dcache line" and "set dcache line-size" commands. Index: i386-tdep.c =================================================================== RCS file: /cvs/src/src/gdb/i386-tdep.c,v retrieving revision 1.339 diff -u -p -r1.339 i386-tdep.c --- i386-tdep.c 11 Oct 2011 19:08:58 -0000 1.339 +++ i386-tdep.c 11 Nov 2011 02:09:28 -0000 @@ -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 gd 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 gd /* 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 gd len, jumplen); return 0; } - - if (isize) - *isize = len; - if (msg) - *msg = NULL; - return 1; + else + { + if (msg) + *msg = NULL; + return 1; + } } static int Index: remote.c =================================================================== RCS file: /cvs/src/src/gdb/remote.c,v retrieving revision 1.466 diff -u -p -r1.466 remote.c --- remote.c 2 Nov 2011 23:44:19 -0000 1.466 +++ remote.c 11 Nov 2011 02:09:28 -0000 @@ -6353,7 +6353,7 @@ remote_write_bytes_aux (const char *head 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. */ @@ -9882,11 +9882,10 @@ remote_download_tracepoint (struct break tpaddr, &isize, NULL)) sprintf (buf + strlen (buf), ":F%x", isize); else - /* If it passed validation at definition but fails now, - something is very wrong. */ - internal_error (__FILE__, __LINE__, - _("Fast tracepoint not " - "valid during download")); + warning (_("Target does not support fast tracepoints at " + "%d-byte long instructions, downloading %d as " + "regular tracepoint."), + isize, b->number); } else /* Fast tracepoints are functionally identical to regular @@ -10413,6 +10412,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) { @@ -10501,6 +10526,7 @@ Specify the serial device it is connecte remote_ops.to_upload_trace_state_variables = remote_upload_trace_state_variables; remote_ops.to_get_raw_trace_data = remote_get_raw_trace_data; + remote_ops.to_get_min_fast_tracepoint_insn_len = remote_get_min_fast_tracepoint_insn_len; remote_ops.to_set_disconnected_tracing = remote_set_disconnected_tracing; remote_ops.to_set_circular_trace_buffer = remote_set_circular_trace_buffer; remote_ops.to_core_of_thread = remote_core_of_thread; Index: target.c =================================================================== RCS file: /cvs/src/src/gdb/target.c,v retrieving revision 1.288 diff -u -p -r1.288 target.c --- target.c 10 Nov 2011 20:07:51 -0000 1.288 +++ target.c 11 Nov 2011 02:09:28 -0000 @@ -688,6 +688,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); @@ -889,6 +890,9 @@ update_current_target (void) de_fault (to_get_raw_trace_data, (LONGEST (*) (gdb_byte *, ULONGEST, LONGEST)) tcomplain); + de_fault (to_get_min_fast_tracepoint_insn_len, + (int (*) (void)) + return_minus_one); de_fault (to_set_disconnected_tracing, (void (*) (int)) target_ignore); Index: target.h =================================================================== RCS file: /cvs/src/src/gdb/target.h,v retrieving revision 1.215 diff -u -p -r1.215 target.h --- target.h 2 Nov 2011 23:44:19 -0000 1.215 +++ target.h 11 Nov 2011 02:09:28 -0000 @@ -734,6 +734,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); @@ -1516,6 +1522,9 @@ extern int target_search_memory (CORE_AD #define target_get_raw_trace_data(buf,offset,len) \ (*current_target.to_get_raw_trace_data) ((buf), (offset), (len)) +#define target_get_min_fast_tracepoint_insn_len() \ + (*current_target.to_get_min_fast_tracepoint_insn_len) () + #define target_set_disconnected_tracing(val) \ (*current_target.to_set_disconnected_tracing) (val) Index: gdbserver/linux-i386-ipa.c =================================================================== RCS file: /cvs/src/src/gdb/gdbserver/linux-i386-ipa.c,v retrieving revision 1.5 diff -u -p -r1.5 linux-i386-ipa.c --- gdbserver/linux-i386-ipa.c 16 May 2011 15:59:45 -0000 1.5 +++ gdbserver/linux-i386-ipa.c 11 Nov 2011 02:09:28 -0000 @@ -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 (stru may use it proper at some point. */ const char *gdbserver_xmltarget; +/* Attempt to allocate memory for trampolines in the first 64 KiB of + memory to enable smaller jump patches. */ + +static void +initialize_fast_tracepoint_trampoline_buffer (void) +{ + const CORE_ADDR buffer_end = 64 * 1024; + /* Ensure that the buffer will be at least 1 KiB in size, which is + enough space for over 200 fast tracepoints. */ + const int min_buffer_size = 1024; + char buf[IPA_BUFSIZ]; + CORE_ADDR mmap_min_addr = buffer_end + 1; + ULONGEST buffer_size; + FILE *f = fopen ("/proc/sys/vm/mmap_min_addr", "r"); + + if (!f) + { + snprintf (buf, sizeof (buf), "mmap_min_addr open failed: %s", + strerror (errno)); + set_trampoline_buffer_space (0, 0, buf); + return; + } + + if (fgets (buf, IPA_BUFSIZ, f)) + sscanf (buf, "%llu", &mmap_min_addr); + + fclose (f); + + buffer_size = buffer_end - mmap_min_addr; + + if (buffer_size >= min_buffer_size) + { + if (mmap ((void *) (uintptr_t) mmap_min_addr, buffer_size, + PROT_READ | PROT_EXEC | PROT_WRITE, + MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, + -1, 0) + != MAP_FAILED) + set_trampoline_buffer_space (mmap_min_addr, buffer_end, NULL); + else + { + snprintf (buf, IPA_BUFSIZ, "low-64K-buffer mmap() failed: %s", + strerror (errno)); + set_trampoline_buffer_space (0, 0, buf); + } + } + else + { + snprintf (buf, IPA_BUFSIZ, "mmap_min_addr is %d, must be %d or less", + (int) mmap_min_addr, (int) buffer_end - min_buffer_size); + set_trampoline_buffer_space (0, 0, buf); + } +} + void initialize_low_tracepoint (void) { init_registers_i386_linux (); + initialize_fast_tracepoint_trampoline_buffer (); } Index: gdbserver/linux-low.c =================================================================== RCS file: /cvs/src/src/gdb/gdbserver/linux-low.c,v retrieving revision 1.178 diff -u -p -r1.178 linux-low.c --- gdbserver/linux-low.c 7 Oct 2011 12:06:48 -0000 1.178 +++ gdbserver/linux-low.c 11 Nov 2011 02:09:28 -0000 @@ -4933,15 +4933,20 @@ linux_install_fast_tracepoint_jump_pad ( 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_op linux_install_fast_tracepoint_jump_pad, linux_emit_ops, linux_supports_disable_randomization, + linux_get_min_fast_tracepoint_insn_len, }; static void Index: gdbserver/linux-low.h =================================================================== RCS file: /cvs/src/src/gdb/gdbserver/linux-low.h,v retrieving revision 1.47 diff -u -p -r1.47 linux-low.h --- gdbserver/linux-low.h 1 Jan 2011 15:33:24 -0000 1.47 +++ gdbserver/linux-low.h 11 Nov 2011 02:09:28 -0000 @@ -132,14 +132,22 @@ struct linux_target_ops CORE_ADDR lockaddr, ULONGEST orig_size, CORE_ADDR *jump_entry, + CORE_ADDR *trampoline, + ULONGEST *trampoline_size, unsigned char *jjump_pad_insn, ULONGEST *jjump_pad_insn_size, CORE_ADDR *adjusted_insn_addr, - CORE_ADDR *adjusted_insn_addr_end); + CORE_ADDR *adjusted_insn_addr_end, + char *err); /* Return the bytecode operations vector for the current inferior. Returns NULL if bytecode compilation is not supported. */ struct emit_ops *(*emit_ops) (void); + + /* Return the minimum length of an instruction that can be safely overwritten + for use as a fast tracepoint. */ + int (*get_min_fast_tracepoint_insn_len) (void); + }; extern struct linux_target_ops the_low_target; Index: gdbserver/linux-x86-low.c =================================================================== RCS file: /cvs/src/src/gdb/gdbserver/linux-x86-low.c,v retrieving revision 1.30 diff -u -p -r1.30 linux-x86-low.c --- gdbserver/linux-x86-low.c 15 Sep 2011 22:54:12 -0000 1.30 +++ gdbserver/linux-x86-low.c 11 Nov 2011 02:09:28 -0000 @@ -42,6 +42,7 @@ void init_registers_amd64_avx_linux (voi 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 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 (C 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 (C 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 (C 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 (CO CORE_ADDR lockaddr, ULONGEST orig_size, CORE_ADDR *jump_entry, + CORE_ADDR *trampoline, + ULONGEST *trampoline_size, unsigned char *jjump_pad_insn, ULONGEST *jjump_pad_insn_size, CORE_ADDR *adjusted_insn_addr, - CORE_ADDR *adjusted_insn_addr_end) + CORE_ADDR *adjusted_insn_addr_end, + char *err) { #ifdef __x86_64__ if (register_size (0) == 8) return amd64_install_fast_tracepoint_jump_pad (tpoint, tpaddr, collector, lockaddr, orig_size, jump_entry, + trampoline, trampoline_size, jjump_pad_insn, jjump_pad_insn_size, adjusted_insn_addr, - adjusted_insn_addr_end); + adjusted_insn_addr_end, + err); #endif return i386_install_fast_tracepoint_jump_pad (tpoint, tpaddr, collector, lockaddr, orig_size, jump_entry, + trampoline, trampoline_size, jjump_pad_insn, jjump_pad_insn_size, adjusted_insn_addr, - adjusted_insn_addr_end); + adjusted_insn_addr_end, + err); +} + +/* Return the minimum instruction length for fast tracepoints on x86/x86-64 + architectures. */ + +static int +x86_get_min_fast_tracepoint_insn_len (void) +{ + static int warned_about_fast_tracepoints = 0; + +#ifdef __x86_64__ + /* On x86-64, 5-byte jump instructions with a 4-byte offset are always + used for fast tracepoints. */ + if (register_size (0) == 8) + return 5; +#endif + + if (in_process_agent_loaded ()) + { + char errbuf[IPA_BUFSIZ]; + + errbuf[0] = '\0'; + + /* On x86, if trampolines are available, then 4-byte jump instructions + with a 2-byte offset may be used, otherwise 5-byte jump instructions + with a 4-byte offset are used instead. */ + if (have_fast_tracepoint_trampoline_buffer (errbuf)) + return 4; + else + { + /* GDB has no channel to explain to user why a shorter fast + tracepoint is not possible, but at least make GDBserver + mention that something has gone awry. */ + if (!warned_about_fast_tracepoints) + { + warning ("4-byte fast tracepoints not available; %s\n", errbuf); + warned_about_fast_tracepoints = 1; + } + return 5; + } + } + else + { + /* Indicate that the minimum length is currently unknown since the IPA + has not loaded yet. */ + return 0; + } } static void @@ -2873,5 +2963,6 @@ struct linux_target_ops the_low_target = x86_supports_tracepoints, x86_get_thread_area, x86_install_fast_tracepoint_jump_pad, - x86_emit_ops + x86_emit_ops, + x86_get_min_fast_tracepoint_insn_len, }; Index: gdbserver/server.h =================================================================== RCS file: /cvs/src/src/gdb/gdbserver/server.h,v retrieving revision 1.87 diff -u -p -r1.87 server.h --- gdbserver/server.h 7 Oct 2011 12:06:48 -0000 1.87 +++ gdbserver/server.h 11 Nov 2011 02:09:28 -0000 @@ -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 (s void supply_static_tracepoint_registers (struct regcache *regcache, const unsigned char *regs, CORE_ADDR pc); +void set_trampoline_buffer_space (CORE_ADDR begin, CORE_ADDR end, + char *errmsg); #else void stop_tracing (void); + +int claim_trampoline_space (ULONGEST used, CORE_ADDR *trampoline); +int have_fast_tracepoint_trampoline_buffer (char *msgbuf); #endif /* Bytecode compilation function vector. */ Index: gdbserver/target.h =================================================================== RCS file: /cvs/src/src/gdb/gdbserver/target.h,v retrieving revision 1.58 diff -u -p -r1.58 target.h --- gdbserver/target.h 2 Nov 2011 04:20:47 -0000 1.58 +++ gdbserver/target.h 11 Nov 2011 02:09:28 -0000 @@ -359,20 +359,26 @@ struct target_ops pad lock object. ORIG_SIZE is the size in bytes of the instruction at TPADDR. JUMP_ENTRY points to the address of the jump pad entry, and on return holds the address past the end of - the created jump pad. JJUMP_PAD_INSN is a buffer containing a - copy of the instruction at TPADDR. ADJUST_INSN_ADDR and - ADJUST_INSN_ADDR_END are output parameters that return the - address range where the instruction at TPADDR was relocated - to. */ + the created jump pad. If a trampoline is created by the function, + then TRAMPOLINE and TRAMPOLINE_SIZE return the address and size of + the trampoline, else they remain unchanged. JJUMP_PAD_INSN is a + buffer containing a copy of the instruction at TPADDR. + ADJUST_INSN_ADDR and ADJUST_INSN_ADDR_END are output parameters that + return the address range where the instruction at TPADDR was relocated + to. If an error occurs, the ERR may be used to pass on an error + message. */ int (*install_fast_tracepoint_jump_pad) (CORE_ADDR tpoint, CORE_ADDR tpaddr, CORE_ADDR collector, CORE_ADDR lockaddr, ULONGEST orig_size, CORE_ADDR *jump_entry, + CORE_ADDR *trampoline, + ULONGEST *trampoline_size, unsigned char *jjump_pad_insn, ULONGEST *jjump_pad_insn_size, CORE_ADDR *adjusted_insn_addr, - CORE_ADDR *adjusted_insn_addr_end); + CORE_ADDR *adjusted_insn_addr_end, + char *err); /* Return the bytecode operations vector for the current inferior. Returns NULL if bytecode compilation is not supported. */ @@ -380,6 +386,10 @@ struct target_ops /* Returns true if the target supports disabling randomization. */ int (*supports_disable_randomization) (void); + + /* Return the minimum length of an instruction that can be safely overwritten + for use as a fast tracepoint. */ + int (*get_min_fast_tracepoint_insn_len) (void); }; extern struct target_ops *the_target; @@ -437,6 +447,10 @@ void set_target_ops (struct target_ops * #define target_supports_fast_tracepoints() \ (the_target->install_fast_tracepoint_jump_pad != NULL) +#define target_get_min_fast_tracepoint_insn_len() \ + (the_target->get_min_fast_tracepoint_insn_len \ + ? (*the_target->get_min_fast_tracepoint_insn_len) () : 0) + #define thread_stopped(thread) \ (*the_target->thread_stopped) (thread) @@ -471,17 +485,23 @@ void set_target_ops (struct target_ops * #define install_fast_tracepoint_jump_pad(tpoint, tpaddr, \ collector, lockaddr, \ orig_size, \ - jump_entry, jjump_pad_insn, \ + jump_entry, \ + trampoline, trampoline_size, \ + jjump_pad_insn, \ jjump_pad_insn_size, \ adjusted_insn_addr, \ - adjusted_insn_addr_end) \ + adjusted_insn_addr_end, \ + err) \ (*the_target->install_fast_tracepoint_jump_pad) (tpoint, tpaddr, \ collector,lockaddr, \ orig_size, jump_entry, \ + trampoline, \ + trampoline_size, \ jjump_pad_insn, \ jjump_pad_insn_size, \ adjusted_insn_addr, \ - adjusted_insn_addr_end) + adjusted_insn_addr_end, \ + err) #define target_emit_ops() \ (the_target->emit_ops ? (*the_target->emit_ops) () : NULL) Index: gdbserver/tracepoint.c =================================================================== RCS file: /cvs/src/src/gdb/gdbserver/tracepoint.c,v retrieving revision 1.30 diff -u -p -r1.30 tracepoint.c --- gdbserver/tracepoint.c 5 Nov 2011 13:16:22 -0000 1.30 +++ gdbserver/tracepoint.c 11 Nov 2011 02:09:29 -0000 @@ -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; @@ -2657,6 +2672,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; +} + /* Sort tracepoints by PC, using a bubble sort. */ static void @@ -2814,18 +2908,30 @@ cmd_qtstart (char *packet) fjump_size); tpoint->jump_pad = prev_ftpoint->jump_pad; tpoint->jump_pad_end = prev_ftpoint->jump_pad_end; + tpoint->trampoline = prev_ftpoint->trampoline; + tpoint->trampoline_end = prev_ftpoint->trampoline_end; tpoint->adjusted_insn_addr = prev_ftpoint->adjusted_insn_addr; tpoint->adjusted_insn_addr_end = prev_ftpoint->adjusted_insn_addr_end; } else { - CORE_ADDR jentry; + CORE_ADDR jentry, trampoline; + ULONGEST trampoline_size; int err = 0; + 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."); + break; + } + prev_ftpoint = NULL; jentry = jump_entry = get_jump_space_head (); + trampoline = 0; + trampoline_size = 0; /* Install the jump pad. */ err = install_fast_tracepoint_jump_pad @@ -2835,9 +2941,11 @@ cmd_qtstart (char *packet) ipa_sym_addrs.addr_collecting, tpoint->orig_size, &jentry, + &trampoline, &trampoline_size, fjump, &fjump_size, &tpoint->adjusted_insn_addr, - &tpoint->adjusted_insn_addr_end); + &tpoint->adjusted_insn_addr_end, + packet); /* Wire it in. */ if (!err) @@ -2848,6 +2956,8 @@ cmd_qtstart (char *packet) { 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); @@ -3431,6 +3541,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. */ @@ -3627,6 +3746,11 @@ handle_tracepoint_query (char *packet) cmd_qtstmat (packet); return 1; } + else if (strcmp ("qTMinFTPILen", packet) == 0) + { + cmd_qtminftpilen (packet); + return 1; + } return 0; } @@ -5243,6 +5367,23 @@ fast_tracepoint_from_jump_pad_address (C 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. */ @@ -5305,6 +5446,8 @@ fast_tracepoint_collecting (CORE_ADDR th { 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; @@ -5343,6 +5486,13 @@ fast_tracepoint_collecting (CORE_ADDR th &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) { @@ -5371,6 +5521,30 @@ fast_tracepoint_collecting (CORE_ADDR th && 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; @@ -7698,6 +7872,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) @@ -7759,6 +7951,16 @@ initialize_tracepoint: mprotect(%p, %d, gdb_jump_pad_buffer, pagesize * 20, strerror (errno)); } + gdb_trampoline_buffer = gdb_trampoline_buffer_end = 0; + + /* It's not a fatal error for something to go wrong with trampoline + buffer setup, but it can be mysterious, so create a channel to + report back on what went wrong, using a fixed size since we may + not be able to allocate space later when the problem occurs. */ + gdb_trampoline_buffer_error = xmalloc (IPA_BUFSIZ); + + strcpy (gdb_trampoline_buffer_error, "No errors reported"); + initialize_low_tracepoint (); #endif } Index: doc/gdb.texinfo =================================================================== RCS file: /cvs/src/src/gdb/doc/gdb.texinfo,v retrieving revision 1.890 diff -u -p -r1.890 gdb.texinfo --- doc/gdb.texinfo 8 Nov 2011 21:34:18 -0000 1.890 +++ doc/gdb.texinfo 11 Nov 2011 02:09:29 -0000 @@ -10373,6 +10373,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 @@ -34902,6 +34919,8 @@ encoded). @value{GDBN} will continue to @itemx qTfP @itemx qTfV @itemx QTFrame +@itemx qTMinFTPILen + @xref{Tracepoint Packets}. @item qThreadExtraInfo,@var{thread-id} @@ -35425,6 +35444,29 @@ 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 may be placed. For instance, on the 32-bit x86 +architecture, it is possible to use a 4-byte jump, but it depends on +the target system being able to create trampolines in the first 64K of +memory, which might or might not be possible for that system. So the +reply to this packet will be 4 if it is able to arrange for that. + +Replies: + +@table @samp +@item 0 +The minimum instruction length is currently unknown. +@item @var{length} +The minimum instruction length is @var{length}, where @var{length} is greater +or equal to 1. @var{length} is a hexadecimal number. A reply of 1 means +that a fast tracepoint may be placed on any instruction regardless of size. +@item E +An error has occurred. +@item +An empty reply indicates that the request is not supported by the stub. +@end table + @item QTStart Begin the tracepoint experiment. Begin collecting data from tracepoint hits in the trace frame buffer. This packet supports the Index: testsuite/gdb.trace/ftrace.c =================================================================== RCS file: testsuite/gdb.trace/ftrace.c diff -N testsuite/gdb.trace/ftrace.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ testsuite/gdb.trace/ftrace.c 11 Nov 2011 02:09:29 -0000 @@ -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; +} Index: testsuite/gdb.trace/ftrace.exp =================================================================== RCS file: testsuite/gdb.trace/ftrace.exp diff -N testsuite/gdb.trace/ftrace.exp --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ testsuite/gdb.trace/ftrace.exp 11 Nov 2011 02:09:29 -0000 @@ -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