This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
[PATCH 02/15] btrace: change branch trace data structure
- From: Markus Metzger <markus dot t dot metzger at intel dot com>
- To: jan dot kratochvil at redhat dot com
- Cc: gdb-patches at sourceware dot org, Christian Himpel <christian dot himpel at intel dot com>
- Date: Thu, 2 May 2013 14:03:23 +0200
- Subject: [PATCH 02/15] btrace: change branch trace data structure
- References: <1367496216-21217-1-git-send-email-markus dot t dot metzger at intel dot com>
The branch trace is represented as 3 vectors:
- a block vector
- a instruction vector
- a function vector
Each vector (except for the first) is computed from the one above.
Change this into a graph where a node represents a sequence of instructions
belonging to the same function and where we have three types of edges to connect
the function segments:
- control flow
- same function (instance)
- call stack
This allows us to navigate in the branch trace. We will need this for "record
goto" and reverse execution.
This patch introduces the data structure and computes the control flow edges.
It also fixes PR gdb/15240 since now recursive calls are handled correctly.
Fix the test that got the number of expected fib instances and also the
function numbers wrong.
CC: Christian Himpel <christian.himpel@intel.com>
2013-05-02 Markus Metzger <markus.t.metzger@intel.com>
* btrace.h (struct btrace_func_link): New.
(enum btrace_function_flag): New.
(struct btrace_inst): Rename to ...
(struct btrace_insn): ...this. Update all users.
(struct btrace_func) <ibegin, iend>: Remove.
(struct btrace_func): Rename to ...
(struct btrace_function): ...this. Update all users.
(struct btrace_function) <segment, flow, up, insn, insn_offset,
number, level, flags>: New.
(struct btrace_insn_iterator): Rename to ...
(struct btrace_insn_history): ...this.
Update all users.
(struct btrace_target_info) <btrace, itrace, ftrace>: Remove.
(struct btrace_target_info) <begin, end, level,
insn_history, call_history>: New.
(btrace_insn_get, btrace_insn_number, btrace_insn_begin,
btrace_insn_end, btrace_insn_prev, btrace_insn_next,
btrace_insn_cmp, btrace_find_insn_by_number,
btrace_find_function_by_number, btrace_set_insn_history,
btrace_set_call_history): New.
* btrace.c (btrace_init_insn_iterator,
btrace_init_func_iterator, compute_itrace): Remove.
(ftrace_debug): Use new btrace_function fields.
(ftrace_function_switched): Also consider gaining and
losing symbol information).
(ftrace_print_insn_addr, ftrace_new_call, ftrace_new_return,
ftrace_new_switch, ftrace_find_caller, ftrace_new_function,
ftrace_update_caller, ftrace_fixup_caller, ftrace_new_tailcall):
New.
(ftrace_new_function): Move. Remove debug print.
(ftrace_update_lines, ftrace_update_insns): New.
(ftrace_update_function): Check for call, ret, and jump.
(compute_ftrace): Renamed to ...
(btrace_compute_ftrace): ...this. Rewritten to compute call
stack.
(btrace_fetch, btrace_clear): Updated.
(btrace_insn_get, btrace_insn_number, btrace_insn_begin,
btrace_insn_end, btrace_insn_prev, btrace_insn_next,
btrace_insn_cmp, btrace_find_insn_by_number,
btrace_find_function_by_number, btrace_set_insn_history,
btrace_set_call_history): New.
* record-btrace.c (require_btrace, record_btrace_info,
btrace_insn_history, record_btrace_insn_history,
record_btrace_insn_history_range): Use new btrace thread
info fields.
(btrace_func_history_src_line): Rename to ...
(btrace_call_history_src_line): ...this. Use new btrace
thread info fields.
(btrace_func_history): Rename to ...
(btrace_call_history): ...this. Use new btrace thread info
fields.
(record_btrace_call_history, record_btrace_call_history_range):
Use new btrace thread info fields.
testsuite/
* gdb.btrace/function_call_history.exp: Fix expected function
trace.
---
gdb/btrace.c | 965 ++++++++++++++++----
gdb/btrace.h | 167 +++-
gdb/record-btrace.c | 379 +++++----
gdb/testsuite/gdb.btrace/function_call_history.exp | 28 +-
4 files changed, 1161 insertions(+), 378 deletions(-)
diff --git a/gdb/btrace.c b/gdb/btrace.c
index 3230a3e..9478350 100644
--- a/gdb/btrace.c
+++ b/gdb/btrace.c
@@ -45,92 +45,11 @@
#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)
+ftrace_print_function_name (const struct btrace_function *bfun)
{
struct minimal_symbol *msym;
struct symbol *sym;
@@ -151,7 +70,7 @@ ftrace_print_function_name (struct btrace_func *bfun)
This function never returns NULL. */
static const char *
-ftrace_print_filename (struct btrace_func *bfun)
+ftrace_print_filename (const struct btrace_function *bfun)
{
struct symbol *sym;
const char *filename;
@@ -166,44 +85,52 @@ ftrace_print_filename (struct btrace_func *bfun)
return filename;
}
-/* Print an ftrace debug status message. */
+/* Print the address of an instruction.
+ This function never returns NULL. */
-static void
-ftrace_debug (struct btrace_func *bfun, const char *prefix)
+static const char *
+ftrace_print_insn_addr (const struct btrace_insn *insn)
{
- 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);
+ if (insn == NULL)
+ return "<nil>";
+
+ return core_addr_to_string_nz (insn->pc);
}
-/* Initialize a recorded function segment. */
+/* Print an ftrace debug status message. */
static void
-ftrace_init_func (struct btrace_func *bfun, struct minimal_symbol *mfun,
- struct symbol *fun, unsigned int idx)
+ftrace_debug (const struct btrace_function *bfun, const char *prefix)
{
- bfun->msym = mfun;
- bfun->sym = fun;
- bfun->lbegin = INT_MAX;
- bfun->lend = 0;
- bfun->ibegin = idx;
- bfun->iend = idx;
+ const char *fun, *file;
+ unsigned int ibegin, iend;
+ int lbegin, lend, level;
+
+ fun = ftrace_print_function_name (bfun);
+ file = ftrace_print_filename (bfun);
+ level = bfun->level;
+
+ lbegin = bfun->lbegin;
+ lend = bfun->lend;
+
+ ibegin = bfun->insn_offset;
+ iend = ibegin + VEC_length (btrace_insn_s, bfun->insn);
+
+ DEBUG_FTRACE ("%s: fun = %s, file = %s, level = %d, lines = [%d; %d], "
+ "insn = [%u; %u)", prefix, fun, file, level, lbegin, lend,
+ ibegin, iend);
}
/* Check whether the function has changed. */
static int
-ftrace_function_switched (struct btrace_func *bfun,
- struct minimal_symbol *mfun, struct symbol *fun)
+ftrace_function_switched (const struct btrace_function *bfun,
+ const struct minimal_symbol *mfun,
+ const 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;
@@ -228,6 +155,14 @@ ftrace_function_switched (struct btrace_func *bfun,
return 1;
}
+ /* If we lost symbol information, we switched functions. */
+ if (!(msym == NULL && sym == NULL) && mfun == NULL && fun == NULL)
+ return 1;
+
+ /* If we gained symbol information, we switched functions. */
+ if (msym == NULL && sym == NULL && !(mfun == NULL && fun == NULL))
+ return 1;
+
return 0;
}
@@ -236,7 +171,7 @@ ftrace_function_switched (struct btrace_func *bfun,
in another file is expanded in this function. */
static int
-ftrace_skip_file (struct btrace_func *bfun, const char *filename)
+ftrace_skip_file (const struct btrace_function *bfun, const char *filename)
{
struct symbol *sym;
const char *bfile;
@@ -254,83 +189,458 @@ ftrace_skip_file (struct btrace_func *bfun, const char *filename)
return (filename_cmp (bfile, filename) != 0);
}
-/* Compute the function trace from the instruction trace. */
+/* Allocate and initialize a new branch trace function segment. */
-static VEC (btrace_func_s) *
-compute_ftrace (VEC (btrace_inst_s) *itrace)
+static struct btrace_function *
+ftrace_new_function (struct btrace_function *prev,
+ struct minimal_symbol *mfun,
+ struct symbol *fun)
{
- VEC (btrace_func_s) *ftrace;
- struct btrace_inst *binst;
- struct btrace_func *bfun;
- unsigned int idx;
+ struct btrace_function *bfun;
- DEBUG ("compute ftrace");
+ bfun = xzalloc (sizeof (*bfun));
- ftrace = NULL;
- bfun = NULL;
+ bfun->msym = mfun;
+ bfun->sym = fun;
+ bfun->lbegin = INT_MAX;
+ bfun->flow.prev = prev;
- for (idx = 0; VEC_iterate (btrace_inst_s, itrace, idx, binst); ++idx)
+ if (prev != NULL)
{
- struct symtab_and_line sal;
- struct bound_minimal_symbol mfun;
- struct symbol *fun;
- const char *filename;
+ gdb_assert (prev->flow.next == NULL);
+ prev->flow.next = bfun;
+
+ bfun->number = prev->number + 1;
+ bfun->insn_offset = prev->insn_offset
+ + VEC_length (btrace_insn_s, prev->insn);
+ }
+
+ return bfun;
+}
+
+/* Update the UP field of a function segment. */
+
+static void
+ftrace_update_caller (struct btrace_function *bfun,
+ struct btrace_function *caller)
+{
+ if (bfun->up != NULL)
+ ftrace_debug (bfun, "updating caller");
+
+ bfun->up = caller;
+
+ ftrace_debug (bfun, "set caller");
+}
+
+/* Fix up the caller for a function segment. */
+
+static void
+ftrace_fixup_caller (struct btrace_function *bfun,
+ struct btrace_function *caller)
+{
+ struct btrace_function *prev, *next;
+
+ ftrace_update_caller (bfun, caller);
+
+ /* Update all function segments belonging to the same function. */
+ for (prev = bfun->segment.prev; prev != NULL; prev = prev->segment.prev)
+ ftrace_update_caller (prev, caller);
+
+ for (next = bfun->segment.next; next != NULL; next = next->segment.next)
+ ftrace_update_caller (next, caller);
+}
+
+/* Add a new function segment for a call. */
+
+static struct btrace_function *
+ftrace_new_call (struct btrace_function *caller,
+ struct minimal_symbol *mfun,
+ struct symbol *fun)
+{
+ struct btrace_function *bfun;
+
+ bfun = ftrace_new_function (caller, mfun, fun);
+ bfun->up = caller;
+ bfun->level = caller->level + 1;
+
+ ftrace_debug (bfun, "new call");
+
+ return bfun;
+}
+
+/* Add a new function segment for a tail call. */
+
+static struct btrace_function *
+ftrace_new_tailcall (struct btrace_function *caller,
+ struct minimal_symbol *mfun,
+ struct symbol *fun)
+{
+ struct btrace_function *bfun;
+
+ bfun = ftrace_new_function (caller, mfun, fun);
+ bfun->up = caller;
+ bfun->level = caller->level + 1;
+ bfun->flags |= bfun_up_links_to_tailcall;
+
+ ftrace_debug (bfun, "new tail call");
+
+ return bfun;
+}
+
+/* Find the caller of BFUN.
+ This is the first function segment up the call stack from BFUN with
+ MFUN/FUN symbol information. */
+
+static struct btrace_function *
+ftrace_find_caller (struct btrace_function *bfun,
+ struct minimal_symbol *mfun,
+ struct symbol *fun)
+{
+ for (; bfun != NULL; bfun = bfun->up)
+ {
+ /* Skip functions with incompatible symbol information. */
+ if (ftrace_function_switched (bfun, mfun, fun))
+ continue;
+
+ /* This is the function segment we're looking for. */
+ break;
+ }
+
+ return bfun;
+}
+
+/* Find the last actual call in the back trace of BFUN. */
+
+static struct btrace_function *
+ftrace_find_call (struct gdbarch *gdbarch, struct btrace_function *bfun)
+{
+ if (!gdbarch_insn_call_p_p (gdbarch))
+ return NULL;
+
+ for (; bfun != NULL; bfun = bfun->up)
+ {
+ struct btrace_insn *last;
CORE_ADDR pc;
- pc = binst->pc;
+ if (VEC_empty (btrace_insn_s, bfun->insn))
+ continue;
+
+ last = VEC_last (btrace_insn_s, bfun->insn);
+ pc = last->pc;
+
+ if (gdbarch_insn_call_p (gdbarch, pc))
+ break;
+ }
+
+ return bfun;
+}
+
+/* Add a new function segment for a return. */
+
+static struct btrace_function *
+ftrace_new_return (struct gdbarch *gdbarch,
+ struct btrace_function *prev,
+ struct minimal_symbol *mfun,
+ struct symbol *fun)
+{
+ struct btrace_function *bfun, *caller;
+
+ bfun = ftrace_new_function (prev, mfun, fun);
+
+ /* It is important to start at PREV's caller. Otherwise, we might find
+ PREV itself, if PREV is a recursive function. */
+ caller = ftrace_find_caller (prev->up, mfun, fun);
+ if (caller != NULL)
+ {
+ /* The caller of PREV is the preceding btrace function segment in this
+ function instance. */
+ gdb_assert (caller->segment.next == NULL);
+
+ caller->segment.next = bfun;
+ bfun->segment.prev = caller;
+
+ /* Maintain the function level. */
+ bfun->level = caller->level;
- /* 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);
+ /* Maintain the call stack. */
+ bfun->up = caller->up;
- if (fun == NULL && mfun.minsym == NULL)
+ ftrace_debug (bfun, "new return");
+ }
+ else
+ {
+ /* We did not find a caller. This could mean that something went
+ wrong or that the call is simply not included in the trace. */
+
+ /* Let's search for some actual call. */
+ caller = ftrace_find_call (gdbarch, prev->up);
+ if (caller == NULL)
{
- DEBUG_FTRACE ("no symbol at %u, pc=%s", idx,
- core_addr_to_string_nz (pc));
- continue;
- }
+ /* There is no call in PREV's back trace. We assume that the
+ branch trace did not include it. */
+
+ /* Let's find the topmost call function - this skips tail calls. */
+ while (prev->up != NULL)
+ prev = prev->up;
- /* If we're switching functions, we start over. */
- if (ftrace_function_switched (bfun, mfun.minsym, fun))
+ /* We maintain levels for a series of returns for which we have
+ not seen the calls, but we restart at level 0, otherwise. */
+ bfun->level = min (0, prev->level) - 1;
+
+ /* Fix up the call stack for PREV. */
+ ftrace_fixup_caller (prev, bfun);
+ prev->flags |= bfun_up_links_to_ret;
+
+ ftrace_debug (bfun, "new return - no caller");
+ }
+ else
{
- bfun = VEC_safe_push (btrace_func_s, ftrace, NULL);
+ /* There is a call in PREV's back trace to which we should have
+ returned. Let's remain at this level. */
+ bfun->level = prev->level;
- ftrace_init_func (bfun, mfun.minsym, fun, idx);
- ftrace_debug (bfun, "init");
+ ftrace_debug (bfun, "new return - unknown caller");
}
+ }
+
+ return bfun;
+}
+
+/* Add a new function segment for a function switch. */
+
+static struct btrace_function *
+ftrace_new_switch (struct btrace_function *prev,
+ struct minimal_symbol *mfun,
+ struct symbol *fun,
+ const struct btrace_insn *insn)
+{
+ struct btrace_function *bfun;
+
+ /* This is an unexplained function switch. The call stack will likely
+ be wrong at this point. */
+ bfun = ftrace_new_function (prev, mfun, fun);
+
+ /* We keep the function level. */
+ bfun->level = prev->level;
+
+ ftrace_debug (bfun, "new switch");
- /* Update the instruction range. */
- bfun->iend = idx;
- ftrace_debug (bfun, "update insns");
+ return bfun;
+}
+
+/* Update the branch trace function segment. Never returns NULL. */
+
+static struct btrace_function *
+ftrace_update_function (struct gdbarch *gdbarch,
+ struct btrace_function *bfun, CORE_ADDR pc)
+{
+ struct bound_minimal_symbol bmfun;
+ struct minimal_symbol *mfun;
+ struct symbol *fun;
+ struct btrace_insn *last;
+
+ /* 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);
+ bmfun = lookup_minimal_symbol_by_pc (pc);
+ mfun = bmfun.minsym;
+
+ if (fun == NULL && mfun == NULL)
+ DEBUG_FTRACE ("no symbol at %s", core_addr_to_string_nz (pc));
+
+ /* If we didn't have a function before, we create one. */
+ if (bfun == NULL)
+ return ftrace_new_function (bfun, mfun, fun);
+
+ /* Check the last instruction, if we have one.
+ We do this check first, since it allows us to fill in the call stack
+ links in addition to the normal flow links. */
+ last = NULL;
+ if (!VEC_empty (btrace_insn_s, bfun->insn))
+ last = VEC_last (btrace_insn_s, bfun->insn);
- /* Let's see if we have source correlation, as well. */
- sal = find_pc_line (pc, 0);
- if (sal.symtab == NULL || sal.line == 0)
+ if (last != NULL)
+ {
+ CORE_ADDR lpc;
+
+ lpc = last->pc;
+
+ /* Check for returns. */
+ if (gdbarch_insn_ret_p_p (gdbarch) && gdbarch_insn_ret_p (gdbarch, lpc))
+ return ftrace_new_return (gdbarch, bfun, mfun, fun);
+
+ /* Check for calls. */
+ if (gdbarch_insn_call_p_p (gdbarch) && gdbarch_insn_call_p (gdbarch, lpc))
{
- DEBUG_FTRACE ("no lines at %u, pc=%s", idx,
- core_addr_to_string_nz (pc));
- continue;
+ int size;
+
+ size = gdb_insn_length (gdbarch, lpc);
+
+ /* Ignore calls to the next instruction. They are used for PIC. */
+ if (lpc + size != pc)
+ return ftrace_new_call (bfun, mfun, fun);
}
+ }
+
+ /* Check if we're switching functions for some other reason. */
+ if (ftrace_function_switched (bfun, mfun, fun))
+ {
+ DEBUG_FTRACE ("switching from %s in %s at %s",
+ ftrace_print_insn_addr (last),
+ ftrace_print_function_name (bfun),
+ ftrace_print_filename (bfun));
- /* 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))
+ if (last != NULL)
{
- DEBUG_FTRACE ("ignoring file at %u, pc=%s, file=%s", idx,
- core_addr_to_string_nz (pc), filename);
- continue;
+ CORE_ADDR start, lpc;
+
+ /* If we have symbol information for our current location, use
+ it to check that we jump to the start of a function. */
+ if (fun != NULL || mfun != NULL)
+ start = get_pc_function_start (pc);
+ else
+ start = pc;
+
+ lpc = last->pc;
+
+ /* Jumps indicate optimized tail calls. */
+ if (start == pc
+ && gdbarch_insn_jump_p_p (gdbarch)
+ && gdbarch_insn_jump_p (gdbarch, lpc))
+ return ftrace_new_tailcall (bfun, mfun, fun);
}
- /* 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_new_switch (bfun, mfun, fun, last);
+ }
+
+ return bfun;
+}
+
+/* Update the source correlation for a branch trace function segment. */
+
+static void
+ftrace_update_lines (struct btrace_function *bfun, CORE_ADDR pc)
+{
+ struct symtab_and_line sal;
+ const char *filename;
+
+ sal = find_pc_line (pc, 0);
+ if (sal.symtab == NULL || sal.line == 0)
+ {
+ DEBUG_FTRACE ("no lines at %s", core_addr_to_string_nz (pc));
+ return;
+ }
+
+ /* 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 %s, file=%s",
+ core_addr_to_string_nz (pc), filename);
+ return;
}
- return ftrace;
+ /* Update the line range. */
+ bfun->lbegin = min (bfun->lbegin, sal.line);
+ bfun->lend = max (bfun->lend, sal.line);
+
+ if (record_debug > 1)
+ ftrace_debug (bfun, "update lines");
+}
+
+/* Update the instructions for a branch trace function segment. */
+
+static void
+ftrace_update_insns (struct btrace_function *bfun, CORE_ADDR pc)
+{
+ struct btrace_insn *insn;
+
+ insn = VEC_safe_push (btrace_insn_s, bfun->insn, NULL);
+ insn->pc = pc;
+
+ if (record_debug > 1)
+ ftrace_debug (bfun, "update insn");
+}
+
+/* Compute the function branch trace. */
+
+static void
+btrace_compute_ftrace (struct btrace_thread_info *btinfo,
+ VEC (btrace_block_s) *btrace)
+{
+ struct btrace_function *begin, *end;
+ struct gdbarch *gdbarch;
+ unsigned int blk;
+ int level;
+
+ DEBUG ("compute ftrace");
+
+ gdbarch = target_gdbarch ();
+ begin = NULL;
+ end = NULL;
+ level = INT_MAX;
+ blk = VEC_length (btrace_block_s, btrace);
+
+ while (blk != 0)
+ {
+ btrace_block_s *block;
+ CORE_ADDR pc;
+
+ blk -= 1;
+
+ block = VEC_index (btrace_block_s, btrace, blk);
+ pc = block->begin;
+
+ for (;;)
+ {
+ 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;
+ }
+
+ end = ftrace_update_function (gdbarch, end, pc);
+ if (begin == NULL)
+ begin = end;
+
+ /* Maintain the function level offset. */
+ level = min (level, end->level);
+
+ ftrace_update_insns (end, pc);
+ ftrace_update_lines (end, 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;
+ }
+ }
+
+ /* Add an empty dummy function to mark the end of the branch trace. */
+ end = ftrace_new_function (end, NULL, NULL);
+
+ btinfo->begin = begin;
+ btinfo->end = end;
+
+ /* LEVEL is the minimal function level of all btrace function segments.
+ Define the global level offset to -LEVEL so all function levels are
+ normalized to start at zero. */
+ btinfo->level = -level;
}
/* See btrace.h. */
@@ -394,6 +704,7 @@ btrace_fetch (struct thread_info *tp)
{
struct btrace_thread_info *btinfo;
VEC (btrace_block_s) *btrace;
+ struct cleanup *cleanup;
DEBUG ("fetch thread %d (%s)", tp->num, target_pid_to_str (tp->ptid));
@@ -402,18 +713,15 @@ btrace_fetch (struct thread_info *tp)
return;
btrace = target_read_btrace (btinfo->target, btrace_read_new);
- if (VEC_empty (btrace_block_s, btrace))
- return;
-
- btrace_clear (tp);
+ cleanup = make_cleanup (VEC_cleanup (btrace_block_s), &btrace);
- btinfo->btrace = btrace;
- btinfo->itrace = compute_itrace (btinfo->btrace);
- btinfo->ftrace = compute_ftrace (btinfo->itrace);
+ if (!VEC_empty (btrace_block_s, btrace))
+ {
+ btrace_clear (tp);
+ btrace_compute_ftrace (btinfo, btrace);
+ }
- /* Initialize branch trace iterators. */
- btrace_init_insn_iterator (btinfo);
- btrace_init_func_iterator (btinfo);
+ do_cleanups (cleanup);
}
/* See btrace.h. */
@@ -422,18 +730,29 @@ void
btrace_clear (struct thread_info *tp)
{
struct btrace_thread_info *btinfo;
+ struct btrace_function *it, *trash;
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);
+ it = btinfo->begin;
+ while (it != NULL)
+ {
+ trash = it;
+ it = it->flow.next;
+
+ xfree (trash);
+ }
+
+ btinfo->begin = NULL;
+ btinfo->end = NULL;
- btinfo->btrace = NULL;
- btinfo->itrace = NULL;
- btinfo->ftrace = NULL;
+ xfree (btinfo->insn_history);
+ xfree (btinfo->call_history);
+
+ btinfo->insn_history = NULL;
+ btinfo->call_history = NULL;
}
/* See btrace.h. */
@@ -541,3 +860,301 @@ parse_xml_btrace (const char *buffer)
return btrace;
}
+
+/* See btrace.h. */
+
+const struct btrace_insn *
+btrace_insn_get (const struct btrace_insn_iterator *it)
+{
+ struct btrace_function *function;
+ unsigned int index, end;
+
+ if (it == NULL)
+ return NULL;
+
+ index = it->index;
+ function = it->function;
+ if (function == NULL)
+ return NULL;
+
+ end = VEC_length (btrace_insn_s, function->insn);
+ if (end == 0)
+ return NULL;
+
+ gdb_assert (index < end);
+
+ return VEC_index (btrace_insn_s, function->insn, index);
+}
+
+/* See btrace.h. */
+
+unsigned int
+btrace_insn_number (const struct btrace_insn_iterator *it)
+{
+ struct btrace_function *function;
+
+ if (it == NULL)
+ return 0;
+
+ function = it->function;
+ if (function == NULL)
+ return 0;
+
+ return function->insn_offset + it->index;
+}
+
+/* See btrace.h. */
+
+void
+btrace_insn_begin (struct btrace_insn_iterator *it,
+ struct btrace_thread_info *btinfo)
+{
+ struct btrace_function *begin;
+
+ begin = btinfo->begin;
+ if (begin == NULL)
+ error (_("No trace."));
+
+ it->function = begin;
+ it->index = 0;
+}
+
+/* See btrace.h. */
+
+void
+btrace_insn_end (struct btrace_insn_iterator *it,
+ struct btrace_thread_info *btinfo)
+{
+ struct btrace_function *end;
+
+ end = btinfo->end;
+ if (end == NULL)
+ error (_("No trace."));
+
+ /* The last function is an empty dummy. */
+ it->function = end;
+ it->index = 0;
+}
+
+/* See btrace.h. */
+
+unsigned int
+btrace_insn_next (struct btrace_insn_iterator * it, unsigned int stride)
+{
+ struct btrace_function *function;
+ unsigned int index, end, space, adv, steps;
+
+ if (it == NULL)
+ return 0;
+
+ function = it->function;
+ if (function == NULL)
+ return 0;
+
+ steps = 0;
+ index = it->index;
+
+ while (stride != 0)
+ {
+ end = VEC_length (btrace_insn_s, function->insn);
+
+ /* Compute the number of instructions remaining in this segment. */
+ gdb_assert ((end == 0 && index == 0) || index < end);
+ space = end - index;
+
+ /* Advance the iterator as far as possible within this segment. */
+ adv = min (space, stride);
+ stride -= adv;
+ index += adv;
+ steps += adv;
+
+ /* Move to the next function if we're at the end of this one. */
+ if (index == end)
+ {
+ struct btrace_function *next;
+
+ next = function->flow.next;
+ if (next == NULL)
+ {
+ /* We stepped past the last function - an empty dummy. */
+ gdb_assert (adv == 0);
+ break;
+ }
+
+ /* We now point to the first instruction in the new function. */
+ function = next;
+ index = 0;
+ }
+
+ /* We did make progress. */
+ gdb_assert (adv > 0);
+ }
+
+ /* Update the iterator. */
+ it->function = function;
+ it->index = index;
+
+ return steps;
+}
+
+/* See btrace.h. */
+
+unsigned int
+btrace_insn_prev (struct btrace_insn_iterator * it, unsigned int stride)
+{
+ struct btrace_function *function;
+ unsigned int index, adv, steps;
+
+ if (it == NULL)
+ return 0;
+
+ function = it->function;
+ if (function == NULL)
+ return 0;
+
+ steps = 0;
+ index = it->index;
+
+ while (stride != 0)
+ {
+ /* Move to the previous function if we're at the start of this one. */
+ if (index == 0)
+ {
+ struct btrace_function *prev;
+
+ prev = function->flow.prev;
+ if (prev == NULL)
+ break;
+
+ /* We point to one after the last instruction in the new function. */
+ function = prev;
+ index = VEC_length (btrace_insn_s, function->insn);
+
+ /* There is at least one instruction in this function segment. */
+ gdb_assert (index > 0);
+ }
+
+ /* Advance the iterator as far as possible within this segment. */
+ adv = min (index, stride);
+ stride -= adv;
+ index -= adv;
+ steps += adv;
+
+ /* We did make progress. */
+ gdb_assert (adv > 0);
+ }
+
+ /* Update the iterator. */
+ it->function = function;
+ it->index = index;
+
+ return steps;
+}
+
+/* See btrace.h. */
+
+int
+btrace_insn_cmp (const struct btrace_insn_iterator *lhs,
+ const struct btrace_insn_iterator *rhs)
+{
+ unsigned int lnum, rnum;
+
+ lnum = btrace_insn_number (lhs);
+ rnum = btrace_insn_number (rhs);
+
+ return (int) (lnum - rnum);
+}
+
+/* See btrace.h. */
+
+int
+btrace_find_insn_by_number (struct btrace_insn_iterator *it,
+ const struct btrace_thread_info *btinfo,
+ unsigned int number)
+{
+ struct btrace_function *bfun;
+ unsigned int last;
+
+ for (bfun = btinfo->end; bfun != NULL; bfun = bfun->flow.prev)
+ if (bfun->insn_offset <= number)
+ break;
+
+ if (bfun == NULL)
+ return 0;
+
+ last = bfun->insn_offset + VEC_length (btrace_insn_s, bfun->insn);
+ if (last <= number)
+ return 0;
+
+ it->function = bfun;
+ it->index = number - bfun->insn_offset;
+
+ return 1;
+}
+
+/* See btrace.h. */
+
+struct btrace_function *
+btrace_find_function_by_number (const struct btrace_thread_info *btinfo,
+ unsigned int number)
+{
+ struct btrace_function *bfun;
+
+ if (btinfo == NULL)
+ return NULL;
+
+ for (bfun = btinfo->end; bfun != NULL; bfun = bfun->flow.prev)
+ {
+ unsigned int bnum;
+
+ bnum = bfun->number;
+ if (number == bnum)
+ return bfun;
+
+ /* Functions are ordered and numbered consecutively. We could bail out
+ earlier. On the other hand, it is very unlikely that we search for
+ a nonexistent function. */
+ }
+
+ return NULL;
+}
+
+/* See btrace.h. */
+
+void
+btrace_set_insn_history (struct btrace_thread_info *btinfo,
+ struct btrace_insn_iterator *begin,
+ struct btrace_insn_iterator *end)
+{
+ struct btrace_insn_history *history;
+
+ history = btinfo->insn_history;
+ if (history == NULL)
+ {
+ history = xzalloc (sizeof (*history));
+ btinfo->insn_history = history;
+ }
+
+ history->begin = *begin;
+ history->end = *end;
+}
+
+/* See btrace.h. */
+
+void
+btrace_set_call_history (struct btrace_thread_info *btinfo,
+ struct btrace_function *begin,
+ struct btrace_function *end)
+{
+ struct btrace_call_history *history;
+
+ history = btinfo->call_history;
+ if (history == NULL)
+ {
+ history = xzalloc (sizeof (*history));
+ btinfo->call_history = history;
+ }
+
+ history->begin = begin;
+ history->end = end;
+}
diff --git a/gdb/btrace.h b/gdb/btrace.h
index bd8425d..ac7acdb 100644
--- a/gdb/btrace.h
+++ b/gdb/btrace.h
@@ -29,63 +29,106 @@
#include "btrace-common.h"
struct thread_info;
+struct btrace_function;
/* A branch trace instruction.
This represents a single instruction in a branch trace. */
-struct btrace_inst
+struct btrace_insn
{
/* The address of this instruction. */
CORE_ADDR pc;
};
-/* A branch trace function.
+/* A vector of branch trace instructions. */
+typedef struct btrace_insn btrace_insn_s;
+DEF_VEC_O (btrace_insn_s);
+
+/* A doubly-linked list of branch trace function segments. */
+struct btrace_func_link
+{
+ struct btrace_function *prev;
+ struct btrace_function *next;
+};
+
+/* Flags for btrace function segments. */
+enum btrace_function_flag
+{
+ /* The 'up' link interpretation.
+ If set, it points to the function segment we returned to.
+ If clear, it points to the function segment we called from. */
+ bfun_up_links_to_ret = (1 << 0),
+
+ /* The 'up' link points to a tail call. This obviously only makes sense
+ if bfun_up_links_to_ret is clear. */
+ bfun_up_links_to_tailcall = (1 << 1)
+};
+
+/* A branch trace function segment.
This represents a function segment in a branch trace, i.e. a consecutive
number of instructions belonging to the same function. */
-struct btrace_func
+struct btrace_function
{
/* The full and minimal symbol for the function. One of them may be NULL. */
struct minimal_symbol *msym;
struct symbol *sym;
+ /* The previous and next segment belonging to the same function. */
+ struct btrace_func_link segment;
+
+ /* The previous and next function in control flow order. */
+ struct btrace_func_link flow;
+
+ /* The directly preceding function segment in a (fake) call stack. */
+ struct btrace_function *up;
+
+ /* The instructions in this function segment. */
+ VEC (btrace_insn_s) *insn;
+
+ /* The instruction number offset for the first instruction in this
+ function segment. */
+ unsigned int insn_offset;
+
+ /* The function number. */
+ unsigned int number;
+
+ /* The function level. */
+ int level;
+
/* 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 (both inclusive). */
- unsigned int ibegin, iend;
+ /* A bit-vector of btrace_function_flag. */
+ unsigned int flags;
};
-/* 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;
+/* A branch trace instruction iterator. */
+struct btrace_insn_iterator
+{
+ /* The branch trace function segment containing the instruction. */
+ struct btrace_function *function;
-/* Define functions operating on branch trace vectors. */
-DEF_VEC_O (btrace_inst_s);
-DEF_VEC_O (btrace_func_s);
+ /* The index into the function segment's instruction vector. */
+ unsigned int index;
+};
/* Branch trace iteration state for "record instruction-history". */
-struct btrace_insn_iterator
+struct btrace_insn_history
{
- /* The instruction index range from begin (inclusive) to end (exclusive)
- that has been covered last time.
- If end < begin, the branch trace has just been updated. */
- unsigned int begin;
- unsigned int end;
+ /* The branch trace instruction range from begin (inclusive) to
+ end (exclusive) that has been covered last time. */
+ struct btrace_insn_iterator begin;
+ struct btrace_insn_iterator end;
};
/* Branch trace iteration state for "record function-call-history". */
-struct btrace_func_iterator
+struct btrace_call_history
{
- /* The function index range from begin (inclusive) to end (exclusive)
- that has been covered last time.
- If end < begin, the branch trace has just been updated. */
- unsigned int begin;
- unsigned int end;
+ /* The branch trace function range from begin (inclusive) to end (exclusive)
+ that has been covered last time. */
+ struct btrace_function *begin;
+ struct btrace_function *end;
};
/* Branch trace information per thread.
@@ -104,15 +147,19 @@ struct btrace_thread_info
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;
+ struct btrace_function *begin;
+ struct btrace_function *end;
+
+ /* The function level offset. When added to each function's level,
+ this normalizes the function levels such that the smallest level
+ becomes zero. */
+ int level;
/* The instruction history iterator. */
- struct btrace_insn_iterator insn_iterator;
+ struct btrace_insn_history *insn_history;
/* The function call history iterator. */
- struct btrace_func_iterator func_iterator;
+ struct btrace_call_history *call_history;
};
/* Enable branch tracing for a thread. */
@@ -139,4 +186,60 @@ extern void btrace_free_objfile (struct objfile *);
/* Parse a branch trace xml document into a block vector. */
extern VEC (btrace_block_s) *parse_xml_btrace (const char*);
+/* Dereference a branch trace instruction iterator. Return a pointer to the
+ instruction the iterator points to or NULL if the interator does not point
+ to a valid instruction. */
+extern const struct btrace_insn *
+btrace_insn_get (const struct btrace_insn_iterator *);
+
+/* Return the instruction number for a branch trace iterator. Returns zero
+ if the iterator does not point to a valid instruction. */
+extern unsigned int btrace_insn_number (const struct btrace_insn_iterator *);
+
+/* Initialize a branch trace instruction iterator to point to the begin/end of
+ the branch trace. Throws an error if there is no branch trace. */
+extern void btrace_insn_begin (struct btrace_insn_iterator *,
+ struct btrace_thread_info *);
+extern void btrace_insn_end (struct btrace_insn_iterator *,
+ struct btrace_thread_info *);
+
+/* Increment/decrement a branch trace instruction iterator. Return the number
+ of instructions by which the instruction iterator has been advanced.
+ Returns zero, if the operation failed. */
+extern unsigned int btrace_insn_next (struct btrace_insn_iterator *,
+ unsigned int stride);
+extern unsigned int btrace_insn_prev (struct btrace_insn_iterator *,
+ unsigned int stride);
+
+/* Compare two branch trace instruction iterators.
+ Return a negative number if LHS < RHS.
+ Return zero if LHS == RHS.
+ Return a positive number if LHS > RHS. */
+extern int btrace_insn_cmp (const struct btrace_insn_iterator *lhs,
+ const struct btrace_insn_iterator *rhs);
+
+/* Find an instruction in the function branch trace by its number.
+ If the instruction is found, initialize the branch trace instruction
+ iterator to point to this instruction and return 1.
+ Return 0, otherwise. */
+extern int btrace_find_insn_by_number (struct btrace_insn_iterator *,
+ const struct btrace_thread_info *,
+ unsigned int number);
+
+/* Find a function in the function branch trace by its number.
+ Return a pointer to that function or NULL if no such function is found. */
+extern struct btrace_function *
+btrace_find_function_by_number (const struct btrace_thread_info *,
+ unsigned int number);
+
+/* Set the branch trace instruction history to [BEGIN; END). */
+extern void btrace_set_insn_history (struct btrace_thread_info *,
+ struct btrace_insn_iterator *begin,
+ struct btrace_insn_iterator *end);
+
+/* Set the branch trace function call history to [BEGIN; END). */
+extern void btrace_set_call_history (struct btrace_thread_info *,
+ struct btrace_function *begin,
+ struct btrace_function *end);
+
#endif /* BTRACE_H */
diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
index 8fb413e..e2506a8 100644
--- a/gdb/record-btrace.c
+++ b/gdb/record-btrace.c
@@ -74,7 +74,7 @@ require_btrace (void)
btinfo = &tp->btrace;
- if (VEC_empty (btrace_inst_s, btinfo->itrace))
+ if (btinfo->begin == NULL)
error (_("No trace."));
return btinfo;
@@ -205,6 +205,7 @@ static void
record_btrace_info (void)
{
struct btrace_thread_info *btinfo;
+ struct btrace_function *bfun;
struct thread_info *tp;
unsigned int insts, funcs;
@@ -217,8 +218,15 @@ record_btrace_info (void)
btrace_fetch (tp);
btinfo = &tp->btrace;
- insts = VEC_length (btrace_inst_s, btinfo->itrace);
- funcs = VEC_length (btrace_func_s, btinfo->ftrace);
+ bfun = btinfo->end;
+ insts = 0;
+ funcs = 0;
+
+ if (bfun != NULL)
+ {
+ funcs = bfun->number;
+ insts = bfun->insn_offset + VEC_length (btrace_insn_s, bfun->insn);
+ }
printf_unfiltered (_("Recorded %u instructions in %u functions for thread "
"%d (%s).\n"), insts, funcs, tp->num,
@@ -236,27 +244,32 @@ ui_out_field_uint (struct ui_out *uiout, const char *fld, unsigned int val)
/* Disassemble a section of the recorded instruction trace. */
static void
-btrace_insn_history (struct btrace_thread_info *btinfo, struct ui_out *uiout,
- unsigned int begin, unsigned int end, int flags)
+btrace_insn_history (struct ui_out *uiout,
+ const struct btrace_insn_iterator *begin,
+ const struct btrace_insn_iterator *end, int flags)
{
struct gdbarch *gdbarch;
- struct btrace_inst *inst;
- unsigned int idx;
+ struct btrace_insn *inst;
+ struct btrace_insn_iterator it;
- DEBUG ("itrace (0x%x): [%u; %u[", flags, begin, end);
+ DEBUG ("itrace (0x%x): [%u; %u)", flags, btrace_insn_number (begin),
+ btrace_insn_number (end));
gdbarch = target_gdbarch ();
- for (idx = begin; VEC_iterate (btrace_inst_s, btinfo->itrace, idx, inst)
- && idx < end; ++idx)
+ for (it = *begin; btrace_insn_cmp (&it, end) < 0; btrace_insn_next (&it, 1))
{
+ const struct btrace_insn *insn;
+
+ insn = btrace_insn_get (&it);
+
/* Print the instruction index. */
- ui_out_field_uint (uiout, "index", idx);
+ ui_out_field_uint (uiout, "index", btrace_insn_number (&it));
ui_out_text (uiout, "\t");
/* Disassembly with '/m' flag may not produce the expected result.
See PR gdb/11833. */
- gdb_disassembly (gdbarch, uiout, NULL, flags, 1, inst->pc, inst->pc + 1);
+ gdb_disassembly (gdbarch, uiout, NULL, flags, 1, insn->pc, insn->pc + 1);
}
}
@@ -266,72 +279,60 @@ static void
record_btrace_insn_history (int size, int flags)
{
struct btrace_thread_info *btinfo;
+ struct btrace_insn_history *history;
+ struct btrace_insn_iterator begin, end;
struct cleanup *uiout_cleanup;
struct ui_out *uiout;
- unsigned int context, last, begin, end;
+ unsigned int context, covered;
uiout = current_uiout;
uiout_cleanup = make_cleanup_ui_out_tuple_begin_end (uiout,
"insn history");
btinfo = require_btrace ();
- last = VEC_length (btrace_inst_s, btinfo->itrace);
-
context = abs (size);
- begin = btinfo->insn_iterator.begin;
- end = btinfo->insn_iterator.end;
-
- DEBUG ("insn-history (0x%x): %d, prev: [%u; %u[", flags, size, begin, end);
-
if (context == 0)
error (_("Bad record instruction-history-size."));
- /* We start at the end. */
- if (end < begin)
+ history = btinfo->insn_history;
+ if (history == NULL)
{
- /* Truncate the context, if necessary. */
- context = min (context, last);
+ /* No matter the direction, we start with the tail of the trace. */
+ btrace_insn_end (&begin, btinfo);
+ end = begin;
- end = last;
- begin = end - context;
+ covered = btrace_insn_prev (&begin, context);
}
- else if (size < 0)
+ else
{
- if (begin == 0)
- {
- printf_unfiltered (_("At the start of the branch trace record.\n"));
+ begin = history->begin;
+ end = history->end;
- btinfo->insn_iterator.end = 0;
- return;
- }
-
- /* Truncate the context, if necessary. */
- context = min (context, begin);
+ DEBUG ("insn-history (0x%x): %d, prev: [%u; %u)", flags, size,
+ btrace_insn_number (&begin), btrace_insn_number (&end));
- end = begin;
- begin -= context;
- }
- else
- {
- if (end == last)
+ if (size < 0)
{
- printf_unfiltered (_("At the end of the branch trace record.\n"));
-
- btinfo->insn_iterator.begin = last;
- return;
+ end = begin;
+ covered = btrace_insn_prev (&begin, context);
+ }
+ else
+ {
+ begin = end;
+ covered = btrace_insn_next (&end, context);
}
-
- /* Truncate the context, if necessary. */
- context = min (context, last - end);
-
- begin = end;
- end += context;
}
- btrace_insn_history (btinfo, uiout, begin, end, flags);
-
- btinfo->insn_iterator.begin = begin;
- btinfo->insn_iterator.end = end;
+ if (covered > 0)
+ btrace_insn_history (uiout, &begin, &end, flags);
+ else
+ {
+ if (size < 0)
+ printf_unfiltered (_("At the start of the branch trace record.\n"));
+ else
+ printf_unfiltered (_("At the end of the branch trace record.\n"));
+ }
+ btrace_set_insn_history (btinfo, &begin, &end);
do_cleanups (uiout_cleanup);
}
@@ -341,39 +342,41 @@ static void
record_btrace_insn_history_range (ULONGEST from, ULONGEST to, int flags)
{
struct btrace_thread_info *btinfo;
+ struct btrace_insn_history *history;
+ struct btrace_insn_iterator begin, end;
struct cleanup *uiout_cleanup;
struct ui_out *uiout;
- unsigned int last, begin, end;
+ unsigned int low, high;
+ int found;
uiout = current_uiout;
uiout_cleanup = make_cleanup_ui_out_tuple_begin_end (uiout,
"insn history");
- btinfo = require_btrace ();
- last = VEC_length (btrace_inst_s, btinfo->itrace);
-
- begin = (unsigned int) from;
- end = (unsigned int) to;
+ low = (unsigned int) from;
+ high = (unsigned int) to;
- DEBUG ("insn-history (0x%x): [%u; %u[", flags, begin, end);
+ DEBUG ("insn-history (0x%x): [%u; %u)", flags, low, high);
/* Check for wrap-arounds. */
- if (begin != from || end != to)
+ if (low != from || high != to)
error (_("Bad range."));
- if (end <= begin)
+ if (high <= low)
error (_("Bad range."));
- if (last <= begin)
- error (_("Range out of bounds."));
+ btinfo = require_btrace ();
- /* Truncate the range, if necessary. */
- if (last < end)
- end = last;
+ found = btrace_find_insn_by_number (&begin, btinfo, low);
+ if (found == 0)
+ error (_("Range out of bounds."));
- btrace_insn_history (btinfo, uiout, begin, end, flags);
+ /* Silently truncate the range, if necessary. */
+ found = btrace_find_insn_by_number (&end, btinfo, high);
+ if (found == 0)
+ btrace_insn_end (&end, btinfo);
- btinfo->insn_iterator.begin = begin;
- btinfo->insn_iterator.end = end;
+ btrace_insn_history (uiout, &begin, &end, flags);
+ btrace_set_insn_history (btinfo, &begin, &end);
do_cleanups (uiout_cleanup);
}
@@ -412,23 +415,27 @@ record_btrace_insn_history_from (ULONGEST from, int size, int flags)
/* Print the instruction number range for a function call history line. */
static void
-btrace_func_history_insn_range (struct ui_out *uiout, struct btrace_func *bfun)
+btrace_call_history_insn_range (struct ui_out *uiout,
+ const struct btrace_function *bfun)
{
- ui_out_field_uint (uiout, "insn begin", bfun->ibegin);
+ unsigned int begin, end;
- if (bfun->ibegin == bfun->iend)
- return;
+ begin = bfun->insn_offset;
+ end = begin + VEC_length (btrace_insn_s, bfun->insn);
+ ui_out_field_uint (uiout, "insn begin", begin);
ui_out_text (uiout, "-");
- ui_out_field_uint (uiout, "insn end", bfun->iend);
+ ui_out_field_uint (uiout, "insn end", end);
}
/* Print the source line information for a function call history line. */
static void
-btrace_func_history_src_line (struct ui_out *uiout, struct btrace_func *bfun)
+btrace_call_history_src_line (struct ui_out *uiout,
+ const struct btrace_function *bfun)
{
struct symbol *sym;
+ int begin, end;
sym = bfun->sym;
if (sym == NULL)
@@ -437,130 +444,183 @@ btrace_func_history_src_line (struct ui_out *uiout, struct btrace_func *bfun)
ui_out_field_string (uiout, "file",
symtab_to_filename_for_display (sym->symtab));
- if (bfun->lend == 0)
+ begin = bfun->lbegin;
+ end = bfun->lend;
+
+ if (end == 0)
return;
ui_out_text (uiout, ":");
- ui_out_field_int (uiout, "min line", bfun->lbegin);
+ ui_out_field_int (uiout, "min line", begin);
- if (bfun->lend == bfun->lbegin)
+ if (end == begin)
return;
ui_out_text (uiout, "-");
- ui_out_field_int (uiout, "max line", bfun->lend);
+ ui_out_field_int (uiout, "max line", end);
}
/* Disassemble a section of the recorded function trace. */
static void
-btrace_func_history (struct btrace_thread_info *btinfo, struct ui_out *uiout,
- unsigned int begin, unsigned int end,
+btrace_call_history (struct ui_out *uiout,
+ const struct btrace_function *begin,
+ const struct btrace_function *end,
enum record_print_flag flags)
{
- struct btrace_func *bfun;
- unsigned int idx;
+ const struct btrace_function *bfun;
- DEBUG ("ftrace (0x%x): [%u; %u[", flags, begin, end);
+ DEBUG ("ftrace (0x%x): [%u; %u)", flags, begin->number, end->number);
- for (idx = begin; VEC_iterate (btrace_func_s, btinfo->ftrace, idx, bfun)
- && idx < end; ++idx)
+ for (bfun = begin; bfun != end; bfun = bfun->flow.next)
{
+ struct minimal_symbol *msym;
+ struct symbol *sym;
+
+ msym = bfun->msym;
+ sym = bfun->sym;
+
/* Print the function index. */
- ui_out_field_uint (uiout, "index", idx);
+ ui_out_field_uint (uiout, "index", bfun->number);
ui_out_text (uiout, "\t");
if ((flags & record_print_insn_range) != 0)
{
- btrace_func_history_insn_range (uiout, bfun);
+ btrace_call_history_insn_range (uiout, bfun);
ui_out_text (uiout, "\t");
}
if ((flags & record_print_src_line) != 0)
{
- btrace_func_history_src_line (uiout, bfun);
+ btrace_call_history_src_line (uiout, bfun);
ui_out_text (uiout, "\t");
}
- if (bfun->sym != NULL)
- ui_out_field_string (uiout, "function", SYMBOL_PRINT_NAME (bfun->sym));
- else if (bfun->msym != NULL)
- ui_out_field_string (uiout, "function", SYMBOL_PRINT_NAME (bfun->msym));
+ if (sym != NULL)
+ ui_out_field_string (uiout, "function", SYMBOL_PRINT_NAME (sym));
+ else if (msym != NULL)
+ ui_out_field_string (uiout, "function", SYMBOL_PRINT_NAME (msym));
+
ui_out_text (uiout, "\n");
}
}
+/* Decrement a btrace function iterator. Return the number of functions
+ by which the iterator has been decremented.
+ Returns zero, if the operation failed. */
+
+static unsigned int
+btrace_func_prev (struct btrace_function **it, unsigned int stride)
+{
+ struct btrace_function *bfun;
+ unsigned int covered;
+
+ bfun = *it;
+ covered = 0;
+ while (covered < stride)
+ {
+ struct btrace_function *prev;
+
+ prev = bfun->flow.prev;
+ if (prev == NULL)
+ break;
+
+ bfun = prev;
+ covered += 1;
+ }
+
+ *it = bfun;
+ return covered;
+}
+
+/* Increment a btrace function iterator. Return the number of functions
+ by which the iterator has been incremented.
+ Returns zero, if the operation failed. */
+
+static unsigned int
+btrace_func_next (struct btrace_function **it, unsigned int stride)
+{
+ struct btrace_function *bfun;
+ unsigned int covered;
+
+ bfun = *it;
+ covered = 0;
+ while (covered < stride)
+ {
+ struct btrace_function *next;
+
+ next = bfun->flow.next;
+ if (next == NULL)
+ break;
+
+ bfun = next;
+ covered += 1;
+ }
+
+ *it = bfun;
+ return covered;
+}
+
/* The to_call_history method of target record-btrace. */
static void
record_btrace_call_history (int size, int flags)
{
struct btrace_thread_info *btinfo;
+ struct btrace_call_history *history;
+ struct btrace_function *begin, *end;
struct cleanup *uiout_cleanup;
struct ui_out *uiout;
- unsigned int context, last, begin, end;
+ unsigned int context, covered;
uiout = current_uiout;
uiout_cleanup = make_cleanup_ui_out_tuple_begin_end (uiout,
"insn history");
- btinfo = require_btrace ();
- last = VEC_length (btrace_func_s, btinfo->ftrace);
-
context = abs (size);
- begin = btinfo->func_iterator.begin;
- end = btinfo->func_iterator.end;
-
- DEBUG ("func-history (0x%x): %d, prev: [%u; %u[", flags, size, begin, end);
-
if (context == 0)
error (_("Bad record function-call-history-size."));
- /* We start at the end. */
- if (end < begin)
+ btinfo = require_btrace ();
+ history = btinfo->call_history;
+ if (history == NULL)
{
- /* Truncate the context, if necessary. */
- context = min (context, last);
+ /* No matter the direction, we start with the tail of the trace. */
+ begin = btinfo->end;
+ end = begin;
- end = last;
- begin = end - context;
+ covered = btrace_func_prev (&begin, context);
}
- else if (size < 0)
+ else
{
- if (begin == 0)
- {
- printf_unfiltered (_("At the start of the branch trace record.\n"));
+ begin = history->begin;
+ end = history->end;
- btinfo->func_iterator.end = 0;
- return;
- }
-
- /* Truncate the context, if necessary. */
- context = min (context, begin);
+ DEBUG ("call-history (0x%x): %d, prev: [%u; %u[", flags, size,
+ begin->number, end->number);
- end = begin;
- begin -= context;
- }
- else
- {
- if (end == last)
+ if (size < 0)
{
- printf_unfiltered (_("At the end of the branch trace record.\n"));
-
- btinfo->func_iterator.begin = last;
- return;
+ end = begin;
+ covered = btrace_func_prev (&begin, context);
+ }
+ else
+ {
+ begin = end;
+ covered = btrace_func_next (&end, context);
}
-
- /* Truncate the context, if necessary. */
- context = min (context, last - end);
-
- begin = end;
- end += context;
}
- btrace_func_history (btinfo, uiout, begin, end, flags);
-
- btinfo->func_iterator.begin = begin;
- btinfo->func_iterator.end = end;
+ if (covered > 0)
+ btrace_call_history (uiout, begin, end, flags);
+ else
+ {
+ if (size < 0)
+ printf_unfiltered (_("At the start of the branch trace record.\n"));
+ else
+ printf_unfiltered (_("At the end of the branch trace record.\n"));
+ }
+ btrace_set_call_history (btinfo, begin, end);
do_cleanups (uiout_cleanup);
}
@@ -570,39 +630,40 @@ static void
record_btrace_call_history_range (ULONGEST from, ULONGEST to, int flags)
{
struct btrace_thread_info *btinfo;
+ struct btrace_call_history *history;
+ struct btrace_function *begin, *end;
struct cleanup *uiout_cleanup;
struct ui_out *uiout;
- unsigned int last, begin, end;
+ unsigned int low, high;
uiout = current_uiout;
uiout_cleanup = make_cleanup_ui_out_tuple_begin_end (uiout,
"func history");
- btinfo = require_btrace ();
- last = VEC_length (btrace_func_s, btinfo->ftrace);
-
- begin = (unsigned int) from;
- end = (unsigned int) to;
+ low = (unsigned int) from;
+ high = (unsigned int) to;
- DEBUG ("func-history (0x%x): [%u; %u[", flags, begin, end);
+ DEBUG ("call-history (0x%x): [%u; %u[", flags, low, high);
/* Check for wrap-arounds. */
- if (begin != from || end != to)
+ if (low != from || high != to)
error (_("Bad range."));
- if (end <= begin)
+ if (high <= low)
error (_("Bad range."));
- if (last <= begin)
- error (_("Range out of bounds."));
+ btinfo = require_btrace ();
- /* Truncate the range, if necessary. */
- if (last < end)
- end = last;
+ begin = btrace_find_function_by_number (btinfo, low);
+ if (begin == NULL)
+ error (_("Range out of bounds."));
- btrace_func_history (btinfo, uiout, begin, end, flags);
+ /* Silently truncate the range, if necessary. */
+ end = btrace_find_function_by_number (btinfo, high);
+ if (end == NULL)
+ end = btinfo->end;
- btinfo->func_iterator.begin = begin;
- btinfo->func_iterator.end = end;
+ btrace_call_history (uiout, begin, end, flags);
+ btrace_set_call_history (btinfo, begin, end);
do_cleanups (uiout_cleanup);
}
diff --git a/gdb/testsuite/gdb.btrace/function_call_history.exp b/gdb/testsuite/gdb.btrace/function_call_history.exp
index 97447e1..7658637 100644
--- a/gdb/testsuite/gdb.btrace/function_call_history.exp
+++ b/gdb/testsuite/gdb.btrace/function_call_history.exp
@@ -204,16 +204,18 @@ set bp_location [gdb_get_line_number "bp.2" $testfile.c]
gdb_breakpoint $bp_location
gdb_continue_to_breakpoint "cont to $bp_location" ".*$testfile.c:$bp_location.*"
-# at this point we expect to have main, fib, ..., fib, main, where fib occurs 8 times,
-# so we limit the output to only show the latest 10 function calls
-gdb_test_no_output "set record function-call-history-size 10"
-set message "show recursive function call history"
-gdb_test_multiple "record function-call-history" $message {
- -re "13\tmain\r\n14\tfib\r\n15\tfib\r\n16\tfib\r\n17\tfib\r\n18\tfib\r\n19\tfib\r\n20\tfib\r\n21\tfib\r\n22 main\r\n$gdb_prompt $" {
- pass $message
- }
- -re "13\tinc\r\n14\tmain\r\n15\tinc\r\n16\tmain\r\n17\tinc\r\n18\tmain\r\n19\tinc\r\n20\tmain\r\n21\tfib\r\n22\tmain\r\n$gdb_prompt $" {
- # recursive function calls appear only as 1 call
- kfail "gdb/15240" $message
- }
-}
+# at this point we expect to have main, fib, ..., fib, main, where fib occurs 9 times,
+# so we limit the output to only show the latest 11 function calls
+gdb_test_no_output "set record function-call-history-size 11"
+gdb_test "record function-call-history" "
+20\tmain\r
+21\tfib\r
+22\tfib\r
+23\tfib\r
+24\tfib\r
+25\tfib\r
+26\tfib\r
+27\tfib\r
+28\tfib\r
+29\tfib\r
+30\tmain" "show recursive function call history"
--
1.7.1