[patch v4 02/13] thread, btrace: add generic branch trace support

markus.t.metzger
2012-11-27T10:49:44Z

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-11-27 Markus Metzger <markus.t.metzger@intel.com>

* target.h: Add include.
(struct target_ops): Add btrace ops.
* target.c (update_current_target): Initialize btrace ops.
(target_supports_btrace): New function.
(target_enable_btrace): New function.
(target_disable_btrace): New function.
(target_read_btrace): New function.
(target_btrace_has_changed): New function.
* 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.
* common/btrace-common.h: New file.

---
gdb/Makefile.in | 4 +-
gdb/btrace.c | 175 ++++++++++++++++++++++++++++++++++++++++++++
gdb/btrace.h | 74 +++++++++++++++++++
gdb/common/btrace-common.h | 56 ++++++++++++++
gdb/gdbthread.h | 4 +
gdb/infcmd.c | 2 +
gdb/target.c | 89 ++++++++++++++++++++++
gdb/target.h | 35 +++++++++
gdb/thread.c | 4 +
9 files changed, 441 insertions(+), 2 deletions(-)
create mode 100644 gdb/btrace.c
create mode 100644 gdb/btrace.h
create mode 100644 gdb/common/btrace-common.h

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 3dd7b85..a8dbe83 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -751,7 +751,7 @@ SFILES = ada-exp.y ada-lang.c ada-typeprint.c ada-valprint.c ada-tasks.c \
regset.c sol-thread.c windows-termcap.c \
common/gdb_vecs.c common/common-utils.c common/xml-utils.c \
common/ptid.c common/buffer.c gdb-dlfcn.c common/agent.c \
- common/format.c
+ common/format.c btrace.c

LINTFILES = $(SFILES) $(YYFILES) $(CONFIG_SRCS) init.c

@@ -925,7 +925,7 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \
inferior.o osdata.o gdb_usleep.o record.o gcore.o \
gdb_vecs.o jit.o progspace.o skip.o probe.o \
common-utils.o buffer.o ptid.o gdb-dlfcn.o common-agent.o \
- format.o registry.o
+ format.o registry.o btrace.o

TSOBS = inflow.o

