From 9accd112a61b0eaee2724185171761707b4f53e1 Mon Sep 17 00:00:00 2001 From: Markus Metzger Date: Mon, 11 Mar 2013 08:35:11 +0000 Subject: Add the gdb remote target operations for branch tracing. We define the following packets: Qbtrace:bts enable branch tracing for the current thread returns "OK" or "Enn" Qbtrace:off disable branch tracing for the current thread returns "OK" or "Enn" qXfer:btrace:read read the full branch trace data for the current thread gdb/ * target.h (enum target_object): Add TARGET_OBJECT_BTRACE. * remote.c: Include btrace.h. (struct btrace_target_info): New struct. (remote_supports_btrace): New function. (send_Qbtrace): New function. (remote_enable_btrace): New function. (remote_disable_btrace): New function. (remote_teardown_btrace): New function. (remote_read_btrace): New function. (init_remote_ops): Add btrace ops. (enum ): Add btrace packets. (struct protocol_feature remote_protocol_features[]): Add btrace packets. (_initialize_remote): Add packet configuration for branch tracing. gdbserver/ * target.h (struct target_ops): Add btrace ops. (target_supports_btrace): New macro. (target_enable_btrace): New macro. (target_disable_btrace): New macro. (target_read_btrace): New macro. * gdbthread.h (struct thread_info): Add btrace field. * server.c: Include btrace-common.h. (handle_btrace_general_set): New function. (handle_btrace_enable): New function. (handle_btrace_disable): New function. (handle_general_set): Call handle_btrace_general_set. (handle_qxfer_btrace): New function. (struct qxfer qxfer_packets[]): Add btrace entry. * inferiors.c (remove_thread): Disable btrace. * linux-low: Include linux-btrace.h. (linux_low_enable_btrace): New function. (linux_low_read_btrace): New function. (linux_target_ops): Add btrace ops. * configure.srv (i[34567]86-*-linux*): Add linux-btrace.o. Add srv_linux_btrace=yes. (x86_64-*-linux*): Add linux-btrace.o. Add srv_linux_btrace=yes. * configure.ac: Define HAVE_LINUX_BTRACE. * config.in: Regenerated. * configure: Regenerated. --- gdb/gdbserver/ChangeLog | 28 ++++++++ gdb/gdbserver/config.in | 3 + gdb/gdbserver/configure | 6 ++ gdb/gdbserver/configure.ac | 5 ++ gdb/gdbserver/configure.srv | 6 +- gdb/gdbserver/gdbthread.h | 5 ++ gdb/gdbserver/inferiors.c | 3 + gdb/gdbserver/linux-low.c | 57 ++++++++++++++++ gdb/gdbserver/server.c | 161 ++++++++++++++++++++++++++++++++++++++++++++ gdb/gdbserver/target.h | 29 ++++++++ 10 files changed, 301 insertions(+), 2 deletions(-) (limited to 'gdb/gdbserver') diff --git a/gdb/gdbserver/ChangeLog b/gdb/gdbserver/ChangeLog index b26f85b..9a5ae02 100644 --- a/gdb/gdbserver/ChangeLog +++ b/gdb/gdbserver/ChangeLog @@ -1,5 +1,33 @@ 2013-03-11 Markus Metzger + * target.h (struct target_ops): Add btrace ops. + (target_supports_btrace): New macro. + (target_enable_btrace): New macro. + (target_disable_btrace): New macro. + (target_read_btrace): New macro. + * gdbthread.h (struct thread_info): Add btrace field. + * server.c: Include btrace-common.h. + (handle_btrace_general_set): New function. + (handle_btrace_enable): New function. + (handle_btrace_disable): New function. + (handle_general_set): Call handle_btrace_general_set. + (handle_qxfer_btrace): New function. + (struct qxfer qxfer_packets[]): Add btrace entry. + * inferiors.c (remove_thread): Disable btrace. + * linux-low: Include linux-btrace.h. + (linux_low_enable_btrace): New function. + (linux_low_read_btrace): New function. + (linux_target_ops): Add btrace ops. + * configure.srv (i[34567]86-*-linux*): Add linux-btrace.o. + Add srv_linux_btrace=yes. + (x86_64-*-linux*): Add linux-btrace.o. + Add srv_linux_btrace=yes. + * configure.ac: Define HAVE_LINUX_BTRACE. + * config.in: Regenerated. + * configure: Regenerated. + +2013-03-11 Markus Metzger + * server.c (handle_qxfer): Preserve error message if -3 is returned. (qxfer): Document the -3 return value. diff --git a/gdb/gdbserver/config.in b/gdb/gdbserver/config.in index a7c5445..738c322 100644 --- a/gdb/gdbserver/config.in +++ b/gdb/gdbserver/config.in @@ -73,6 +73,9 @@ /* Define to 1 if you have the `dl' library (-ldl). */ #undef HAVE_LIBDL +/* Define if the target supports branch tracing. */ +#undef HAVE_LINUX_BTRACE + /* Define to 1 if you have the header file. */ #undef HAVE_LINUX_ELF_H diff --git a/gdb/gdbserver/configure b/gdb/gdbserver/configure index f37f802..da257bb 100755 --- a/gdb/gdbserver/configure +++ b/gdb/gdbserver/configure @@ -5344,6 +5344,12 @@ $as_echo "#define HAVE_PTRACE_GETFPXREGS 1" >>confdefs.h fi fi +if test "${srv_linux_btrace}" = "yes"; then + +$as_echo "#define HAVE_LINUX_BTRACE 1" >>confdefs.h + +fi + if test "$ac_cv_header_sys_procfs_h" = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for lwpid_t in sys/procfs.h" >&5 $as_echo_n "checking for lwpid_t in sys/procfs.h... " >&6; } diff --git a/gdb/gdbserver/configure.ac b/gdb/gdbserver/configure.ac index 0b30858..f6227d1 100644 --- a/gdb/gdbserver/configure.ac +++ b/gdb/gdbserver/configure.ac @@ -292,6 +292,11 @@ if test "${srv_linux_regsets}" = "yes"; then fi fi +if test "${srv_linux_btrace}" = "yes"; then + AC_DEFINE(HAVE_LINUX_BTRACE, 1, + [Define if the target supports branch tracing.]) +fi + if test "$ac_cv_header_sys_procfs_h" = yes; then BFD_HAVE_SYS_PROCFS_TYPE(lwpid_t) BFD_HAVE_SYS_PROCFS_TYPE(psaddr_t) diff --git a/gdb/gdbserver/configure.srv b/gdb/gdbserver/configure.srv index 0bda563..271a0fe 100644 --- a/gdb/gdbserver/configure.srv +++ b/gdb/gdbserver/configure.srv @@ -112,10 +112,11 @@ case "${target}" in srv_xmlfiles="${srv_xmlfiles} $srv_amd64_linux_xmlfiles" fi srv_tgtobj="linux-low.o linux-osdata.o linux-x86-low.o i386-low.o i387-fp.o linux-procfs.o" - srv_tgtobj="${srv_tgtobj} linux-ptrace.o" + srv_tgtobj="${srv_tgtobj} linux-ptrace.o linux-btrace.o" srv_linux_usrregs=yes srv_linux_regsets=yes srv_linux_thread_db=yes + srv_linux_btrace=yes ipa_obj="${ipa_i386_linux_regobj} linux-i386-ipa.o" ;; i[34567]86-*-lynxos*) srv_regobj="i386.o" @@ -314,11 +315,12 @@ case "${target}" in ;; x86_64-*-linux*) srv_regobj="$srv_amd64_linux_regobj $srv_i386_linux_regobj" srv_tgtobj="linux-low.o linux-osdata.o linux-x86-low.o i386-low.o i387-fp.o linux-procfs.o" - srv_tgtobj="${srv_tgtobj} linux-ptrace.o" + srv_tgtobj="${srv_tgtobj} linux-ptrace.o linux-btrace.o" srv_xmlfiles="$srv_i386_linux_xmlfiles $srv_amd64_linux_xmlfiles" srv_linux_usrregs=yes # This is for i386 progs. srv_linux_regsets=yes srv_linux_thread_db=yes + srv_linux_btrace=yes ipa_obj="${ipa_amd64_linux_regobj} linux-amd64-ipa.o" ;; x86_64-*-mingw*) srv_regobj="$srv_amd64_regobj" diff --git a/gdb/gdbserver/gdbthread.h b/gdb/gdbserver/gdbthread.h index 85951d2..5d4955b 100644 --- a/gdb/gdbserver/gdbthread.h +++ b/gdb/gdbserver/gdbthread.h @@ -21,6 +21,8 @@ #include "server.h" +struct btrace_target_info; + struct thread_info { struct inferior_list_entry entry; @@ -57,6 +59,9 @@ struct thread_info Each item in the list holds the current step of the while-stepping action. */ struct wstep_state *while_stepping; + + /* Branch trace target information for this thread. */ + struct btrace_target_info *btrace; }; extern struct inferior_list all_threads; diff --git a/gdb/gdbserver/inferiors.c b/gdb/gdbserver/inferiors.c index ba3c6cd..6953d0e 100644 --- a/gdb/gdbserver/inferiors.c +++ b/gdb/gdbserver/inferiors.c @@ -161,6 +161,9 @@ free_one_thread (struct inferior_list_entry *inf) void remove_thread (struct thread_info *thread) { + if (thread->btrace != NULL) + target_disable_btrace (thread->btrace); + remove_inferior (&all_threads, (struct inferior_list_entry *) thread); free_one_thread (&thread->entry); } diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c index 5f03628..b5084c9 100644 --- a/gdb/gdbserver/linux-low.c +++ b/gdb/gdbserver/linux-low.c @@ -84,6 +84,10 @@ #endif #endif +#ifdef HAVE_LINUX_BTRACE +# include "linux-btrace.h" +#endif + #ifndef HAVE_ELF32_AUXV_T /* Copied from glibc's elf.h. */ typedef struct @@ -5821,6 +5825,47 @@ linux_qxfer_libraries_svr4 (const char *annex, unsigned char *readbuf, return len; } +#ifdef HAVE_LINUX_BTRACE + +/* Enable branch tracing. */ + +static struct btrace_target_info * +linux_low_enable_btrace (ptid_t ptid) +{ + struct btrace_target_info *tinfo; + + tinfo = linux_enable_btrace (ptid); + if (tinfo != NULL) + tinfo->ptr_bits = register_size (0) * 8; + + return tinfo; +} + +/* Read branch trace data as btrace xml document. */ + +static void +linux_low_read_btrace (struct btrace_target_info *tinfo, struct buffer *buffer, + int type) +{ + VEC (btrace_block_s) *btrace; + struct btrace_block *block; + int i; + + btrace = linux_read_btrace (tinfo, type); + + buffer_grow_str (buffer, "\n"); + buffer_grow_str (buffer, "\n"); + + for (i = 0; VEC_iterate (btrace_block_s, btrace, i, block); i++) + buffer_xml_printf (buffer, "\n", + paddress (block->begin), paddress (block->end)); + + buffer_grow_str (buffer, "\n"); + + VEC_free (btrace_block_s, btrace); +} +#endif /* HAVE_LINUX_BTRACE */ + static struct target_ops linux_target_ops = { linux_create_inferior, linux_attach, @@ -5885,6 +5930,18 @@ static struct target_ops linux_target_ops = { linux_get_min_fast_tracepoint_insn_len, linux_qxfer_libraries_svr4, linux_supports_agent, +#ifdef HAVE_LINUX_BTRACE + linux_supports_btrace, + linux_low_enable_btrace, + linux_disable_btrace, + linux_low_read_btrace, +#else + NULL, + NULL, + NULL, + NULL, + NULL, +#endif }; static void diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c index 9592c69..6bb36d8 100644 --- a/gdb/gdbserver/server.c +++ b/gdb/gdbserver/server.c @@ -28,6 +28,7 @@ #include #endif #include "gdb_wait.h" +#include "btrace-common.h" /* The thread set with an `Hc' packet. `Hc' is deprecated in favor of `vCont'. Note the multi-process extensions made `vCont' a @@ -396,6 +397,88 @@ write_qxfer_response (char *buf, const void *data, int len, int is_more) PBUFSIZ - 2) + 1; } +/* Handle btrace enabling. */ + +static const char * +handle_btrace_enable (struct thread_info *thread) +{ + if (thread->btrace != NULL) + return "E.Btrace already enabled."; + + thread->btrace = target_enable_btrace (thread->entry.id); + if (thread->btrace == NULL) + return "E.Could not enable btrace."; + + return NULL; +} + +/* Handle btrace disabling. */ + +static const char * +handle_btrace_disable (struct thread_info *thread) +{ + + if (thread->btrace == NULL) + return "E.Branch tracing not enabled."; + + if (target_disable_btrace (thread->btrace) != 0) + return "E.Could not disable branch tracing."; + + thread->btrace = NULL; + return NULL; +} + +/* Handle the "Qbtrace" packet. */ + +static int +handle_btrace_general_set (char *own_buf) +{ + struct thread_info *thread; + const char *err; + char *op; + + if (strncmp ("Qbtrace:", own_buf, strlen ("Qbtrace:")) != 0) + return 0; + + op = own_buf + strlen ("Qbtrace:"); + + if (!target_supports_btrace ()) + { + strcpy (own_buf, "E.Target does not support branch tracing."); + return -1; + } + + if (ptid_equal (general_thread, null_ptid) + || ptid_equal (general_thread, minus_one_ptid)) + { + strcpy (own_buf, "E.Must select a single thread."); + return -1; + } + + thread = find_thread_ptid (general_thread); + if (thread == NULL) + { + strcpy (own_buf, "E.No such thread."); + return -1; + } + + err = NULL; + + if (strcmp (op, "bts") == 0) + err = handle_btrace_enable (thread); + else if (strcmp (op, "off") == 0) + err = handle_btrace_disable (thread); + else + err = "E.Bad Qbtrace operation. Use bts or off."; + + if (err != 0) + strcpy (own_buf, err); + else + write_ok (own_buf); + + return 1; +} + /* Handle all of the extended 'Q' packets. */ static void @@ -552,6 +635,9 @@ handle_general_set (char *own_buf) return; } + if (handle_btrace_general_set (own_buf)) + return; + /* Otherwise we didn't know what packet it was. Say we didn't understand it. */ own_buf[0] = 0; @@ -1251,9 +1337,77 @@ handle_qxfer_fdpic (const char *annex, gdb_byte *readbuf, return (*the_target->read_loadmap) (annex, offset, readbuf, len); } +/* Handle qXfer:btrace:read. */ + +static int +handle_qxfer_btrace (const char *annex, + gdb_byte *readbuf, const gdb_byte *writebuf, + ULONGEST offset, LONGEST len) +{ + static struct buffer cache; + struct thread_info *thread; + int type; + + if (the_target->read_btrace == NULL || writebuf != NULL) + return -2; + + if (!target_running ()) + return -1; + + if (ptid_equal (general_thread, null_ptid) + || ptid_equal (general_thread, minus_one_ptid)) + { + strcpy (own_buf, "E.Must select a single thread."); + return -3; + } + + thread = find_thread_ptid (general_thread); + if (thread == NULL) + { + strcpy (own_buf, "E.No such thread."); + return -3; + } + + if (thread->btrace == NULL) + { + strcpy (own_buf, "E.Btrace not enabled."); + return -3; + } + + if (strcmp (annex, "all") == 0) + type = btrace_read_all; + else if (strcmp (annex, "new") == 0) + type = btrace_read_new; + else + { + strcpy (own_buf, "E.Bad annex."); + return -3; + } + + if (offset == 0) + { + buffer_free (&cache); + + target_read_btrace (thread->btrace, &cache, type); + } + else if (offset > cache.used_size) + { + buffer_free (&cache); + return -3; + } + + if (len > cache.used_size - offset) + len = cache.used_size - offset; + + memcpy (readbuf, cache.buffer + offset, len); + + return len; +} + static const struct qxfer qxfer_packets[] = { { "auxv", handle_qxfer_auxv }, + { "btrace", handle_qxfer_btrace }, { "fdpic", handle_qxfer_fdpic}, { "features", handle_qxfer_features }, { "libraries", handle_qxfer_libraries }, @@ -1656,6 +1810,13 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p) if (target_supports_agent ()) strcat (own_buf, ";QAgent+"); + if (target_supports_btrace ()) + { + strcat (own_buf, ";Qbtrace:bts+"); + strcat (own_buf, ";Qbtrace:off+"); + strcat (own_buf, ";qXfer:btrace:read+"); + } + return; } diff --git a/gdb/gdbserver/target.h b/gdb/gdbserver/target.h index cc9a910..f257459 100644 --- a/gdb/gdbserver/target.h +++ b/gdb/gdbserver/target.h @@ -22,6 +22,8 @@ #define TARGET_H struct emit_ops; +struct btrace_target_info; +struct buffer; /* Ways to "resume" a thread. */ @@ -397,6 +399,21 @@ struct target_ops /* Return true if target supports debugging agent. */ int (*supports_agent) (void); + + /* Check whether the target supports branch tracing. */ + int (*supports_btrace) (void); + + /* Enable branch tracing for @ptid and allocate a branch trace target + information struct for reading and for disabling branch trace. */ + struct btrace_target_info *(*enable_btrace) (ptid_t ptid); + + /* Disable branch tracing. */ + int (*disable_btrace) (struct btrace_target_info *tinfo); + + /* Read branch trace data into buffer. We use an int to specify the type + to break a cyclic dependency. */ + void (*read_btrace) (struct btrace_target_info *, struct buffer *, int type); + }; extern struct target_ops *the_target; @@ -520,6 +537,18 @@ int kill_inferior (int); (the_target->supports_agent ? \ (*the_target->supports_agent) () : 0) +#define target_supports_btrace() \ + (the_target->supports_btrace ? (*the_target->supports_btrace) () : 0) + +#define target_enable_btrace(ptid) \ + (*the_target->enable_btrace) (ptid) + +#define target_disable_btrace(tinfo) \ + (*the_target->disable_btrace) (tinfo) + +#define target_read_btrace(tinfo, buffer, type) \ + (*the_target->read_btrace) (tinfo, buffer, type) + /* Start non-stop mode, returns 0 on success, -1 on failure. */ int start_non_stop (int nonstop); -- cgit v1.1