[patch v9 01/23] thread, btrace: add generic branch trace support

Markus Metzger
2013-03-04T17:07:57Z

Add branch trace information to struct thread_info to hold the branch trace
information for that thread.

Add functions to enable, disable, clear, and fetch a thread's branch trace.

2013-03-04 Markus Metzger <markus.t.metzger@intel.com>

* target.h: Include btrace.h.
(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: Include btrace.h.
(struct thread_info): Add btrace field.
* thread.c: Include btrace.h.
(clear_thread_inferior_resources): Call btrace_disable.
* infcmd.c: Include btrace.h.
(detach_command): Call btrace_disconnect.
* common/btrace-common.h: New file.

---
gdb/Makefile.in | 4 +-
gdb/btrace.c | 467 ++++++++++++++++++++++++++++++++++++++++++++
gdb/btrace.h | 135 +++++++++++++
gdb/common/btrace-common.h | 62 ++++++
gdb/gdbthread.h | 4 +
gdb/infcmd.c | 2 +
gdb/target.c | 69 +++++++
gdb/target.h | 35 ++++
gdb/thread.c | 3 +
9 files changed, 779 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 ed30db5..1cf8134 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -759,7 +759,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

@@ -928,7 +928,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..b6cd1c8
--- /dev/null
+++ b/gdb/btrace.c
@@ -0,0 +1,467 @@
+/* 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 "exceptions.h"
+#include "inferior.h"
+#include "target.h"
+#include "record.h"
+#include "symtab.h"
+#include "disasm.h"
+#include "source.h"
+#include "filenames.h"
+
+/* Print a record debug message. Use do ... while (0) to avoid ambiguities
+ when used in if statements. */
+
+#define DEBUG(msg, args...) \
+ do \
+ { \
+ if (record_debug != 0) \
+ fprintf_unfiltered (gdb_stdlog, \
+ "[btrace] " msg "\n", ##args); \
+ } \
+ while (0)
+
+#define DEBUG_FTRACE(msg, args...) DEBUG ("[ftrace] " msg, ##args)
+
+/* Initialize the instruction iterator. */
+
+static void
+btrace_init_insn_iterator (struct btrace_thread_info *btinfo)
+{
+ DEBUG ("init insn iterator");
+
+ btinfo->insn_iterator.begin = 1;
+ btinfo->insn_iterator.end = 0;
+}
+
+/* Initialize the function iterator. */
+
+static void
+btrace_init_func_iterator (struct btrace_thread_info *btinfo)
+{
+ DEBUG ("init func iterator");
+
+ btinfo->func_iterator.begin = 1;
+ btinfo->func_iterator.end = 0;
+}
+
+/* Compute the instruction trace from the block trace. */
+
+static VEC (btrace_inst_s) *
+compute_itrace (VEC (btrace_block_s) *btrace)
+{
+ VEC (btrace_inst_s) *itrace;
+ struct gdbarch *gdbarch;
+ unsigned int b;
+
+ DEBUG ("compute itrace");
+
+ itrace = NULL;
+ gdbarch = target_gdbarch ();
+ b = VEC_length (btrace_block_s, btrace);
+
+ while (b-- != 0)
+ {
+ btrace_block_s *block;
+ CORE_ADDR pc;
+
+ block = VEC_index (btrace_block_s, btrace, b);
+ pc = block->begin;
+
+ /* Add instructions for this block. */
+ for (;;)
+ {
+ btrace_inst_s *inst;
+ int size;
+
+ /* We should hit the end of the block. Warn if we went too far. */
+ if (block->end < pc)
+ {
+ warning (_("Recorded trace may be corrupted."));
+ break;
+ }
+
+ inst = VEC_safe_push (btrace_inst_s, itrace, NULL);
+ inst->pc = pc;
+
+ /* We're done once we pushed the instruction at the end. */
+ if (block->end == pc)
+ break;
+
+ size = gdb_insn_length (gdbarch, pc);
+
+ /* Make sure we terminate if we fail to compute the size. */
+ if (size <= 0)
+ {
+ warning (_("Recorded trace may be incomplete."));
+ break;
+ }
+
+ pc += size;
+ }
+ }
+
+ return itrace;
+}
+
+/* Return the function name of a recorded function segment for printing.
+ This function never returns NULL. */
+
+static const char *
+ftrace_print_function_name (struct btrace_func *bfun)
+{
+ struct minimal_symbol *msym;
+ struct symbol *sym;
+
+ msym = bfun->msym;
+ sym = bfun->sym;
+
+ if (sym != NULL)
+ return SYMBOL_PRINT_NAME (sym);
+
+ if (msym != NULL)
+ return SYMBOL_PRINT_NAME (msym);
+
+ return "<unknown>";
+}
+
+/* Return the file name of a recorded function segment for printing.
+ This function never returns NULL. */
+
+static const char *
+ftrace_print_filename (struct btrace_func *bfun)
+{
+ struct minimal_symbol *msym;
+ struct symbol *sym;
+ const char *filename;
+
+ msym = bfun->msym;
+ sym = bfun->sym;
+
+ if (sym != NULL)
+ filename = symtab_to_filename_for_display (sym->symtab);
+ else if (msym != NULL)
+ {
+ filename = msym->filename;
+ if (filename == NULL || *filename == 0)
+ filename = "<unknown>";
+ }
+ else
+ filename = "<unknown>";
+
+ return filename;
+}
+
+/* Print an ftrace debug status message. */
+
+static void
+ftrace_debug (struct btrace_func *bfun, const char *prefix)
+{
+ DEBUG_FTRACE ("%s: fun = %s, file = %s, lines = [%d; %d], insn = [%u; %u]",
+ prefix, ftrace_print_function_name (bfun),
+ ftrace_print_filename (bfun), bfun->lbegin, bfun->lend,
+ bfun->ibegin, bfun->iend);
+}
+
+/* Initialize a recorded function segment. */
+
+static void
+ftrace_init_func (struct btrace_func *bfun, struct minimal_symbol *mfun,
+ struct symbol *fun, unsigned int idx)
+{
+ bfun->msym = mfun;
+ bfun->sym = fun;
+ bfun->lbegin = INT_MAX;
+ bfun->lend = 0;
+ bfun->ibegin = idx;
+ bfun->iend = idx;
+}
+
+/* Check whether the function has changed. */
+
+static int
+ftrace_function_switched (struct btrace_func *bfun,
+ struct minimal_symbol *mfun, struct symbol *fun)
+{
+ struct minimal_symbol *msym;
+ struct symbol *sym;
+
+ /* The function changed if we did not have one before. */
+ if (bfun == NULL)
+ return 1;
+
+ msym = bfun->msym;
+ sym = bfun->sym;
+
+ /* If the minimal symbol changed, we certainly switched functions. */
+ if (mfun != NULL && msym != NULL)
+ {
+ const char *bfname, *fname;
+
+ /* Check the function name. */
+ if (strcmp_iw (SYMBOL_PRINT_NAME (mfun),
+ SYMBOL_PRINT_NAME (msym)) != 0)
+ return 1;
+
+ /* Check the location of those functions, as well. */
+ bfname = msym->filename;
+ fname = mfun->filename;
+ if (fname != NULL && bfname != NULL
+ && filename_cmp (fname, bfname) != 0)
+ return 1;
+ }
+
+ /* If the symbol changed, we certainly switched functions. */
+ if (fun != NULL && sym != NULL)
+ {
+ const char *bfname, *fname;
+
+ /* Check the function name. */
+ if (strcmp_iw (SYMBOL_PRINT_NAME (fun),
+ SYMBOL_PRINT_NAME (sym)) != 0)
+ return 1;
+
+ /* Check the location of those functions, as well. */
+ bfname = symtab_to_fullname (sym->symtab);
+ fname = symtab_to_fullname (fun->symtab);
+ if (filename_cmp (fname, bfname) != 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Check if we should skip this file when generating the function call
+ history. We would want to do that if, say, a macro that is defined
+ in another file is expanded in this function. */
+
+static int
+ftrace_skip_file (struct btrace_func *bfun, const char *filename)
+{
+ struct minimal_symbol *msym;
+ struct symbol *sym;
+ const char *bfile;
+
+ msym = bfun->msym;
+ sym = bfun->sym;
+
+ if (sym != NULL)
+ bfile = symtab_to_fullname (sym->symtab);
+ else if (msym != NULL && msym->filename != NULL)
+ bfile = msym->filename;
+ else
+ bfile = "";
+
+ if (filename == NULL)
+ filename = "";
+
+ return (filename_cmp (bfile, filename) != 0);
+}
+
+/* Compute the function trace from the instruction trace. */
+
+static VEC (btrace_func_s) *
+compute_ftrace (VEC (btrace_inst_s) *itrace)
+{
+ VEC (btrace_func_s) *ftrace;
+ struct btrace_inst *binst;
+ struct btrace_func *bfun;
+ unsigned int idx;
+
+ DEBUG ("compute ftrace");
+
+ ftrace = NULL;
+ bfun = NULL;
+
+ for (idx = 0; VEC_iterate (btrace_inst_s, itrace, idx, binst); ++idx)
+ {
+ struct symtab_and_line sal;
+ struct minimal_symbol *mfun;
+ struct symbol *fun;
+ const char *filename;
+ CORE_ADDR pc;
+
+ pc = binst->pc;
+
+ /* Try to determine the function we're in. We use both types of symbols
+ to avoid surprises when we sometimes get a full symbol and sometimes
+ only a minimal symbol. */
+ fun = find_pc_function (pc);
+ mfun = lookup_minimal_symbol_by_pc (pc);
+
+ if (fun == NULL && mfun == NULL)
+ {
+ DEBUG_FTRACE ("no symbol at %u, pc=%s", idx,
+ core_addr_to_string_nz (pc));
+ continue;
+ }
+
+ /* If we're switching functions, we start over. */
+ if (ftrace_function_switched (bfun, mfun, fun))
+ {
+ bfun = VEC_safe_push (btrace_func_s, ftrace, NULL);
+
+ ftrace_init_func (bfun, mfun, fun, idx);
+ ftrace_debug (bfun, "init");
+ }
+
+ /* Update the instruction range. */
+ bfun->iend = idx;
+ ftrace_debug (bfun, "update insns");
+
+ /* Let's see if we have source correlation, as well. */
+ sal = find_pc_line (pc, 0);
+ if (sal.symtab == NULL || sal.line == 0)
+ {
+ DEBUG_FTRACE ("no lines at %u, pc=%s", idx,
+ core_addr_to_string_nz (pc));
+ continue;
+ }
+
+ /* Check if we switched files. This could happen if, say, a macro that
+ is defined in another file is expanded here. */
+ filename = symtab_to_fullname (sal.symtab);
+ if (ftrace_skip_file (bfun, filename))
+ {
+ DEBUG_FTRACE ("ignoring file at %u, pc=%s, file=%s", idx,
+ core_addr_to_string_nz (pc), filename);
+ continue;
+ }
+
+ /* Update the line range. */
+ bfun->lbegin = min (bfun->lbegin, sal.line);
+ bfun->lend = max (bfun->lend, sal.line);
+ ftrace_debug (bfun, "update lines");
+ }
+
+ return ftrace;
+}
+
+/* See btrace.h. */
+
+void
+btrace_enable (struct thread_info *tp)
+{
+ if (tp->btrace.target != NULL)
+ return;
+
+ if (!target_supports_btrace ())
+ error (_("Target does not support branch tracing."));
+
+ DEBUG ("enable thread %d (%s)", tp->num, target_pid_to_str (tp->ptid));
+
+ tp->btrace.target = target_enable_btrace (tp->ptid);
+}
+
+/* See btrace.h. */
+
+void
+btrace_disable (struct thread_info *tp)
+{
+ struct btrace_thread_info *btp = &tp->btrace;
+ int errcode = 0;
+
+ if (btp->target == NULL)
+ return;
+
+ DEBUG ("disable thread %d (%s)", tp->num, target_pid_to_str (tp->ptid));
+
+ target_disable_btrace (btp->target);
+ btp->target = NULL;
+
+ btrace_clear (tp);
+}
+
+/* See btrace.h. */
+
+void
+btrace_disconnect (void)
+{
+ struct thread_info *tp;
+
+ DEBUG ("disconnect");
+
+ ALL_THREADS (tp)
+ btrace_disable (tp);
+}
+
+/* See btrace.h. */
+
+void
+btrace_fetch (struct thread_info *tp)
+{
+ struct btrace_thread_info *btinfo;
+
+ DEBUG ("fetch thread %d (%s)", tp->num, target_pid_to_str (tp->ptid));
+
+ btinfo = &tp->btrace;
+ if (btinfo->target == NULL)
+ return;
+
+ if (!target_btrace_has_changed (btinfo->target))
+ return;
+
+ btrace_clear (tp);
+
+ btinfo->btrace = target_read_btrace (btinfo->target);
+ btinfo->itrace = compute_itrace (btinfo->btrace);
+ btinfo->ftrace = compute_ftrace (btinfo->itrace);
+
+ /* Initialize branch trace iterators. */
+ btrace_init_insn_iterator (btinfo);
+ btrace_init_func_iterator (btinfo);
+}
+
+/* See btrace.h. */
+
+void
+btrace_clear (struct thread_info *tp)
+{
+ struct btrace_thread_info *btinfo;
+
+ DEBUG ("clear thread %d (%s)", tp->num, target_pid_to_str (tp->ptid));
+
+ btinfo = &tp->btrace;
+
+ VEC_free (btrace_block_s, btinfo->btrace);
+ VEC_free (btrace_inst_s, btinfo->itrace);
+ VEC_free (btrace_func_s, btinfo->ftrace);
+
+ btinfo->btrace = NULL;
+ btinfo->itrace = NULL;
+ btinfo->ftrace = NULL;
+}
+
+/* See btrace.h. */
+
+void
+btrace_free_objfile (struct objfile *objfile)
+{
+ struct thread_info *tp;
+
+ DEBUG ("free objfile");
+
+ ALL_THREADS (tp)
+ btrace_clear (tp);
+}
diff --git a/gdb/btrace.h b/gdb/btrace.h
new file mode 100644
index 0000000..a1b01c8
--- /dev/null
+++ b/gdb/btrace.h
@@ -0,0 +1,135 @@
+/* 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
+
+/* Branch tracing (btrace) is a per-thread control-flow execution trace of the
+ inferior. For presentation purposes, the branch trace is represented as a
+ list of sequential control-flow blocks, one such list per thread. */
+
+#include "btrace-common.h"
+
+struct thread_info;
+
+/* A branch trace instruction.
+
+ This represents a single instruction in a branch trace. */
+struct btrace_inst
+{
+ /* The address of this instruction. */
+ CORE_ADDR pc;
+};
+
+/* A branch trace function.
+
+ This represents a function segment in a branch trace, i.e. a consecutive
+ number of instructions belonging to the same function. */
+struct btrace_func
+{
+ /* The full and minimal symbol for the function. One of them may be NULL. */
+ struct minimal_symbol *msym;
+ struct symbol *sym;
+
+ /* The source line range of this function segment (both inclusive). */
+ int lbegin, lend;
+
+ /* The instruction number range in the instruction trace corresponding
+ to this function segment. */
+ unsigned int ibegin, iend;
+};
+
+/* Branch trace may also be represented as a vector of:
+
+ - branch trace instructions starting with the oldest instruction.
+ - branch trace functions starting with the oldest function. */
+typedef struct btrace_inst btrace_inst_s;
+typedef struct btrace_func btrace_func_s;
+
+/* Define functions operating on branch trace vectors. */
+DEF_VEC_O (btrace_inst_s);
+DEF_VEC_O (btrace_func_s);
+
+/* Branch trace iteration state for "record instruction-history". */
+struct btrace_insn_iterator
+{
+ /* The instruction index range [begin; end[ that has been covered last time.
+ If end < begin, the branch trace has just been updated. */
+ unsigned int begin;
+ unsigned int end;
+};
+
+/* Branch trace iteration state for "record function-call-history". */
+struct btrace_func_iterator
+{
+ /* The function index range [begin; end[ that has been covered last time.
+ If end < begin, the branch trace has just been updated. */
+ unsigned int begin;
+ unsigned int end;
+};
+
+/* Branch trace information per thread.
+
+ This represents the branch trace configuration as well as the entry point
+ into the branch trace data. For the latter, it also contains the index into
+ an array of branch trace blocks used for iterating though the branch trace
+ blocks of a thread. */
+struct btrace_thread_info
+{
+ /* The target branch trace information for this thread.
+
+ This contains the branch trace configuration as well as any
+ target-specific information necessary for implementing branch tracing on
+ the underlying architecture. */
+ struct btrace_target_info *target;
+
+ /* The current branch trace for this thread. */
+ VEC (btrace_block_s) *btrace;
+ VEC (btrace_inst_s) *itrace;
+ VEC (btrace_func_s) *ftrace;
+
+ /* The instruction history iterator. */
+ struct btrace_insn_iterator insn_iterator;
+
+ /* The function call history iterator. */
+ struct btrace_func_iterator func_iterator;
+};
+
+/* Enable branch tracing for a thread. */
+extern void btrace_enable (struct thread_info *tp);
+
+/* Disable branch tracing for a thread.
+ This will also delete the current branch trace data. */
+extern void btrace_disable (struct thread_info *);
+
+/* Disconnect branch tracing on detach. */
+extern void btrace_disconnect (void);
+
+/* Fetch the branch trace for a single thread. */
+extern void btrace_fetch (struct thread_info *);
+
+/* Clear the branch trace for a single thread. */
+extern void btrace_clear (struct thread_info *);
+
+/* Clear the branch trace for all threads when an object file goes away. */
+extern void btrace_free_objfile (struct objfile *);
+
+#endif /* BTRACE_H */
diff --git a/gdb/common/btrace-common.h b/gdb/common/btrace-common.h
new file mode 100644
index 0000000..90372ba
--- /dev/null
+++ b/gdb/common/btrace-common.h
@@ -0,0 +1,62 @@
+/* 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
+
+/* Branch tracing (btrace) is a per-thread control-flow execution trace of the
+ inferior. For presentation purposes, the branch trace is represented as a
+ list of sequential control-flow blocks, one such list per thread. */
+
+#ifdef GDBSERVER
+# include "server.h"
+#else
+# include "defs.h"
+#endif
+
+#include "vec.h"
+
+/* A branch trace block.
+
+ This represents a block of sequential control-flow. Adjacent blocks will be
+ connected via calls, returns, or jumps. The latter can be direct or
+ indirect, conditional or unconditional. Branches can further be
+ asynchronous, e.g. interrupts. */
+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 824e4d0..0846322 100644
--- a/gdb/gdbthread.h
+++ b/gdb/gdbthread.h
@@ -27,6 +27,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),... */
@@ -226,6 +227,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 1ef3b48..d808e92 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"

/* Local functions: */

@@ -2725,6 +2726,7 @@ detach_command (char *args, int from_tty)
error (_("The program is not being run."));

disconnect_tracing (from_tty);
+ btrace_disconnect ();

target_detach (args, from_tty);

diff --git a/gdb/target.c b/gdb/target.c
index ecb1325..9dfbe08 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -4147,6 +4147,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 1971265..628c108 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?
*/
@@ -1897,4 +1914,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 6b53c7a..cffaa42 100644
--- a/gdb/thread.c
+++ b/gdb/thread.c
@@ -33,6 +33,7 @@
#include "regcache.h"
#include "gdb.h"
#include "gdb_string.h"
+#include "btrace.h"

#include <ctype.h>
#include <sys/types.h>
@@ -116,6 +117,8 @@ clear_thread_inferior_resources (struct thread_info *tp)

bpstat_clear (&tp->control.stop_bpstat);

+ btrace_disable (tp);
+
do_all_intermediate_continuations_thread (tp, 1);
do_all_continuations_thread (tp, 1);
}
--
1.7.1