diff --git a/gdb/btrace.c b/gdb/btrace.c
new file mode 100644
index 0000000..05fc627
--- /dev/null
+++ b/gdb/btrace.c
@@ -0,0 +1,175 @@
+/* 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 "inferior.h"
+#include "target.h"
+
+/* See btrace.h. */
+void
+enable_btrace (struct thread_info *tp)
+{
+ if (!target_supports_btrace ())
+ error (_("Target does not support branch tracing."));
+
+ if (tp->btrace.target)
+ error (_("Branch tracing already enabled for %s."),
+ target_pid_to_str (tp->ptid));
+
+ tp->btrace.target = target_enable_btrace (tp->ptid);
+}
+
+/* See btrace.h. */
+void
+disable_btrace (struct thread_info *tp)
+{
+ struct btrace_thread_info *btp = &tp->btrace;
+ int errcode = 0;
+
+ if (!btp->target)
+ error (_("Branch tracing not enabled for %s."),
+ target_pid_to_str (tp->ptid));
+
+ /* When killing the inferior, we may have lost our target before we disable
+ branch tracing. */
+ if (target_supports_btrace ())
+ target_disable_btrace (btp->target);
+
+ btp->target = NULL;
+ VEC_free (btrace_block_s, btp->btrace);
+}
+
+/* Disable branch tracing for @tp. Ignore errors. */
+static int
+do_disconnect_btrace (struct thread_info *tp, void *ignored)
+{
+ if (tp->btrace.target)
+ {
+ volatile struct gdb_exception error;
+
+ TRY_CATCH (error, RETURN_MASK_ERROR)
+ disable_btrace (tp);
+ }
+
+ return 0;
+}
+
+/* See btrace.h. */
+void
+disconnect_btrace (void)
+{
+ iterate_over_threads (do_disconnect_btrace, NULL);
+}
+
+/* Update @btp's trace data in case of new trace. */
+static void
+update_btrace (struct btrace_thread_info *btp)
+{
+ if (btp->target && target_btrace_has_changed (btp->target))
+ {
+ btp->btrace = target_read_btrace (btp->target);
+ btp->iterator = -1;
+
+ /* The first block ends at the current pc. */
+ if (!VEC_empty (btrace_block_s, btp->btrace))
+ {
+ struct frame_info *frame = get_current_frame ();
+
+ if (frame)
+ {
+ struct btrace_block *head =
+ VEC_index (btrace_block_s, btp->btrace, 0);
+
+ if (head && !head->end)
+ head->end = get_frame_pc (frame);
+ }
+ }
+ }
+}
+
+/* See btrace.h. */
+VEC (btrace_block_s) *
+get_btrace (struct thread_info *tp)
+{
+ update_btrace (&tp->btrace);
+
+ return tp->btrace.btrace;
+}
+
+/* See btrace.h. */
+struct btrace_block *
+read_btrace (struct thread_info *tp, int index)
+{
+ struct btrace_thread_info *btp = &tp->btrace;
+
+ if (index < 0)
+ error (_("Invalid index: %d."), index);
+
+ update_btrace (btp);
+ btp->iterator = index;
+
+ if (btp->iterator >= VEC_length (btrace_block_s, btp->btrace))
+ {
+ btp->iterator = VEC_length (btrace_block_s, btp->btrace);
+ return NULL;
+ }
+
+ return VEC_index (btrace_block_s, btp->btrace, btp->iterator);
+}
+
+/* See btrace.h. */
+struct btrace_block *
+prev_btrace (struct thread_info *tp)
+{
+ struct btrace_thread_info *btp = &tp->btrace;
+
+ update_btrace (btp);
+ btp->iterator += 1;
+
+ if (btp->iterator >= VEC_length (btrace_block_s, btp->btrace))
+ {
+ btp->iterator = VEC_length (btrace_block_s, btp->btrace);
+ return NULL;
+ }
+
+ return VEC_index (btrace_block_s, btp->btrace, btp->iterator);
+}
+
+/* See btrace.h. */
+struct btrace_block *
+next_btrace (struct thread_info *tp)
+{
+ struct btrace_thread_info *btp = &tp->btrace;
+
+ update_btrace (btp);
+ btp->iterator -= 1;
+
+ if (btp->iterator <= -1)
+ {
+ btp->iterator = -1;
+ return NULL;
+ }
+
+ return VEC_index (btrace_block_s, btp->btrace, btp->iterator);
+}
diff --git a/gdb/btrace.h b/gdb/btrace.h
new file mode 100644
index 0000000..e70a46e
--- /dev/null
+++ b/gdb/btrace.h
@@ -0,0 +1,74 @@
+/* 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 "btrace-common.h"
+
+struct thread_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.
+ In additon to valid vector indices, the iterator can be:
+
+ -1 one before the head
+ VEC_length() one after the tail
+
+ Prev and next will remain at those extreme positions. */
+ int iterator;
+};
+
+/* Enable branch tracing for a thread.
+ Allocates the branch trace target information and stores it in @tp. */
+extern void enable_btrace (struct thread_info *tp);
+
+/* Disable branch tracing for a thread.
+ Deallocates the branch trace target information as well as the current branch
+ trace data. */
+extern void 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/common/btrace-common.h b/gdb/common/btrace-common.h
new file mode 100644
index 0000000..4e1cd66
--- /dev/null
+++ b/gdb/common/btrace-common.h
@@ -0,0 +1,56 @@
+/* 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_COMMON_H
+#define BTRACE_COMMON_H
+
+#include "vec.h"
+
+/* For CORE_ADDR. */
+#ifdef GDBSERVER
+# include "server.h"
+#else
+# include "defs.h"
+#endif
+
+/* A branch trace block.
+
+ Beware that a block is not a branch. Rather, blocks are connected through
+ branches. */
+struct btrace_block
+{
+ /* The address of the first instruction in the block. */
+ CORE_ADDR begin;
+ /* The address of the last instruction in the block. */
+ 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;
+
+/* Define functions operating on a vector of branch trace blocks. */
+DEF_VEC_O (btrace_block_s);
+
+/* Target specific branch trace information. */
+struct btrace_target_info;
+
+#endif /* BTRACE_COMMON_H */
diff --git a/gdb/gdbthread.h b/gdb/gdbthread.h
index 0250555..69320f3 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),... */
@@ -227,6 +228,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 7a08e31..36063ed 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: */

