This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
[PATCH 04/16] thread, btrace: add generic branch trace support
- From: markus dot t dot metzger at intel dot com
- To: kettenis at gnu dot org
- Cc: gdb-patches at sourceware dot org, markus dot t dot metzger at gmail dot com, Markus Metzger <markus dot t dot metzger at intel dot com>
- Date: Wed, 23 May 2012 13:22:19 +0200
- Subject: [PATCH 04/16] thread, btrace: add generic branch trace support
- References: <1337772151-20265-1-git-send-email-markus.t.metzger@intel.com>
From: Markus Metzger <markus.t.metzger@intel.com>
Add branch trace information to struct thread_info to hold the branch trace
information for that thread.
Add functions to enable/disable, and get a thread's branch trace, as well as
for iterating over it.
Iteration uses a per-thread iterator stored in the thread_info's branch trace
information.
Iterators are reset implicitly when a thread's branch trace changes.
2012-05-23 Markus Metzger <markus.t.metzger@intel.com>
gdb/
* target.h: Add include
(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
(target_btrace_has_changed): New macro
* target.c (update_current_target): Initialize btrace ops
* btrace.h: New file
* btrace.c: New file
* Makefile.in: Add btrace.c
* gdbthread.h: Add include
(struct thread_info): Add btrace field
* thread.c: Add include
(free_thread): Call disable_btrace
* infcmd.c: Add include
(detach_command): Call disconnect_btrace
---
gdb/Makefile.in | 4 +-
gdb/btrace.c | 254 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
gdb/btrace.h | 89 +++++++++++++++++++
gdb/gdbthread.h | 4 +
gdb/infcmd.c | 2 +
gdb/target.c | 20 +++++
gdb/target.h | 32 +++++++
gdb/thread.c | 3 +
8 files changed, 406 insertions(+), 2 deletions(-)
create mode 100644 gdb/btrace.c
create mode 100644 gdb/btrace.h
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 4368f07..5fa7c12 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -744,7 +744,7 @@ SFILES = ada-exp.y ada-lang.c ada-typeprint.c ada-valprint.c ada-tasks.c \
annotate.c common/signals.c copying.c dfp.c gdb.c inf-child.c \
regset.c sol-thread.c windows-termcap.c \
common/common-utils.c common/xml-utils.c \
- common/ptid.c common/buffer.c gdb-dlfcn.c common/agent.c
+ common/ptid.c common/buffer.c gdb-dlfcn.c common/agent.c btrace.c
LINTFILES = $(SFILES) $(YYFILES) $(CONFIG_SRCS) init.c
@@ -915,7 +915,7 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \
target-descriptions.o target-memory.o xml-tdesc.o xml-builtin.o \
inferior.o osdata.o gdb_usleep.o record.o gcore.o \
jit.o progspace.o skip.o probe.o \
- common-utils.o buffer.o ptid.o gdb-dlfcn.o common-agent.o
+ common-utils.o buffer.o ptid.o gdb-dlfcn.o common-agent.o btrace.o
TSOBS = inflow.o
diff --git a/gdb/btrace.c b/gdb/btrace.c
new file mode 100644
index 0000000..e66a986
--- /dev/null
+++ b/gdb/btrace.c
@@ -0,0 +1,254 @@
+/* Branch trace support for GDB, the GNU debugger.
+
+ Copyright (C) 2012 Free Software Foundation, Inc.
+
+ Contributed by Intel Corp. <markus.t.metzger@intel.com>
+
+ This file is part of GDB.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#include "btrace.h"
+#include "gdbthread.h"
+#include "frame.h"
+#include "exceptions.h"
+
+#include <errno.h>
+
+#if !defined(EALREADY)
+/* Remap EALREADY for systems that do not define it, e.g. mingw. */
+# define EALREADY EBUSY
+#endif
+
+int
+enable_btrace (struct thread_info *tinfo)
+{
+ if (!tinfo)
+ return EINVAL;
+
+ if (tinfo->btrace.target)
+ return EALREADY;
+
+ tinfo->btrace.target = target_enable_btrace (tinfo->ptid);
+ if (!tinfo->btrace.target)
+ return (errno ? errno : ENOSYS);
+
+ return 0;
+}
+
+int
+disable_btrace (struct thread_info *tinfo)
+{
+ struct btrace_thread_info *btinfo;
+ int errcode = 0;
+
+ if (!tinfo)
+ return EINVAL;
+
+ btinfo = &tinfo->btrace;
+
+ if (!btinfo->target)
+ return EALREADY;
+
+ /* When killing the inferior, we may have lost our target before we disable
+ branch tracing. */
+ if (target_supports_btrace ())
+ errcode = target_disable_btrace (btinfo->target);
+
+ if (!errcode)
+ {
+ VEC_free (btrace_block_s, btinfo->btrace);
+ btinfo->btrace = NULL;
+ btinfo->target = NULL;
+ }
+
+ return errcode;
+}
+
+static int
+do_disconnect_btrace (struct thread_info *tinfo, void *ignored)
+{
+ if (tinfo->btrace.target)
+ {
+ volatile struct gdb_exception error;
+
+ TRY_CATCH (error, RETURN_MASK_ERROR)
+ {
+ /* Switching threads makes it easier for targets like kgdb, where we
+ need to switch cpus, as well. */
+ switch_to_thread (tinfo->ptid);
+
+ disable_btrace (tinfo);
+ }
+ }
+
+ return 0;
+}
+
+void
+disconnect_btrace (void)
+{
+ ptid_t ptid = inferior_ptid;
+
+ iterate_over_threads (do_disconnect_btrace, NULL);
+
+ switch_to_thread (ptid);
+}
+
+static struct btrace_thread_info *
+get_btinfo (struct thread_info *tinfo)
+{
+ if (!tinfo)
+ {
+ errno = EINVAL;
+ return NULL;
+ }
+ return &tinfo->btrace;
+}
+
+static int
+update_btrace (struct btrace_thread_info *btinfo)
+{
+ if (!btinfo)
+ return EINVAL;
+
+ if (btinfo->target && target_btrace_has_changed (btinfo->target))
+ {
+ btinfo->btrace = target_read_btrace (btinfo->target);
+ btinfo->iterator = -1;
+
+ if (!btinfo->btrace)
+ return (errno ? errno : ENOSYS);
+
+ /* The first block ends at the current pc. */
+ if (!VEC_empty (btrace_block_s, btinfo->btrace))
+ {
+ struct frame_info *frame = get_current_frame ();
+ if (frame)
+ {
+ struct btrace_block *head =
+ VEC_index (btrace_block_s, btinfo->btrace, 0);
+ if (head && !head->end)
+ head->end = get_frame_pc (frame);
+ }
+ }
+ }
+
+ return 0;
+}
+
+VEC (btrace_block_s) *
+get_btrace (struct thread_info *tinfo)
+{
+ struct btrace_thread_info *btinfo = get_btinfo (tinfo);
+ int errcode;
+
+ if (!btinfo)
+ return NULL;
+
+ errcode = update_btrace (btinfo);
+ if (errcode)
+ {
+ errno = errcode;
+ return NULL;
+ }
+ return btinfo->btrace;
+}
+
+struct btrace_block *
+read_btrace (struct thread_info *tinfo, int index)
+{
+ struct btrace_thread_info *btinfo = get_btinfo (tinfo);
+ int errcode;
+
+ if (!btinfo)
+ return NULL;
+
+ if (index < 0)
+ {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ errcode = update_btrace (btinfo);
+ if (errcode)
+ {
+ errno = errcode;
+ return NULL;
+ }
+
+ btinfo->iterator = index;
+
+ if (btinfo->iterator >= VEC_length (btrace_block_s, btinfo->btrace))
+ {
+ btinfo->iterator = VEC_length (btrace_block_s, btinfo->btrace);
+ return NULL;
+ }
+
+ return VEC_index (btrace_block_s, btinfo->btrace, btinfo->iterator);
+}
+
+struct btrace_block *
+prev_btrace (struct thread_info *tinfo)
+{
+ struct btrace_thread_info *btinfo = get_btinfo (tinfo);
+ int errcode;
+
+ if (!btinfo)
+ return NULL;
+
+ errcode = update_btrace (btinfo);
+ if (errcode)
+ {
+ errno = errcode;
+ return NULL;
+ }
+
+ btinfo->iterator += 1;
+
+ if (btinfo->iterator >= VEC_length (btrace_block_s, btinfo->btrace))
+ {
+ btinfo->iterator = VEC_length (btrace_block_s, btinfo->btrace);
+ return NULL;
+ }
+
+ return VEC_index (btrace_block_s, btinfo->btrace, btinfo->iterator);
+}
+
+struct btrace_block *
+next_btrace (struct thread_info *tinfo)
+{
+ struct btrace_thread_info *btinfo = get_btinfo (tinfo);
+ int errcode;
+
+ if (!btinfo)
+ return NULL;
+
+ errcode = update_btrace (btinfo);
+ if (errcode)
+ {
+ errno = errcode;
+ return NULL;
+ }
+
+ btinfo->iterator -= 1;
+
+ if (btinfo->iterator < 0)
+ {
+ btinfo->iterator = -1;
+ return NULL;
+ }
+
+ return VEC_index (btrace_block_s, btinfo->btrace, btinfo->iterator);
+}
diff --git a/gdb/btrace.h b/gdb/btrace.h
new file mode 100644
index 0000000..97f0f52
--- /dev/null
+++ b/gdb/btrace.h
@@ -0,0 +1,89 @@
+/* Branch trace support for GDB, the GNU debugger.
+
+ Copyright (C) 2012 Free Software Foundation, Inc.
+
+ Contributed by Intel Corp. <markus.t.metzger@intel.com>.
+
+ This file is part of GDB.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef BTRACE_H
+#define BTRACE_H
+
+#include "vec.h"
+#include "defs.h"
+
+struct thread_info;
+
+/* A branch trace block.
+
+ Beware that a block is not a branch. Rather, blocks are connected through
+ branches. */
+struct btrace_block
+{
+ CORE_ADDR begin;
+ CORE_ADDR end;
+};
+
+/* Branch trace is represented as a vector of branch trace blocks starting with
+ the most recent block. */
+typedef struct btrace_block btrace_block_s;
+DEF_VEC_O (btrace_block_s);
+
+/* Target specific branch trace information. */
+struct btrace_target_info;
+
+/* Branch trace information per thread. */
+struct btrace_thread_info
+{
+ /* The target branch trace information for this thread. */
+ struct btrace_target_info *target;
+
+ /* The current branch trace for this thread. */
+ VEC (btrace_block_s) *btrace;
+
+ /* The current iterator position in the above trace vector. */
+ int iterator;
+};
+
+/* Enable branch tracing for a thread.
+ Allocates the branch trace target information.
+ Returns 0 on success and an error value, otherwise. */
+extern int enable_btrace (struct thread_info *);
+
+/* Disable branch tracing for a thread.
+ Deallocates the branch trace target information as well as the current branch
+ trace data.
+ Returns 0 on success and an error value, otherwise. */
+extern int disable_btrace (struct thread_info *);
+
+/* Disconnect branch tracing on detach. */
+extern void disconnect_btrace (void);
+
+/* Return the current branch trace vector for a thread, or NULL if ther is no
+ trace. */
+extern VEC (btrace_block_s) *get_btrace (struct thread_info *);
+
+/* Functions to iterate over a thread's branch trace.
+ There is one global iterator per thread. The iterator is reset implicitly
+ when branch trace for this thread changes.
+ On success, read_btrace sets the iterator to the returned trace entry.
+ Returns the selected block or NULL if there is no trace or the iteratoris
+ out of bounds. */
+extern struct btrace_block *read_btrace (struct thread_info *, int);
+extern struct btrace_block *prev_btrace (struct thread_info *);
+extern struct btrace_block *next_btrace (struct thread_info *);
+
+#endif /* BTRACE_H */
diff --git a/gdb/gdbthread.h b/gdb/gdbthread.h
index fb8de16..055bac0 100644
--- a/gdb/gdbthread.h
+++ b/gdb/gdbthread.h
@@ -28,6 +28,7 @@ struct symtab;
#include "frame.h"
#include "ui-out.h"
#include "inferior.h"
+#include "btrace.h"
/* Frontend view of the thread state. Possible extensions: stepping,
finishing, until(ling),... */
@@ -225,6 +226,9 @@ struct thread_info
/* Function that is called to free PRIVATE. If this is NULL, then
xfree will be called on PRIVATE. */
void (*private_dtor) (struct private_thread_info *);
+
+ /* Branch trace information for this thread. */
+ struct btrace_thread_info btrace;
};
/* Create an empty thread list, or empty the existing one. */
diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index 1f7564a..f9299e9 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -56,6 +56,7 @@
#include "inf-loop.h"
#include "continuations.h"
#include "linespec.h"
+#include "btrace.h"
/* Functions exported for general use, in inferior.h: */
@@ -2680,6 +2681,7 @@ detach_command (char *args, int from_tty)
error (_("The program is not being run."));
disconnect_tracing (from_tty);
+ disconnect_btrace ();
target_detach (args, from_tty);
diff --git a/gdb/target.c b/gdb/target.c
index 6dba936..4d2ea9e 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -701,6 +701,11 @@ update_current_target (void)
INHERIT (to_traceframe_info, t);
INHERIT (to_use_agent, t);
INHERIT (to_can_use_agent, t);
+ INHERIT (to_supports_btrace, t);
+ INHERIT (to_enable_btrace, t);
+ INHERIT (to_disable_btrace, t);
+ INHERIT (to_btrace_has_changed, t);
+ INHERIT (to_read_btrace, t);
INHERIT (to_magic, t);
INHERIT (to_supports_evaluation_of_breakpoint_conditions, t);
/* Do not inherit to_memory_map. */
@@ -939,6 +944,21 @@ update_current_target (void)
(int (*) (void))
return_zero);
de_fault (to_execution_direction, default_execution_direction);
+ de_fault (to_supports_btrace,
+ (int (*) (void))
+ return_zero);
+ de_fault (to_enable_btrace,
+ (struct btrace_target_info * (*) (ptid_t))
+ tcomplain);
+ de_fault (to_disable_btrace,
+ (int (*) (struct btrace_target_info *))
+ tcomplain);
+ de_fault (to_btrace_has_changed,
+ (int (*) (struct btrace_target_info *))
+ tcomplain);
+ de_fault (to_read_btrace,
+ (VEC (btrace_block_s) * (*) (struct btrace_target_info *))
+ tcomplain);
#undef de_fault
diff --git a/gdb/target.h b/gdb/target.h
index 84b462a..86513f7 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -62,6 +62,7 @@ struct expression;
#include "memattr.h"
#include "vec.h"
#include "gdb_signals.h"
+#include "btrace.h"
enum strata
{
@@ -849,6 +850,22 @@ struct target_ops
/* Is the target able to use agent in current state? */
int (*to_can_use_agent) (void);
+ /* Check whether the target supports branch tracing. */
+ int (*to_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 *(*to_enable_btrace) (ptid_t ptid);
+
+ /* Disable branch tracing. Deallocated @tinfo on success. */
+ int (*to_disable_btrace) (struct btrace_target_info *tinfo);
+
+ /* Check whether branch trace changed on the target. */
+ int (*to_btrace_has_changed) (struct btrace_target_info *);
+
+ /* Read branch trace data. */
+ VEC (btrace_block_s) *(*to_read_btrace) (struct btrace_target_info *);
+
int to_magic;
/* Need sub-structure for target machine related rather than comm related?
*/
@@ -1710,6 +1727,21 @@ extern char *target_fileio_read_stralloc (const char *filename);
#define target_can_use_agent() \
(*current_target.to_can_use_agent) ()
+#define target_supports_btrace() \
+ (*current_target.to_supports_btrace) ()
+
+#define target_enable_btrace(ptid) \
+ (*current_target.to_enable_btrace) (ptid)
+
+#define target_disable_btrace(tinfo) \
+ (*current_target.to_disable_btrace) (tinfo)
+
+#define target_btrace_has_changed(tinfo) \
+ (*current_target.to_btrace_has_changed) (tinfo)
+
+#define target_read_btrace(tinfo) \
+ (*current_target.to_read_btrace) (tinfo)
+
/* Command logging facility. */
#define target_log_command(p) \
diff --git a/gdb/thread.c b/gdb/thread.c
index d361dd8..e234fff 100644
--- a/gdb/thread.c
+++ b/gdb/thread.c
@@ -34,6 +34,7 @@
#include "regcache.h"
#include "gdb.h"
#include "gdb_string.h"
+#include "btrace.h"
#include <ctype.h>
#include <sys/types.h>
@@ -132,6 +133,8 @@ free_thread (struct thread_info *tp)
xfree (tp->private);
}
+ disable_btrace (tp);
+
xfree (tp->name);
xfree (tp);
}
--
1.7.1