aboutsummaryrefslogtreecommitdiff
path: root/gdb/btrace.c
diff options
context:
space:
mode:
Diffstat (limited to 'gdb/btrace.c')
-rw-r--r--gdb/btrace.c449
1 files changed, 449 insertions, 0 deletions
diff --git a/gdb/btrace.c b/gdb/btrace.c
new file mode 100644
index 0000000..2acecbe
--- /dev/null
+++ b/gdb/btrace.c
@@ -0,0 +1,449 @@
+/* Branch trace support for GDB, the GNU debugger.
+
+ Copyright (C) 2013 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 symbol *sym;
+ const char *filename;
+
+ sym = bfun->sym;
+
+ if (sym != NULL)
+ filename = symtab_to_filename_for_display (sym->symtab);
+ 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
+ && strcmp (SYMBOL_LINKAGE_NAME (mfun), SYMBOL_LINKAGE_NAME (msym)) != 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 (SYMBOL_LINKAGE_NAME (fun), SYMBOL_LINKAGE_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 symbol *sym;
+ const char *bfile;
+
+ sym = bfun->sym;
+
+ if (sym != NULL)
+ bfile = symtab_to_fullname (sym->symtab);
+ 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_teardown (struct thread_info *tp)
+{
+ struct btrace_thread_info *btp = &tp->btrace;
+ int errcode = 0;
+
+ if (btp->target == NULL)
+ return;
+
+ DEBUG ("teardown thread %d (%s)", tp->num, target_pid_to_str (tp->ptid));
+
+ target_teardown_btrace (btp->target);
+ btp->target = NULL;
+
+ btrace_clear (tp);
+}
+
+/* See btrace.h. */
+
+void
+btrace_fetch (struct thread_info *tp)
+{
+ struct btrace_thread_info *btinfo;
+ VEC (btrace_block_s) *btrace;
+
+ DEBUG ("fetch thread %d (%s)", tp->num, target_pid_to_str (tp->ptid));
+
+ btinfo = &tp->btrace;
+ if (btinfo->target == NULL)
+ return;
+
+ btrace = target_read_btrace (btinfo->target, btrace_read_new);
+ if (VEC_empty (btrace_block_s, btrace))
+ return;
+
+ btrace_clear (tp);
+
+ btinfo->btrace = btrace;
+ 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);
+}