@@ -2727,6 +2728,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 5015e51..0e13013 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);
INHERIT (to_can_run_breakpoint_commands, t);
@@ -943,6 +948,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,
+ (void (*) (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

@@ -4149,6 +4169,75 @@ target_ranged_break_num_registers (void)
return -1;
}

+/* See target.h. */
+int
+target_supports_btrace (void)
+{
+ struct target_ops *t;
+
+ for (t = current_target.beneath; t != NULL; t = t->beneath)
+ if (t->to_supports_btrace != NULL)
+ return t->to_supports_btrace ();
+
+ return 0;
+}
+
+/* See target.h. */
+struct btrace_target_info *
+target_enable_btrace (ptid_t ptid)
+{
+ struct target_ops *t;
+
+ for (t = current_target.beneath; t != NULL; t = t->beneath)
+ if (t->to_enable_btrace != NULL)
+ return t->to_enable_btrace (ptid);
+
+ tcomplain ();
+ return NULL;
+}
+
+/* See target.h. */
+void
+target_disable_btrace (struct btrace_target_info *btinfo)
+{
+ struct target_ops *t;
+
+ for (t = current_target.beneath; t != NULL; t = t->beneath)
+ if (t->to_disable_btrace != NULL)
+ return t->to_disable_btrace (btinfo);
+
+ tcomplain ();
+}
+
+/* See target.h. */
+int
+target_btrace_has_changed (struct btrace_target_info *btinfo)
+{
+ struct target_ops *t;
+
+ for (t = current_target.beneath; t != NULL; t = t->beneath)
+ if (t->to_btrace_has_changed != NULL)
+ return t->to_btrace_has_changed (btinfo);
+
+ tcomplain ();
+ return 0;
+}
+
+/* See target.h. */
+VEC (btrace_block_s) *
+target_read_btrace (struct btrace_target_info *btinfo)
+{
+ struct target_ops *t;
+
+ for (t = current_target.beneath; t != NULL; t = t->beneath)
+ if (t->to_read_btrace != NULL)
+ return t->to_read_btrace (btinfo);
+
+ tcomplain ();
+ return NULL;
+}
+
+
static void
debug_to_prepare_to_store (struct regcache *regcache)
{
diff --git a/gdb/target.h b/gdb/target.h
index 7907ee1..4889ce4 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
{
@@ -857,6 +858,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 and deallocate @tinfo. */
+ void (*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?
*/
@@ -1894,4 +1911,22 @@ extern void update_target_permissions (void);
/* Blank target vector entries are initialized to target_ignore. */
void target_ignore (void);

+/* Check whether the target supports branch tracing. */
+extern int target_supports_btrace (void);
+
+/* Enable branch tracing for @ptid.
+ Returns a branch tracing target info object. */
+extern struct btrace_target_info *target_enable_btrace (ptid_t ptid);
+
+/* Disable branch tracing. Deallocates @btinfo. */
+extern void target_disable_btrace (struct btrace_target_info *btinfo);
+
+/* Check whether there is no branch tracing data available. */
+extern int target_btrace_has_changed (struct btrace_target_info *btinfo);
+
+/* Read branch tracing data.
+ Returns a vector of branch trace blocks with the latest entry at index 0. */
+extern VEC (btrace_block_s) *target_read_btrace (struct btrace_target_info *);
+
+
#endif /* !defined (TARGET_H) */
diff --git a/gdb/thread.c b/gdb/thread.c
index 7e8eec5..e6b8c6a 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,9 @@ free_thread (struct thread_info *tp)
xfree (tp->private);
}

+ if (tp->btrace.target)
+ disable_btrace (tp);
+
xfree (tp->name);
xfree (tp);
}
--
1.7.1