diff options
Diffstat (limited to 'gdb/gdbserver')
-rw-r--r-- | gdb/gdbserver/ChangeLog | 86 | ||||
-rw-r--r-- | gdb/gdbserver/Makefile.in | 12 | ||||
-rw-r--r-- | gdb/gdbserver/config.in | 3 | ||||
-rwxr-xr-x | gdb/gdbserver/configure | 104 | ||||
-rw-r--r-- | gdb/gdbserver/configure.ac | 48 | ||||
-rw-r--r-- | gdb/gdbserver/linux-amd64-ipa.c | 89 | ||||
-rw-r--r-- | gdb/gdbserver/linux-i386-ipa.c | 77 | ||||
-rw-r--r-- | gdb/gdbserver/mem-break.c | 22 | ||||
-rw-r--r-- | gdb/gdbserver/mem-break.h | 11 | ||||
-rw-r--r-- | gdb/gdbserver/remote-utils.c | 19 | ||||
-rw-r--r-- | gdb/gdbserver/server.c | 48 | ||||
-rw-r--r-- | gdb/gdbserver/server.h | 7 | ||||
-rw-r--r-- | gdb/gdbserver/tracepoint.c | 1258 |
13 files changed, 1771 insertions, 13 deletions
diff --git a/gdb/gdbserver/ChangeLog b/gdb/gdbserver/ChangeLog index 99aacba..531bc6c 100644 --- a/gdb/gdbserver/ChangeLog +++ b/gdb/gdbserver/ChangeLog @@ -1,3 +1,89 @@ +2010-07-01 Pedro Alves <pedro@codesourcery.com> + + Static tracepoints, and integration with UST. + + * configure.ac: Handle --with-ust. substitute ustlibs and ustinc. + * mem-break.c (uninsert_all_breakpoints) + (reinsert_all_breakpoints): New. + * mem-break.h (reinsert_all_breakpoints, uninsert_all_breakpoints): + * tracepoint.c (ust_loaded, helper_thread_id, cmd_buf): New. + (gdb_agent_ust_loaded, helper_thread_id) + (gdb_agent_helper_thread_id): New macros. + (struct ipa_sym_addresses): Add addr_ust_loaded, + addr_helper_thread_id, addr_cmd_buf. + (symbol_list): Add ust_loaded, helper_thread_id, cmd_buf. + (in_process_agent_loaded_ust): New. + (write_e_ust_not_loaded): New. + (maybe_write_ipa_ust_not_loaded): New. + (struct collect_static_trace_data_action): New. + (enum tracepoint_type) <static_tracepoint>: New. + (struct tracepoint) <handle>: Mention static tracepoints. + (struct static_tracepoint_ctx): New. + (CMD_BUF_SIZE): New. + (add_tracepoint_action): Handle static tracepoint actions. + (unprobe_marker_at): New. + (clear_installed_tracepoints): Handle static tracepoints. + (cmd_qtdp): Handle static tracepoints. + (probe_marker_at): New. + (cmd_qtstart): Handle static tracepoints. + (response_tracepoint): Handle static tracepoints. + (cmd_qtfstm, cmd_qtsstm, cmd_qtstmat): New. + (handle_tracepoint_query): Handle qTfSTM, qTsSTM and qTSTMat. + (get_context_regcache): Handle static tracepoints. + (do_action_at_tracepoint): Handle static tracepoint actions. + (traceframe_find_block_type): Handle static trace data blocks. + (traceframe_read_sdata): New. + (download_tracepoints): Download static tracepoint actions. + [HAVE_UST] Include ust/ust.h, dlfcn.h, sys/socket.h, and sys/un.h. + (GDB_PROBE_NAME): New. + (ust_ops): New. + (GET_UST_SYM): New. + (USTF): New. + (dlsym_ust): New. + (ust_marker_to_static_tracepoint): New. + (gdb_probe): New. + (collect_ust_data_at_tracepoint): New. + (gdb_ust_probe): New. + (UNIX_PATH_MAX, SOCK_DIR): New. + (gdb_ust_connect_sync_socket): New. + (resume_thread, stop_thread): New. + (run_inferior_command): New. + (init_named_socket): New. + (gdb_ust_socket_init): New. + (cstr_to_hexstr): New. + (next_st): New. + (first_marker, next_marker): New. + (response_ust_marker): New. + (cmd_qtfstm, cmd_qtsstm): New. + (unprobe_marker_at, probe_marker_at): New. + (cmd_qtstmat, gdb_ust_thread): New. + (gdb_ust_init): New. + (initialize_tracepoint_ftlib): Call gdb_ust_init. + * linux-amd64-ipa.c [HAVE_UST]: Include ust/processor.h + (ST_REGENTRY): New. + (x86_64_st_collect_regmap): New. + (X86_64_NUM_ST_COLLECT_GREGS): New. + (AMD64_RIP_REGNUM): New. + (supply_static_tracepoint_registers): New. + * linux-i386-ipa.c [HAVE_UST]: Include ust/processor.h + (ST_REGENTRY): New. + (i386_st_collect_regmap): New. + (i386_NUM_ST_COLLECT_GREGS): New. + (supply_static_tracepoint_registers): New. + * server.c (handle_query): Handle qXfer:statictrace:read. + <qSupported>: Report support for StaticTracepoints, and + qXfer:statictrace:read features. + * server.h (traceframe_read_sdata) + (supply_static_tracepoint_registers): Declare. + * remote-utils.c (convert_int_to_ascii, hexchars, ishex, tohex) + (unpack_varlen_hex): Include in IPA build. + * Makefile.in (ustlibs, ustinc): New. + (IPA_OBJS): Add remote-utils-ipa.o. + ($(IPA_LIB)): Link -ldl and -lpthread. + (UST_CFLAGS): New. + (IPAGENT_CFLAGS): Add UST_CFLAGS. + * config.in, configure: Regenerate. + 2010-06-20 Ian Lance Taylor <iant@google.com> Pedro Alves <pedro@codesourcery.com> diff --git a/gdb/gdbserver/Makefile.in b/gdb/gdbserver/Makefile.in index 2267c58..76577cf 100644 --- a/gdb/gdbserver/Makefile.in +++ b/gdb/gdbserver/Makefile.in @@ -70,6 +70,10 @@ CC-LD=${CC} INCLUDE_DIR = ${srcdir}/../../include INCLUDE_DEP = $$(INCLUDE_DIR) +# Where is ust? These will be empty if ust was not available. +ustlibs = @ustlibs@ +ustinc = @ustinc@ + # All the includes used for CFLAGS and for lint. # -I. for config files. # -I${srcdir} for our headers. @@ -193,14 +197,14 @@ gdbreplay$(EXEEXT): $(GDBREPLAY_OBS) ${CC-LD} $(INTERNAL_CFLAGS) $(INTERNAL_LDFLAGS) -o gdbreplay$(EXEEXT) $(GDBREPLAY_OBS) \ $(XM_CLIBS) -IPA_OBJS=tracepoint-ipa.o utils-ipa.o regcache-ipa.o ${IPA_DEPFILES} +IPA_OBJS=tracepoint-ipa.o utils-ipa.o regcache-ipa.o remote-utils-ipa.o ${IPA_DEPFILES} IPA_LIB=libinproctrace.so $(IPA_LIB): $(IPA_OBJS) ${ADD_DEPS} ${CDEPS} rm -f $(IPA_LIB) ${CC-LD} -shared -fPIC -Wl,--no-undefined $(INTERNAL_CFLAGS) \ - $(INTERNAL_LDFLAGS) -o $(IPA_LIB) ${IPA_OBJS} + $(INTERNAL_LDFLAGS) -o $(IPA_LIB) ${IPA_OBJS} -ldl -pthread # Put the proper machine-specific files first, so M-. on a machine # specific routine gets the one for the correct machine. @@ -295,9 +299,11 @@ linux_low_h = $(srcdir)/linux-low.h nto_low_h = $(srcdir)/nto-low.h +UST_CFLAGS = $(ustinc) -DCONFIG_UST_GDB_INTEGRATION + # Note, we only build the IPA if -fvisibility=hidden is supported in # the first place. -IPAGENT_CFLAGS = $(CPPFLAGS) $(INTERNAL_CFLAGS) \ +IPAGENT_CFLAGS = $(CPPFLAGS) $(INTERNAL_CFLAGS) $(UST_CFLAGS) \ -fPIC -DGDBSERVER -DIN_PROCESS_AGENT \ -fvisibility=hidden diff --git a/gdb/gdbserver/config.in b/gdb/gdbserver/config.in index 1054966..c547776 100644 --- a/gdb/gdbserver/config.in +++ b/gdb/gdbserver/config.in @@ -154,6 +154,9 @@ /* Define to 1 if you have the <unistd.h> header file. */ #undef HAVE_UNISTD_H +/* Define if UST is available */ +#undef HAVE_UST + /* Checking if errno must be defined */ #undef MUST_DEFINE_ERRNO diff --git a/gdb/gdbserver/configure b/gdb/gdbserver/configure index c48e7a3..3a8033a 100755 --- a/gdb/gdbserver/configure +++ b/gdb/gdbserver/configure @@ -601,6 +601,8 @@ RDYNAMIC REPORT_BUGS_TEXI REPORT_BUGS_TO PKGVERSION +ustinc +ustlibs LIBOBJS INSTALL_DATA INSTALL_SCRIPT @@ -668,6 +670,9 @@ SHELL' ac_subst_files='' ac_user_opts=' enable_option_checking +with_ust +with_ust_include +with_ust_lib with_pkgversion with_bugurl with_libthread_db @@ -1298,6 +1303,11 @@ if test -n "$ac_init_help"; then Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) + --with-ust=PATH Specify prefix directory for the installed UST package + Equivalent to --with-ust-include=PATH/include + plus --with-ust-lib=PATH/lib + --with-ust-include=PATH Specify directory for installed UST include files + --with-ust-lib=PATH Specify the directory for the installed UST library --with-pkgversion=PKG Use PKG in the version string in place of "GDB" --with-bugurl=URL Direct users to URL to report a bug --with-libthread-db=PATH @@ -1747,8 +1757,10 @@ $as_echo "$ac_res" >&6; } ac_fn_c_check_decl () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $2 is declared" >&5 -$as_echo_n "checking whether $2 is declared... " >&6; } + as_decl_name=`echo $2|sed 's/ *(.*//'` + as_decl_use=`echo $2|sed -e 's/(/((/' -e 's/)/) 0&/' -e 's/,/) 0& (/g'` + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $as_decl_name is declared" >&5 +$as_echo_n "checking whether $as_decl_name is declared... " >&6; } if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else @@ -1758,8 +1770,12 @@ $4 int main () { -#ifndef $2 - (void) $2; +#ifndef $as_decl_name +#ifdef __cplusplus + (void) $as_decl_use; +#else + (void) $as_decl_name; +#endif #endif ; @@ -3818,6 +3834,86 @@ done +# Check for UST +ustlibs="" +ustinc="" + + +# Check whether --with-ust was given. +if test "${with_ust+set}" = set; then : + withval=$with_ust; +fi + + +# Check whether --with-ust_include was given. +if test "${with_ust_include+set}" = set; then : + withval=$with_ust_include; +fi + + +# Check whether --with-ust_lib was given. +if test "${with_ust_lib+set}" = set; then : + withval=$with_ust_lib; +fi + + +case $with_ust in + no) + ustlibs= + ustinc= + ;; + "" | yes) + ustlibs=" -lust " + ustinc="" + ;; + *) + ustlibs="-L$with_ust/lib -lust" + ustinc="-I$with_ust/include " + ;; +esac +if test "x$with_ust_include" != x; then + ustinc="-I$with_ust_include " +fi +if test "x$with_ust_lib" != x; then + ustlibs="-L$with_ust_lib -lust" +fi + +if test "x$with_ust" != "xno"; then + saved_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $ustinc" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ust" >&5 +$as_echo_n "checking for ust... " >&6; } + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#define CONFIG_UST_GDB_INTEGRATION +#include <ust/ust.h> + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; +$as_echo "#define HAVE_UST 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; }; ustlibs= ; ustinc= +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CFLAGS="$saved_CFLAGS" +fi + +# Flags needed for UST + + + old_LIBS="$LIBS" LIBS="$LIBS -ldl" for ac_func in dladdr diff --git a/gdb/gdbserver/configure.ac b/gdb/gdbserver/configure.ac index 729a77a..958a158 100644 --- a/gdb/gdbserver/configure.ac +++ b/gdb/gdbserver/configure.ac @@ -45,6 +45,54 @@ AC_CHECK_HEADERS(sgtty.h termio.h termios.h sys/reg.h string.h dnl AC_CHECK_FUNCS(pread pwrite pread64) AC_REPLACE_FUNCS(memmem) +# Check for UST +ustlibs="" +ustinc="" + +AC_ARG_WITH(ust, [ --with-ust=PATH Specify prefix directory for the installed UST package + Equivalent to --with-ust-include=PATH/include + plus --with-ust-lib=PATH/lib]) +AC_ARG_WITH(ust_include, [ --with-ust-include=PATH Specify directory for installed UST include files]) +AC_ARG_WITH(ust_lib, [ --with-ust-lib=PATH Specify the directory for the installed UST library]) + +case $with_ust in + no) + ustlibs= + ustinc= + ;; + "" | yes) + ustlibs=" -lust " + ustinc="" + ;; + *) + ustlibs="-L$with_ust/lib -lust" + ustinc="-I$with_ust/include " + ;; +esac +if test "x$with_ust_include" != x; then + ustinc="-I$with_ust_include " +fi +if test "x$with_ust_lib" != x; then + ustlibs="-L$with_ust_lib -lust" +fi + +if test "x$with_ust" != "xno"; then + saved_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $ustinc" + AC_MSG_CHECKING([for ust]) + AC_TRY_COMPILE([ +#define CONFIG_UST_GDB_INTEGRATION +#include <ust/ust.h> + ],[], + [AC_MSG_RESULT([yes]); AC_DEFINE(HAVE_UST, 1, [Define if UST is available])], + [AC_MSG_RESULT([no]); ustlibs= ; ustinc= ]) + CFLAGS="$saved_CFLAGS" +fi + +# Flags needed for UST +AC_SUBST(ustlibs) +AC_SUBST(ustinc) + dnl dladdr is glibc-specific. It is used by thread-db.c but only for dnl debugging messages. It lives in -ldl which is handled below so we don't dnl use AC_CHECK_LIB (or AC_SEARCH_LIBS) here. Instead we just temporarily diff --git a/gdb/gdbserver/linux-amd64-ipa.c b/gdb/gdbserver/linux-amd64-ipa.c index 9a0bec6..dd094c7 100644 --- a/gdb/gdbserver/linux-amd64-ipa.c +++ b/gdb/gdbserver/linux-amd64-ipa.c @@ -75,6 +75,95 @@ gdb_agent_get_raw_reg (const unsigned char *raw_regs, int regnum) return *(ULONGEST *) (raw_regs + x86_64_ft_collect_regmap[regnum]); } +#ifdef HAVE_UST + +#include <ust/processor.h> + +/* "struct registers" is the UST object type holding the registers at + the time of the static tracepoint marker call. This doesn't + contain RIP, but we know what it must have been (the marker + address). */ + +#define ST_REGENTRY(REG) \ + { \ + offsetof (struct registers, REG), \ + sizeof (((struct registers *) NULL)->REG) \ + } + +static struct +{ + int offset; + int size; +} x86_64_st_collect_regmap[] = + { + ST_REGENTRY(rax), + ST_REGENTRY(rbx), + ST_REGENTRY(rcx), + ST_REGENTRY(rdx), + ST_REGENTRY(rsi), + ST_REGENTRY(rdi), + ST_REGENTRY(rbp), + ST_REGENTRY(rsp), + ST_REGENTRY(r8), + ST_REGENTRY(r9), + ST_REGENTRY(r10), + ST_REGENTRY(r11), + ST_REGENTRY(r12), + ST_REGENTRY(r13), + ST_REGENTRY(r14), + ST_REGENTRY(r15), + { -1, 0 }, + ST_REGENTRY(rflags), + ST_REGENTRY(cs), + ST_REGENTRY(ss), + }; + +#define X86_64_NUM_ST_COLLECT_GREGS \ + (sizeof (x86_64_st_collect_regmap) / sizeof (x86_64_st_collect_regmap[0])) + +/* GDB's RIP register number. */ +#define AMD64_RIP_REGNUM 16 + +void +supply_static_tracepoint_registers (struct regcache *regcache, + const unsigned char *buf, + CORE_ADDR pc) +{ + int i; + unsigned long newpc = pc; + + supply_register (regcache, AMD64_RIP_REGNUM, &newpc); + + for (i = 0; i < X86_64_NUM_ST_COLLECT_GREGS; i++) + if (x86_64_st_collect_regmap[i].offset != -1) + { + switch (x86_64_st_collect_regmap[i].size) + { + case 8: + supply_register (regcache, i, + ((char *) buf) + + x86_64_st_collect_regmap[i].offset); + break; + case 2: + { + unsigned long reg + = * (short *) (((char *) buf) + + x86_64_st_collect_regmap[i].offset); + reg &= 0xffff; + supply_register (regcache, i, ®); + } + break; + default: + internal_error (__FILE__, __LINE__, + "unhandled register size: %d", + x86_64_st_collect_regmap[i].size); + break; + } + } +} + +#endif /* HAVE_UST */ + /* This is only needed because reg-i386-linux-lib.o references it. We may use it proper at some point. */ const char *gdbserver_xmltarget; diff --git a/gdb/gdbserver/linux-i386-ipa.c b/gdb/gdbserver/linux-i386-ipa.c index 7af7924..ea6802f 100644 --- a/gdb/gdbserver/linux-i386-ipa.c +++ b/gdb/gdbserver/linux-i386-ipa.c @@ -110,6 +110,83 @@ gdb_agent_get_raw_reg (unsigned char *raw_regs, int regnum) return *(int *) (raw_regs + i386_ft_collect_regmap[regnum]); } +#ifdef HAVE_UST + +#include <ust/processor.h> + +/* "struct registers" is the UST object type holding the registers at + the time of the static tracepoint marker call. This doesn't + contain EIP, but we know what it must have been (the marker + address). */ + +#define ST_REGENTRY(REG) \ + { \ + offsetof (struct registers, REG), \ + sizeof (((struct registers *) NULL)->REG) \ + } + +static struct +{ + int offset; + int size; +} i386_st_collect_regmap[] = + { + ST_REGENTRY(eax), + ST_REGENTRY(ecx), + ST_REGENTRY(edx), + ST_REGENTRY(ebx), + ST_REGENTRY(esp), + ST_REGENTRY(ebp), + ST_REGENTRY(esi), + ST_REGENTRY(edi), + { -1, 0 }, /* eip */ + ST_REGENTRY(eflags), + ST_REGENTRY(cs), + ST_REGENTRY(ss), + }; + +#define i386_NUM_ST_COLLECT_GREGS \ + (sizeof (i386_st_collect_regmap) / sizeof (i386_st_collect_regmap[0])) + +void +supply_static_tracepoint_registers (struct regcache *regcache, + const unsigned char *buf, + CORE_ADDR pc) +{ + int i; + unsigned int newpc = pc; + + supply_register (regcache, I386_EIP_REGNUM, &newpc); + + for (i = 0; i < i386_NUM_ST_COLLECT_GREGS; i++) + if (i386_st_collect_regmap[i].offset != -1) + { + switch (i386_st_collect_regmap[i].size) + { + case 4: + supply_register (regcache, i, + ((char *) buf) + + i386_st_collect_regmap[i].offset); + break; + case 2: + { + unsigned long reg + = * (short *) (((char *) buf) + + i386_st_collect_regmap[i].offset); + reg &= 0xffff; + supply_register (regcache, i, ®); + } + break; + default: + internal_error ("unhandled register size: %d", + i386_st_collect_regmap[i].size); + } + } +} + +#endif /* HAVE_UST */ + + /* This is only needed because reg-i386-linux-lib.o references it. We may use it proper at some point. */ const char *gdbserver_xmltarget; diff --git a/gdb/gdbserver/mem-break.c b/gdb/gdbserver/mem-break.c index 43b8ead..1b08415 100644 --- a/gdb/gdbserver/mem-break.c +++ b/gdb/gdbserver/mem-break.c @@ -777,6 +777,17 @@ uninsert_breakpoints_at (CORE_ADDR pc) uninsert_raw_breakpoint (bp); } +void +uninsert_all_breakpoints (void) +{ + struct process_info *proc = current_process (); + struct raw_breakpoint *bp; + + for (bp = proc->raw_breakpoints; bp != NULL; bp = bp->next) + if (bp->inserted) + uninsert_raw_breakpoint (bp); +} + static void reinsert_raw_breakpoint (struct raw_breakpoint *bp) { @@ -817,6 +828,17 @@ reinsert_breakpoints_at (CORE_ADDR pc) } void +reinsert_all_breakpoints (void) +{ + struct process_info *proc = current_process (); + struct raw_breakpoint *bp; + + for (bp = proc->raw_breakpoints; bp != NULL; bp = bp->next) + if (!bp->inserted) + reinsert_raw_breakpoint (bp); +} + +void check_breakpoints (CORE_ADDR stop_pc) { struct process_info *proc = current_process (); diff --git a/gdb/gdbserver/mem-break.h b/gdb/gdbserver/mem-break.h index 2669ab9..c28fa89 100644 --- a/gdb/gdbserver/mem-break.h +++ b/gdb/gdbserver/mem-break.h @@ -78,6 +78,17 @@ void reinsert_breakpoints_at (CORE_ADDR where); void uninsert_breakpoints_at (CORE_ADDR where); +/* Reinsert all breakpoints of the current process (and change their + status to inserted). */ + +void reinsert_all_breakpoints (void); + +/* Uninsert all breakpoints of the current process (and change their + status to uninserted). This still leaves the breakpoints in the + table. */ + +void uninsert_all_breakpoints (void); + /* See if any breakpoint claims ownership of STOP_PC. Call the handler for the breakpoint, if found. */ diff --git a/gdb/gdbserver/remote-utils.c b/gdb/gdbserver/remote-utils.c index 3dfd1c9..d64af18 100644 --- a/gdb/gdbserver/remote-utils.c +++ b/gdb/gdbserver/remote-utils.c @@ -74,6 +74,8 @@ typedef int socklen_t; #endif +#ifndef IN_PROCESS_AGENT + #if USE_WIN32API # define INVALID_DESCRIPTOR INVALID_SOCKET #else @@ -371,6 +373,8 @@ fromhex (int a) return 0; } +#endif + static const char hexchars[] = "0123456789abcdef"; static int @@ -394,6 +398,8 @@ ishex (int ch, int *val) return 0; } +#ifndef IN_PROCESS_AGENT + int unhexify (char *bin, const char *hex, int count) { @@ -446,6 +452,8 @@ decode_address_to_semicolon (CORE_ADDR *addrp, const char *start) return end; } +#endif + /* Convert number NIB to a hex digit. */ static int @@ -457,6 +465,8 @@ tohex (int nib) return 'a' + nib - 10; } +#ifndef IN_PROCESS_AGENT + int hexify (char *hex, const char *bin, int count) { @@ -600,6 +610,8 @@ try_rle (char *buf, int remaining, unsigned char *csum, char **p) return n + 1; } +#endif + char * unpack_varlen_hex (char *buff, /* packet to parse */ ULONGEST *result) @@ -617,6 +629,8 @@ unpack_varlen_hex (char *buff, /* packet to parse */ return buff; } +#ifndef IN_PROCESS_AGENT + /* Write a PTID to BUF. Returns BUF+CHARACTERS_WRITTEN. */ char * @@ -1126,6 +1140,8 @@ write_enn (char *buf) buf[3] = '\0'; } +#endif + void convert_int_to_ascii (const unsigned char *from, char *to, int n) { @@ -1142,6 +1158,7 @@ convert_int_to_ascii (const unsigned char *from, char *to, int n) *to++ = 0; } +#ifndef IN_PROCESS_AGENT void convert_ascii_to_int (const char *from, unsigned char *to, int n) @@ -1841,3 +1858,5 @@ buffer_xml_printf (struct buffer *buffer, const char *format, ...) buffer_grow_str (buffer, prev); va_end (ap); } + +#endif diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c index 4e4e5db..e666684 100644 --- a/gdb/gdbserver/server.c +++ b/gdb/gdbserver/server.c @@ -1336,6 +1336,52 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p) return; } + if (strncmp ("qXfer:statictrace:read:", own_buf, + sizeof ("qXfer:statictrace:read:") -1) == 0) + { + unsigned char *data; + CORE_ADDR ofs; + unsigned int len; + char *annex; + ULONGEST nbytes; + + require_running (own_buf); + + if (current_traceframe == -1) + { + write_enn (own_buf); + return; + } + + /* Reject any annex; grab the offset and length. */ + if (decode_xfer_read (own_buf + sizeof ("qXfer:statictrace:read:") -1, + &annex, &ofs, &len) < 0 + || annex[0] != '\0') + { + strcpy (own_buf, "E00"); + return; + } + + /* Read one extra byte, as an indicator of whether there is + more. */ + if (len > PBUFSIZ - 2) + len = PBUFSIZ - 2; + data = malloc (len + 1); + if (!data) + return; + + if (traceframe_read_sdata (current_traceframe, ofs, + data, len + 1, &nbytes)) + write_enn (own_buf); + else if (nbytes > len) + *new_packet_len_p = write_qxfer_response (own_buf, data, len, 1); + else + *new_packet_len_p = write_qxfer_response (own_buf, data, nbytes, 0); + + free (data); + return; + } + /* Protocol features query. */ if (strncmp ("qSupported", own_buf, 10) == 0 && (own_buf[10] == ':' || own_buf[10] == '\0')) @@ -1433,6 +1479,8 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p) strcat (own_buf, ";DisconnectedTracing+"); if (gdb_supports_qRelocInsn && target_supports_fast_tracepoints ()) strcat (own_buf, ";FastTracepoints+"); + strcat (own_buf, ";StaticTracepoints+"); + strcat (own_buf, ";qXfer:statictrace:read+"); } return; diff --git a/gdb/gdbserver/server.h b/gdb/gdbserver/server.h index 8bdd217..51297c7 100644 --- a/gdb/gdbserver/server.h +++ b/gdb/gdbserver/server.h @@ -542,6 +542,10 @@ int fetch_traceframe_registers (int tfnum, struct regcache *regcache, int regnum); +int traceframe_read_sdata (int tfnum, ULONGEST offset, + unsigned char *buf, ULONGEST length, + ULONGEST *nbytes); + /* If a thread is determined to be collecting a fast tracepoint, this structure holds the collect status. */ @@ -569,6 +573,9 @@ int handle_tracepoint_bkpts (struct thread_info *tinfo, CORE_ADDR stop_pc); void initialize_low_tracepoint (void); void supply_fast_tracepoint_registers (struct regcache *regcache, const unsigned char *regs); +void supply_static_tracepoint_registers (struct regcache *regcache, + const unsigned char *regs, + CORE_ADDR pc); #else void stop_tracing (void); #endif diff --git a/gdb/gdbserver/tracepoint.c b/gdb/gdbserver/tracepoint.c index 6557749..c2d7da8 100644 --- a/gdb/gdbserver/tracepoint.c +++ b/gdb/gdbserver/tracepoint.c @@ -135,6 +135,9 @@ trace_vdebug (const char *fmt, ...) # define get_raw_reg gdb_agent_get_raw_reg # define get_trace_state_variable_value gdb_agent_get_trace_state_variable_value # define set_trace_state_variable_value gdb_agent_set_trace_state_variable_value +# define ust_loaded gdb_agent_ust_loaded +# define helper_thread_id gdb_agent_helper_thread_id +# define cmd_buf gdb_agent_cmd_buf #endif #ifndef IN_PROCESS_AGENT @@ -168,6 +171,9 @@ struct ipa_sym_addresses CORE_ADDR addr_get_raw_reg; CORE_ADDR addr_get_trace_state_variable_value; CORE_ADDR addr_set_trace_state_variable_value; + CORE_ADDR addr_ust_loaded; + CORE_ADDR addr_helper_thread_id; + CORE_ADDR addr_cmd_buf; }; #define STRINGIZE_1(STR) #STR @@ -209,6 +215,9 @@ static struct IPA_SYM(get_raw_reg), IPA_SYM(get_trace_state_variable_value), IPA_SYM(set_trace_state_variable_value), + IPA_SYM(ust_loaded), + IPA_SYM(helper_thread_id), + IPA_SYM(cmd_buf), }; struct ipa_sym_addresses ipa_sym_addrs; @@ -223,14 +232,55 @@ in_process_agent_loaded (void) static int read_inferior_integer (CORE_ADDR symaddr, int *val); +/* Returns true if both the in-process agent library and the static + tracepoints libraries are loaded in the inferior. */ + +static int +in_process_agent_loaded_ust (void) +{ + int loaded = 0; + + if (!in_process_agent_loaded ()) + { + warning ("In-process agent not loaded"); + return 0; + } + + if (read_inferior_integer (ipa_sym_addrs.addr_ust_loaded, &loaded)) + { + warning ("Error reading ust_loaded in lib"); + return 0; + } + + return loaded; +} + static void write_e_ipa_not_loaded (char *buffer) { sprintf (buffer, "E.In-process agent library not loaded in process. " - "Dynamic tracepoints unavailable."); + "Fast and static tracepoints unavailable."); } +/* Write an error to BUFFER indicating that UST isn't loaded in the + inferior. */ + +static void +write_e_ust_not_loaded (char *buffer) +{ +#ifdef HAVE_UST + sprintf (buffer, + "E.UST library not loaded in process. " + "Static tracepoints unavailable."); +#else + sprintf (buffer, "E.GDBserver was built without static tracepoints support"); +#endif +} + +/* If the in-process agent library isn't loaded in the inferior, write + an error to BUFFER, and return 1. Otherwise, return 0. */ + static int maybe_write_ipa_not_loaded (char *buffer) { @@ -242,6 +292,26 @@ maybe_write_ipa_not_loaded (char *buffer) return 0; } +/* If the in-process agent library and the ust (static tracepoints) + library aren't loaded in the inferior, write an error to BUFFER, + and return 1. Otherwise, return 0. */ + +static int +maybe_write_ipa_ust_not_loaded (char *buffer) +{ + if (!in_process_agent_loaded ()) + { + write_e_ipa_not_loaded (buffer); + return 1; + } + else if (!in_process_agent_loaded_ust ()) + { + write_e_ust_not_loaded (buffer); + return 1; + } + return 0; +} + /* Cache all future symbols that the tracepoints module might request. We can not request symbols at arbitrary states in the remote protocol, only when the client tells us that new symbols are @@ -347,6 +417,8 @@ static void download_tracepoints (void); static void download_trace_state_variables (void); static void upload_fast_traceframes (void); +static int run_inferior_command (char *cmd); + static int read_inferior_integer (CORE_ADDR symaddr, int *val) { @@ -542,6 +614,12 @@ struct eval_expr_action struct agent_expr *expr; }; +/* An 'L' (collect static trace data) action. */ +struct collect_static_trace_data_action +{ + struct tracepoint_action base; +}; + /* This structure describes a piece of the source-level definition of the tracepoint. The contents are not interpreted by the target, but preserved verbatim for uploading upon reconnection. */ @@ -569,6 +647,10 @@ enum tracepoint_type /* A fast tracepoint implemented with a jump instead of a trap. */ fast_tracepoint, + + /* A static tracepoint, implemented by a program call into a tracing + library. */ + static_tracepoint }; struct tracepoint_hit_ctx; @@ -667,8 +749,8 @@ struct tracepoint char **step_actions_str; /* Handle returned by the breakpoint or tracepoint module when we - inserted the trap or jump. NULL if we haven't inserted it - yet. */ + inserted the trap or jump, or hooked into a static tracepoint. + NULL if we haven't inserted it yet. */ void *handle; #endif @@ -1157,6 +1239,35 @@ struct fast_tracepoint_ctx struct tracepoint *tpoint; }; +/* Static tracepoint specific data to be passed down to + collect_data_at_tracepoint. */ +struct static_tracepoint_ctx +{ + struct tracepoint_hit_ctx base; + + /* The regcache corresponding to the registers state at the time of + the tracepoint hit. Initialized lazily, from REGS. */ + struct regcache regcache; + int regcache_initted; + + /* The buffer space REGCACHE above uses. We use a separate buffer + instead of letting the regcache malloc for both signal safety and + performance reasons; this is allocated on the stack instead. */ + unsigned char *regspace; + + /* The register buffer as passed on by lttng/ust. */ + struct registers *regs; + + /* The "printf" formatter and the args the user passed to the marker + call. We use this to be able to collect "static trace data" + ($_sdata). */ + const char *fmt; + va_list *args; + + /* The GDB tracepoint matching the probed marker that was "hit". */ + struct tracepoint *tpoint; +}; + #else /* Static tracepoint specific data to be passed down to @@ -1228,6 +1339,10 @@ static struct tracepoint *fast_tracepoint_from_ipa_tpoint_address (CORE_ADDR); #define cmpxchg(mem, oldval, newval) \ __sync_val_compare_and_swap (mem, oldval, newval) +/* The size in bytes of the buffer used to talk to the IPA helper + thread. */ +#define CMD_BUF_SIZE 1024 + /* Record that an error occurred during expression evaluation. */ static void @@ -1756,6 +1871,18 @@ add_tracepoint_action (struct tracepoint *tpoint, char *packet) ++act; break; } + case 'L': + { + struct collect_static_trace_data_action *raction; + + raction = xmalloc (sizeof *raction); + raction->base.type = *act; + action = &raction->base; + + trace_debug ("Want to collect static trace data"); + ++act; + break; + } case 'S': trace_debug ("Unexpected step action, ignoring"); ++act; @@ -2147,6 +2274,17 @@ cmd_qtinit (char *packet) write_ok (packet); } +/* Unprobe the UST marker at ADDRESS. */ + +static void +unprobe_marker_at (CORE_ADDR address) +{ + char cmd[CMD_BUF_SIZE]; + + sprintf (cmd, "unprobe_marker_at:%s", paddress (address)); + run_inferior_command (cmd); +} + /* Restore the program to its pre-tracing state. This routine may be called in error situations, so it needs to be careful about only restoring from known-valid bits. */ @@ -2186,6 +2324,19 @@ clear_installed_tracepoints (void) case fast_tracepoint: delete_fast_tracepoint_jump (tpoint->handle); break; + case static_tracepoint: + if (prev_stpoint != NULL + && prev_stpoint->address == tpoint->address) + /* Nothing to do. We already unprobed a tracepoint set at + this marker address (and there can only be one probe + per marker). */ + ; + else + { + unprobe_marker_at (tpoint->address); + prev_stpoint = tpoint; + } + break; } tpoint->handle = NULL; @@ -2258,6 +2409,11 @@ cmd_qtdp (char *own_buf) packet = unpack_varlen_hex (packet, &count); tpoint->orig_size = count; } + else if (*packet == 'S') + { + tpoint->type = static_tracepoint; + ++packet; + } else if (*packet == 'X') { actparm = (char *) packet; @@ -2570,12 +2726,39 @@ sort_tracepoints (void) } } +/* 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 + with the error to reply to GDB, and -1 is also returned. This + allows directly passing IPA errors to GDB. */ + +static int +probe_marker_at (CORE_ADDR address, char *errout) +{ + char cmd[CMD_BUF_SIZE]; + int err; + + sprintf (cmd, "probe_marker_at:%s", paddress (address)); + err = run_inferior_command (cmd); + + if (err == 0) + { + if (*cmd == 'E') + { + strcpy (errout, cmd); + return -1; + } + } + + return err; +} + #define MAX_JUMP_SIZE 20 static void cmd_qtstart (char *packet) { - struct tracepoint *tpoint, *prev_ftpoint; + struct tracepoint *tpoint, *prev_ftpoint, *prev_stpoint; int slow_tracepoint_count, fast_count; CORE_ADDR jump_entry; @@ -2614,6 +2797,9 @@ cmd_qtstart (char *packet) /* No previous fast tpoint yet. */ prev_ftpoint = NULL; + /* No previous static tpoint yet. */ + prev_stpoint = NULL; + *packet = '\0'; /* Install tracepoints. */ @@ -2699,6 +2885,32 @@ cmd_qtstart (char *packet) } } } + else if (tpoint->type == static_tracepoint) + { + if (maybe_write_ipa_ust_not_loaded (packet)) + { + trace_debug ("Requested a static tracepoint, but static " + "tracepoints are not supported."); + break; + } + + /* Can only probe a given marker once. */ + if (prev_stpoint != NULL && prev_stpoint->address == tpoint->address) + { + tpoint->handle = (void *) -1; + } + else + { + if (probe_marker_at (tpoint->address, packet) == 0) + { + tpoint->handle = (void *) -1; + + /* So that we can handle multiple static tracepoints + at the same address easily. */ + prev_stpoint = tpoint; + } + } + } /* Any failure in the inner loop is sufficient cause to give up. */ @@ -3039,6 +3251,8 @@ response_tracepoint (char *packet, struct tracepoint *tpoint) tpoint->pass_count); if (tpoint->type == fast_tracepoint) sprintf (packet + strlen (packet), ":F%x", tpoint->orig_size); + else if (tpoint->type == static_tracepoint) + sprintf (packet + strlen (packet), ":S"); if (tpoint->cond) { @@ -3213,6 +3427,36 @@ cmd_qtsv (char *packet) strcpy (packet, "l"); } +/* Return the first static tracepoint marker, and initialize the state + machine that will iterate through all the static tracepoints + markers. */ + +static void +cmd_qtfstm (char *packet) +{ + if (!maybe_write_ipa_ust_not_loaded (packet)) + run_inferior_command (packet); +} + +/* Return additional static tracepoints markers. */ + +static void +cmd_qtsstm (char *packet) +{ + if (!maybe_write_ipa_ust_not_loaded (packet)) + run_inferior_command (packet); +} + +/* Return the definition of the static tracepoint at a given address. + Result packet is the same as qTsST's. */ + +static void +cmd_qtstmat (char *packet) +{ + if (!maybe_write_ipa_ust_not_loaded (packet)) + run_inferior_command (packet); +} + /* 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. */ @@ -3384,6 +3628,21 @@ handle_tracepoint_query (char *packet) cmd_qtbuffer (packet); return 1; } + else if (strcmp ("qTfSTM", packet) == 0) + { + cmd_qtfstm (packet); + return 1; + } + else if (strcmp ("qTsSTM", packet) == 0) + { + cmd_qtsstm (packet); + return 1; + } + else if (strncmp ("qTSTMat:", packet, strlen ("qTSTMat:")) == 0) + { + cmd_qtstmat (packet); + return 1; + } return 0; } @@ -3697,6 +3956,14 @@ tracepoint_was_hit (struct thread_info *tinfo, CORE_ADDR stop_pc) #endif +#if defined IN_PROCESS_AGENT && defined HAVE_UST +struct ust_marker_data; +static void collect_ust_data_at_tracepoint (struct tracepoint_hit_ctx *ctx, + CORE_ADDR stop_pc, + struct tracepoint *tpoint, + struct traceframe *tframe); +#endif + /* Create a trace frame for the hit of the given tracepoint in the given thread. */ @@ -3803,6 +4070,25 @@ get_context_regcache (struct tracepoint_hit_ctx *ctx) } regcache = &fctx->regcache; } +#ifdef HAVE_UST + if (ctx->type == static_tracepoint) + { + struct static_tracepoint_ctx *sctx = (struct static_tracepoint_ctx *) ctx; + if (!sctx->regcache_initted) + { + sctx->regcache_initted = 1; + init_register_cache (&sctx->regcache, sctx->regspace); + supply_regblock (&sctx->regcache, NULL); + /* Pass down the tracepoint address, because REGS doesn't + include the PC, but we know what it must have been. */ + supply_static_tracepoint_registers (&sctx->regcache, + (const unsigned char *) + sctx->regs, + sctx->tpoint->address); + } + regcache = &sctx->regcache; + } +#endif #else if (ctx->type == trap_tracepoint) { @@ -3910,6 +4196,18 @@ do_action_at_tracepoint (struct tracepoint_hit_ctx *ctx, } } break; + case 'L': + { +#if defined IN_PROCESS_AGENT && defined HAVE_UST + trace_debug ("Want to collect static trace data"); + collect_ust_data_at_tracepoint (ctx, stop_pc, + tpoint, tframe); +#else + trace_debug ("warning: collecting static trace data, " + "but static tracepoints are not supported"); +#endif + } + break; default: trace_debug ("unknown trace action '%c', ignoring", taction->type); break; @@ -4494,6 +4792,11 @@ traceframe_find_block_type (unsigned char *database, unsigned int datasize, /* Skip over the TSV block. */ dataptr += (sizeof (int) + sizeof (LONGEST)); break; + case 'S': + /* Skip over the static trace data block. */ + memcpy (&mlen, dataptr, sizeof (mlen)); + dataptr += (sizeof (mlen) + mlen); + break; default: trace_debug ("traceframe %d has unknown block type 0x%x", tfnum, blocktype); @@ -4684,6 +4987,59 @@ traceframe_read_tsv (int tsvnum, LONGEST *val) return 1; } +/* Read a requested block of static tracepoint data from a trace + frame. */ + +int +traceframe_read_sdata (int tfnum, ULONGEST offset, + unsigned char *buf, ULONGEST length, + ULONGEST *nbytes) +{ + struct traceframe *tframe; + unsigned char *database, *dataptr; + unsigned int datasize; + unsigned short mlen; + + trace_debug ("traceframe_read_sdata"); + + tframe = find_traceframe (tfnum); + + if (!tframe) + { + trace_debug ("traceframe %d not found", tfnum); + return 1; + } + + datasize = tframe->data_size; + database = &tframe->data[0]; + + /* Iterate through a traceframe's blocks, looking for static + tracepoint data. */ + dataptr = traceframe_find_block_type (database, datasize, + tfnum, 'S'); + if (dataptr != NULL) + { + memcpy (&mlen, dataptr, sizeof (mlen)); + dataptr += sizeof (mlen); + if (offset < mlen) + { + if (offset + length > mlen) + length = mlen - offset; + + memcpy (buf, dataptr, length); + *nbytes = length; + } + else + *nbytes = 0; + return 0; + } + + trace_debug ("traceframe %d has no static trace data", tfnum); + + *nbytes = 0; + return 0; +} + /* Return the first fast tracepoint whose jump pad contains PC. */ static struct tracepoint * @@ -5631,7 +5987,8 @@ download_tracepoints (void) { struct tracepoint target_tracepoint; - if (tpoint->type != fast_tracepoint) + if (tpoint->type != fast_tracepoint + && tpoint->type != static_tracepoint) continue; /* Maybe download a compiled condition. */ @@ -5742,6 +6099,14 @@ download_tracepoints (void) expr); break; } + case 'L': + ipa_action = target_malloc + (sizeof (struct collect_static_trace_data_action)); + write_inferior_memory + (ipa_action, + (unsigned char *) action, + sizeof (struct collect_static_trace_data_action)); + break; default: trace_debug ("unknown trace action '%c', ignoring", action->type); @@ -6087,6 +6452,883 @@ upload_fast_traceframes (void) #ifdef IN_PROCESS_AGENT +IP_AGENT_EXPORT int ust_loaded; +IP_AGENT_EXPORT char cmd_buf[CMD_BUF_SIZE]; + +#ifdef HAVE_UST + +/* Static tracepoints. */ + +/* UST puts a "struct tracepoint" in the global namespace, which + conflicts with our tracepoint. Arguably, being a library, it + shouldn't take ownership of such a generic name. We work around it + here. */ +#define tracepoint ust_tracepoint +#include <ust/ust.h> +#undef tracepoint + +extern int serialize_to_text (char *outbuf, int bufsize, + const char *fmt, va_list ap); + +#define GDB_PROBE_NAME "gdb" + +/* We dynamically search for the UST symbols instead of linking them + in. This lets the user decide if the application uses static + tracepoints, instead of always pulling libust.so in. This vector + holds pointers to all functions we care about. */ + +static struct +{ + int (*serialize_to_text) (char *outbuf, int bufsize, + const char *fmt, va_list ap); + + int (*ltt_probe_register) (struct ltt_available_probe *pdata); + int (*ltt_probe_unregister) (struct ltt_available_probe *pdata); + + int (*ltt_marker_connect) (const char *channel, const char *mname, + const char *pname); + int (*ltt_marker_disconnect) (const char *channel, const char *mname, + const char *pname); + + void (*marker_iter_start) (struct marker_iter *iter); + void (*marker_iter_next) (struct marker_iter *iter); + void (*marker_iter_stop) (struct marker_iter *iter); + void (*marker_iter_reset) (struct marker_iter *iter); +} ust_ops; + +#include <dlfcn.h> + +/* Cast through typeof to catch incompatible API changes. Since UST + only builds with gcc, we can freely use gcc extensions here + too. */ +#define GET_UST_SYM(SYM) \ + do \ + { \ + if (ust_ops.SYM == NULL) \ + ust_ops.SYM = (typeof (&SYM)) dlsym (RTLD_DEFAULT, #SYM); \ + if (ust_ops.SYM == NULL) \ + return 0; \ + } while (0) + +#define USTF(SYM) ust_ops.SYM + +/* Get pointers to all libust.so functions we care about. */ + +static int +dlsym_ust (void) +{ + GET_UST_SYM (serialize_to_text); + + GET_UST_SYM (ltt_probe_register); + GET_UST_SYM (ltt_probe_unregister); + GET_UST_SYM (ltt_marker_connect); + GET_UST_SYM (ltt_marker_disconnect); + + GET_UST_SYM (marker_iter_start); + GET_UST_SYM (marker_iter_next); + GET_UST_SYM (marker_iter_stop); + GET_UST_SYM (marker_iter_reset); + + ust_loaded = 1; + return 1; +} + +/* Given an UST marker, return the matching gdb static tracepoint. + The match is done by address. */ + +static struct tracepoint * +ust_marker_to_static_tracepoint (const struct marker *mdata) +{ + struct tracepoint *tpoint; + + for (tpoint = tracepoints; tpoint; tpoint = tpoint->next) + { + if (!tpoint->enabled || tpoint->type != static_tracepoint) + continue; + + if (tpoint->address == (uintptr_t) mdata->location) + return tpoint; + } + + return NULL; +} + +/* The probe function we install on lttng/ust markers. Whenever a + probed ust marker is hit, this function is called. This is similar + to gdb_collect, only for static tracepoints, instead of fast + tracepoints. */ + +static void +gdb_probe (const struct marker *mdata, void *probe_private, + struct registers *regs, void *call_private, + const char *fmt, va_list *args) +{ + struct tracepoint *tpoint; + struct static_tracepoint_ctx ctx; + + /* Don't do anything until the trace run is completely set up. */ + if (!tracing) + { + trace_debug ("gdb_probe: not tracing\n"); + return; + } + + ctx.base.type = static_tracepoint; + ctx.regcache_initted = 0; + ctx.regs = regs; + ctx.fmt = fmt; + ctx.args = args; + + /* Wrap the regblock in a register cache (in the stack, we don't + want to malloc here). */ + ctx.regspace = alloca (register_cache_size ()); + if (ctx.regspace == NULL) + { + trace_debug ("Trace buffer block allocation failed, skipping"); + return; + } + + tpoint = ust_marker_to_static_tracepoint (mdata); + if (tpoint == NULL) + { + trace_debug ("gdb_probe: marker not known: " + "loc:0x%p, ch:\"%s\",n:\"%s\",f:\"%s\"", + mdata->location, mdata->channel, + mdata->name, mdata->format); + return; + } + + ctx.tpoint = tpoint; + + trace_debug ("gdb_probe: collecting marker: " + "loc:0x%p, ch:\"%s\",n:\"%s\",f:\"%s\"", + mdata->location, mdata->channel, + mdata->name, mdata->format); + + /* Test the condition if present, and collect if true. */ + if (tpoint->cond == NULL + || condition_true_at_tracepoint ((struct tracepoint_hit_ctx *) &ctx, + tpoint)) + { + collect_data_at_tracepoint ((struct tracepoint_hit_ctx *) &ctx, + tpoint->address, tpoint); + + if (stopping_tracepoint + || trace_buffer_is_full + || expr_eval_result != expr_eval_no_error) + stop_tracing (); + } + else + { + /* If there was a condition and it evaluated to false, the only + way we would stop tracing is if there was an error during + condition expression evaluation. */ + if (expr_eval_result != expr_eval_no_error) + stop_tracing (); + } +} + +/* Called if the gdb static tracepoint requested collecting "$_sdata", + static tracepoint string data. This is a string passed to the + tracing library by the user, at the time of the tracepoint marker + call. E.g., in the UST marker call: + + trace_mark (ust, bar33, "str %s", "FOOBAZ"); + + the collected data is "str FOOBAZ". +*/ + +static void +collect_ust_data_at_tracepoint (struct tracepoint_hit_ctx *ctx, + CORE_ADDR stop_pc, + struct tracepoint *tpoint, + struct traceframe *tframe) +{ + struct static_tracepoint_ctx *umd = (struct static_tracepoint_ctx *) ctx; + unsigned char *bufspace; + int size; + va_list copy; + unsigned short blocklen; + + if (umd == NULL) + { + trace_debug ("Wanted to collect static trace data, " + "but there's no static trace data"); + return; + } + + va_copy (copy, *umd->args); + size = USTF(serialize_to_text) (NULL, 0, umd->fmt, copy); + va_end (copy); + + trace_debug ("Want to collect ust data"); + + /* 'S' + size + string */ + bufspace = add_traceframe_block (tframe, + 1 + sizeof (blocklen) + size + 1); + if (bufspace == NULL) + { + trace_debug ("Trace buffer block allocation failed, skipping"); + return; + } + + /* Identify a static trace data block. */ + *bufspace = 'S'; + + blocklen = size + 1; + memcpy (bufspace + 1, &blocklen, sizeof (blocklen)); + + va_copy (copy, *umd->args); + USTF(serialize_to_text) ((char *) bufspace + 1 + sizeof (blocklen), + size + 1, umd->fmt, copy); + va_end (copy); + + trace_debug ("Storing static tracepoint data in regblock: %s", + bufspace + 1 + sizeof (blocklen)); +} + +/* The probe to register with lttng/ust. */ +static struct ltt_available_probe gdb_ust_probe = + { + GDB_PROBE_NAME, + NULL, + gdb_probe, + }; + +#endif /* HAVE_UST */ +#endif /* IN_PROCESS_AGENT */ + +#ifdef HAVE_UST + +#include <sys/socket.h> +#include <sys/un.h> + +#ifndef UNIX_PATH_MAX +#define UNIX_PATH_MAX sizeof(((struct sockaddr_un *) NULL)->sun_path) +#endif + +/* Where we put the socked used for synchronization. */ +#define SOCK_DIR P_tmpdir + +#endif /* HAVE_UST */ + +#ifndef IN_PROCESS_AGENT + +#ifdef HAVE_UST + +static int +gdb_ust_connect_sync_socket (int pid) +{ + struct sockaddr_un addr; + int res, fd; + char path[UNIX_PATH_MAX]; + + res = snprintf (path, UNIX_PATH_MAX, "%s/gdb_ust%d", SOCK_DIR, pid); + if (res >= UNIX_PATH_MAX) + { + trace_debug ("string overflow allocating socket name"); + return -1; + } + + res = fd = socket (PF_UNIX, SOCK_STREAM, 0); + if (res == -1) + { + warning ("error opening sync socket: %s\n", strerror (errno)); + return -1; + } + + addr.sun_family = AF_UNIX; + + res = snprintf (addr.sun_path, UNIX_PATH_MAX, "%s", path); + if (res >= UNIX_PATH_MAX) + { + warning ("string overflow allocating socket name\n"); + close (fd); + return -1; + } + + res = connect (fd, (struct sockaddr *) &addr, sizeof (addr)); + if (res == -1) + { + warning ("error connecting sync socket (%s): %s. " + "Make sure the directory exists and that it is writable.", + path, strerror (errno)); + close (fd); + return -1; + } + + return fd; +} + +/* Resume thread PTID. */ + +static void +resume_thread (ptid_t ptid) +{ + struct thread_resume resume_info; + + resume_info.thread = ptid; + resume_info.kind = resume_continue; + resume_info.sig = TARGET_SIGNAL_0; + (*the_target->resume) (&resume_info, 1); +} + +/* Stop thread PTID. */ + +static void +stop_thread (ptid_t ptid) +{ + struct thread_resume resume_info; + + resume_info.thread = ptid; + resume_info.kind = resume_stop; + resume_info.sig = TARGET_SIGNAL_0; + (*the_target->resume) (&resume_info, 1); +} + +/* Ask the in-process agent to run a command. Since we don't want to + have to handle the IPA hitting breakpoints while running the + command, we pause all threads, remove all breakpoints, and then set + the helper thread re-running. We communicate with the helper + thread by means of direct memory xfering, and a socket for + synchronization. */ + +static int +run_inferior_command (char *cmd) +{ + int err = -1; + int fd = -1; + int pid = ptid_get_pid (current_inferior->entry.id); + int tid; + ptid_t ptid = null_ptid; + + trace_debug ("run_inferior_command: running: %s", cmd); + + pause_all (0); + uninsert_all_breakpoints (); + + if (read_inferior_integer (ipa_sym_addrs.addr_helper_thread_id, &tid)) + { + warning ("Error reading helper thread's id in lib"); + goto out; + } + + if (tid == 0) + { + warning ("helper thread not initialized yet"); + goto out; + } + + if (write_inferior_memory (ipa_sym_addrs.addr_cmd_buf, + (unsigned char *) cmd, strlen (cmd) + 1)) + { + warning ("Error writing command"); + goto out; + } + + ptid = ptid_build (pid, tid, 0); + + resume_thread (ptid); + + fd = gdb_ust_connect_sync_socket (pid); + if (fd >= 0) + { + char buf[1] = ""; + int ret; + + trace_debug ("signalling helper thread"); + + do + { + ret = write (fd, buf, 1); + } while (ret == -1 && errno == EINTR); + + trace_debug ("waiting for helper thread's response"); + + do + { + ret = read (fd, buf, 1); + } while (ret == -1 && errno == EINTR); + + close (fd); + + trace_debug ("helper thread's response received"); + } + + out: + + /* Need to read response with the inferior stopped. */ + if (!ptid_equal (ptid, null_ptid)) + { + int was_non_stop = non_stop; + struct target_waitstatus status; + + stop_thread (ptid); + non_stop = 1; + mywait (ptid, &status, 0, 0); + non_stop = was_non_stop; + } + + if (fd >= 0) + { + if (read_inferior_memory (ipa_sym_addrs.addr_cmd_buf, + (unsigned char *) cmd, CMD_BUF_SIZE)) + { + warning ("Error reading command response"); + } + else + { + err = 0; + trace_debug ("run_inferior_command: response: %s", cmd); + } + } + + reinsert_all_breakpoints (); + unpause_all (0); + + return err; +} + +#else /* HAVE_UST */ + +static int +run_inferior_command (char *cmd) +{ + return -1; +} + +#endif /* HAVE_UST */ + +#else /* !IN_PROCESS_AGENT */ + +/* Thread ID of the helper thread. GDBserver reads this to know which + is the help thread. This is an LWP id on Linux. */ +int helper_thread_id; + +#ifdef HAVE_UST + +static int +init_named_socket (const char *name) +{ + int result, fd; + struct sockaddr_un addr; + + result = fd = socket (PF_UNIX, SOCK_STREAM, 0); + if (result == -1) + { + warning ("socket creation failed: %s", strerror (errno)); + return -1; + } + + addr.sun_family = AF_UNIX; + + strncpy (addr.sun_path, name, UNIX_PATH_MAX); + addr.sun_path[UNIX_PATH_MAX - 1] = '\0'; + + result = access (name, F_OK); + if (result == 0) + { + /* File exists. */ + result = unlink (name); + if (result == -1) + { + warning ("unlink failed: %s", strerror (errno)); + close (fd); + return -1; + } + warning ("socket %s already exists; overwriting", name); + } + + result = bind (fd, (struct sockaddr *) &addr, sizeof (addr)); + if (result == -1) + { + warning ("bind failed: %s", strerror (errno)); + close (fd); + return -1; + } + + result = listen (fd, 1); + if (result == -1) + { + warning ("listen: %s", strerror (errno)); + close (fd); + return -1; + } + + return fd; +} + +static int +gdb_ust_socket_init (void) +{ + int result, fd; + char name[UNIX_PATH_MAX]; + + result = snprintf (name, UNIX_PATH_MAX, "%s/gdb_ust%d", + SOCK_DIR, getpid ()); + if (result >= UNIX_PATH_MAX) + { + trace_debug ("string overflow allocating socket name"); + return -1; + } + + fd = init_named_socket (name); + if (fd < 0) + warning ("Error initializing named socket (%s) for communication with the " + "ust helper thread. Check that directory exists and that it " + "is writable.", name); + + return fd; +} + +/* Return an hexstr version of the STR C string, fit for sending to + GDB. */ + +static char * +cstr_to_hexstr (const char *str) +{ + int len = strlen (str); + char *hexstr = xmalloc (len * 2 + 1); + convert_int_to_ascii ((gdb_byte *) str, hexstr, len); + return hexstr; +} + +/* The next marker to be returned on a qTsSTM command. */ +static const struct marker *next_st; + +/* Returns the first known marker. */ + +struct marker * +first_marker (void) +{ + struct marker_iter iter; + + USTF(marker_iter_reset) (&iter); + USTF(marker_iter_start) (&iter); + + return iter.marker; +} + +/* Returns the marker following M. */ + +const struct marker * +next_marker (const struct marker *m) +{ + struct marker_iter iter; + + USTF(marker_iter_reset) (&iter); + USTF(marker_iter_start) (&iter); + + for (; iter.marker != NULL; USTF(marker_iter_next) (&iter)) + { + if (iter.marker == m) + { + USTF(marker_iter_next) (&iter); + return iter.marker; + } + } + + return NULL; +} + +/* Compose packet that is the response to the qTsSTM/qTfSTM/qTSTMat + packets. */ + +static void +response_ust_marker (char *packet, const struct marker *st) +{ + char *strid, *format, *tmp; + + next_st = next_marker (st); + + tmp = xmalloc (strlen (st->channel) + 1 + + strlen (st->name) + 1); + sprintf (tmp, "%s/%s", st->channel, st->name); + + strid = cstr_to_hexstr (tmp); + free (tmp); + + format = cstr_to_hexstr (st->format); + + sprintf (packet, "m%s:%s:%s", + paddress ((uintptr_t) st->location), + strid, + format); + + free (strid); + free (format); +} + +/* Return the first static tracepoint, and initialize the state + machine that will iterate through all the static tracepoints. */ + +static void +cmd_qtfstm (char *packet) +{ + trace_debug ("Returning first trace state variable definition"); + + if (first_marker ()) + response_ust_marker (packet, first_marker ()); + else + strcpy (packet, "l"); +} + +/* Return additional trace state variable definitions. */ + +static void +cmd_qtsstm (char *packet) +{ + trace_debug ("Returning static tracepoint"); + + if (next_st) + response_ust_marker (packet, next_st); + else + strcpy (packet, "l"); +} + +/* Disconnect the GDB probe from a marker at a given address. */ + +static void +unprobe_marker_at (char *packet) +{ + char *p = packet; + ULONGEST address; + struct marker_iter iter; + + p += sizeof ("unprobe_marker_at:") - 1; + + p = unpack_varlen_hex (p, &address); + + USTF(marker_iter_reset) (&iter); + USTF(marker_iter_start) (&iter); + for (; iter.marker != NULL; USTF(marker_iter_next) (&iter)) + if ((uintptr_t ) iter.marker->location == address) + { + int result; + + result = USTF(ltt_marker_disconnect) (iter.marker->channel, + iter.marker->name, + GDB_PROBE_NAME); + if (result < 0) + warning ("could not disable marker %s/%s", + iter.marker->channel, iter.marker->name); + break; + } +} + +/* Connect the GDB probe to a marker at a given address. */ + +static int +probe_marker_at (char *packet) +{ + char *p = packet; + ULONGEST address; + struct marker_iter iter; + struct marker *m; + + p += sizeof ("probe_marker_at:") - 1; + + p = unpack_varlen_hex (p, &address); + + USTF(marker_iter_reset) (&iter); + + for (USTF(marker_iter_start) (&iter), m = iter.marker; + m != NULL; + USTF(marker_iter_next) (&iter), m = iter.marker) + if ((uintptr_t ) m->location == address) + { + int result; + + trace_debug ("found marker for address. " + "ltt_marker_connect (marker = %s/%s)", + m->channel, m->name); + + result = USTF(ltt_marker_connect) (m->channel, m->name, GDB_PROBE_NAME); + if (result && result != -EEXIST) + trace_debug ("ltt_marker_connect (marker = %s/%s, errno = %d)", + m->channel, m->name, -result); + + if (result < 0) + { + sprintf (packet, "E.could not connect marker: channel=%s, name=%s", + m->channel, m->name); + return -1; + } + + strcpy (packet, "OK"); + return 0; + } + + sprintf (packet, "E.no marker found at 0x%s", paddress (address)); + return -1; +} + +static int +cmd_qtstmat (char *packet) +{ + char *p = packet; + ULONGEST address; + struct marker_iter iter; + struct marker *m; + + p += sizeof ("qTSTMat:") - 1; + + p = unpack_varlen_hex (p, &address); + + USTF(marker_iter_reset) (&iter); + + for (USTF(marker_iter_start) (&iter), m = iter.marker; + m != NULL; + USTF(marker_iter_next) (&iter), m = iter.marker) + if ((uintptr_t ) m->location == address) + { + response_ust_marker (packet, m); + return 0; + } + + strcpy (packet, "l"); + return -1; +} + +static void * +gdb_ust_thread (void *arg) +{ + int listen_fd; + + while (1) + { + listen_fd = gdb_ust_socket_init (); + +#ifdef SYS_gettid + if (helper_thread_id == 0) + helper_thread_id = syscall (SYS_gettid); +#endif + + if (listen_fd == -1) + { + warning ("could not create sync socket\n"); + break; + } + + while (1) + { + socklen_t tmp; + struct sockaddr_un sockaddr; + int fd; + char buf[1]; + int ret; + + tmp = sizeof (sockaddr); + + do + { + fd = accept (listen_fd, &sockaddr, &tmp); + } + /* It seems an ERESTARTSYS can escape out of accept. */ + while (fd == -512 || (fd == -1 && errno == EINTR)); + + if (fd < 0) + { + warning ("Accept returned %d, error: %s\n", + fd, strerror (errno)); + break; + } + + do + { + ret = read (fd, buf, 1); + } while (ret == -1 && errno == EINTR); + + if (ret == -1) + { + warning ("reading socket (fd=%d) failed with %s", + fd, strerror (errno)); + close (fd); + break; + } + + if (cmd_buf[0]) + { + if (strcmp ("qTfSTM", cmd_buf) == 0) + { + cmd_qtfstm (cmd_buf); + } + else if (strcmp ("qTsSTM", cmd_buf) == 0) + { + cmd_qtsstm (cmd_buf); + } + else if (strncmp ("unprobe_marker_at:", + cmd_buf, + sizeof ("unprobe_marker_at:") - 1) == 0) + { + unprobe_marker_at (cmd_buf); + } + else if (strncmp ("probe_marker_at:", + cmd_buf, + sizeof ("probe_marker_at:") - 1) == 0) + { + probe_marker_at (cmd_buf); + } + else if (strncmp ("qTSTMat:", + cmd_buf, + sizeof ("qTSTMat:") - 1) == 0) + { + cmd_qtstmat (cmd_buf); + } + else if (strcmp (cmd_buf, "help") == 0) + { + strcpy (cmd_buf, "for help, press F1\n"); + } + else + strcpy (cmd_buf, ""); + } + + write (fd, buf, 1); + close (fd); + } + } + + return NULL; +} + +#include <signal.h> + +static void +gdb_ust_init (void) +{ + int res; + pthread_t thread; + sigset_t new_mask; + sigset_t orig_mask; + + if (!dlsym_ust ()) + return; + + /* We want the helper thread to be as transparent as possible, so + have it inherit an all-signals-blocked mask. */ + + sigfillset (&new_mask); + res = pthread_sigmask (SIG_SETMASK, &new_mask, &orig_mask); + if (res) + fatal ("pthread_sigmask (1) failed: %s", strerror (res)); + + res = pthread_create (&thread, + NULL, + gdb_ust_thread, + NULL); + + res = pthread_sigmask (SIG_SETMASK, &orig_mask, NULL); + if (res) + fatal ("pthread_sigmask (2) failed: %s", strerror (res)); + + while (helper_thread_id == 0) + usleep (1); + + USTF(ltt_probe_register) (&gdb_ust_probe); +} + +#endif /* HAVE_UST */ + #include <sys/mman.h> #include <fcntl.h> @@ -6098,9 +7340,13 @@ static void __attribute__ ((constructor)) initialize_tracepoint_ftlib (void) { initialize_tracepoint (); -} +#ifdef HAVE_UST + gdb_ust_init (); #endif +} + +#endif /* IN_PROCESS_AGENT */ static LONGEST tsv_get_timestamp (void) |