diff options
Diffstat (limited to 'gdb/btrace.c')
-rw-r--r-- | gdb/btrace.c | 449 |
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); +} |