forked from Imagelibrary/binutils-gdb
record-btrace: indicate gaps
Indicate gaps in the trace due to decode errors. Internally, a gap is represented as a btrace function segment without instructions and with a non-zero format-specific error code. Show the gap when traversing the instruction or function call history. Also indicate gaps in "info record". It looks like this: (gdb) info record Active record target: record-btrace Recording format: Branch Trace Store. Buffer size: 64KB. Recorded 32 instructions in 5 functions (1 gaps) for thread 1 (process 7182). (gdb) record function-call-history /cli 1 fib inst 1,9 at src/fib.c:9,14 2 fib inst 10,20 at src/fib.c:6,14 3 [decode error (1): instruction overflow] 4 fib inst 21,28 at src/fib.c:11,14 5 fib inst 29,33 at src/fib.c:6,9 (gdb) record instruction-history 20,22 20 0x000000000040062f <fib+47>: sub $0x1,%rax [decode error (1): instruction overflow] 21 0x0000000000400613 <fib+19>: add $0x1,%rax 22 0x0000000000400617 <fib+23>: mov %rax,0x200a3a(%rip) (gdb) Gaps are ignored during reverse execution and replay. 2015-02-09 Markus Metzger <markus.t.metzger@intel.com> * btrace.c (ftrace_find_call): Skip gaps. (ftrace_new_function): Initialize level. (ftrace_new_call, ftrace_new_tailcall, ftrace_new_return) (ftrace_new_switch): Update level computation. (ftrace_new_gap): New. (ftrace_update_function): Create new function after gap. (btrace_compute_ftrace_bts): Create gap on error. (btrace_stitch_bts): Update parameters. Clear trace if it becomes empty. (btrace_stitch_trace): Update parameters. Update callers. (btrace_clear): Reset the number of gaps. (btrace_insn_get): Return NULL if the iterator points to a gap. (btrace_insn_number): Return zero if the iterator points to a gap. (btrace_insn_end): Allow gaps at the end. (btrace_insn_next, btrace_insn_prev, btrace_insn_cmp): Handle gaps. (btrace_find_insn_by_number): Assert that the found iterator does not point to a gap. (btrace_call_next, btrace_call_prev): Assert that the last function is not a gap. * btrace.h (btrace_bts_error): New. (btrace_function): Update comment. (btrace_function) <insn, insn_offset, number>: Update comment. (btrace_function) <errcode>: New. (btrace_thread_info) <ngaps>: New. (btrace_thread_info) <replay>: Update comment. (btrace_insn_get): Update comment. * record-btrace.c (btrace_ui_out_decode_error): New. (record_btrace_info): Print number of gaps. (btrace_insn_history, btrace_call_history): Call btrace_ui_out_decode_error for gaps. (record_btrace_step_thread, record_btrace_start_replaying): Skip gaps. testsuite/ * gdb.btrace/buffer-size.exp: Update "info record" output. * gdb.btrace/delta.exp: Update "info record" output. * gdb.btrace/enable.exp: Update "info record" output. * gdb.btrace/finish.exp: Update "info record" output. * gdb.btrace/instruction_history.exp: Update "info record" output. * gdb.btrace/next.exp: Update "info record" output. * gdb.btrace/nexti.exp: Update "info record" output. * gdb.btrace/step.exp: Update "info record" output. * gdb.btrace/stepi.exp: Update "info record" output. * gdb.btrace/nohist.exp: Update "info record" output.
This commit is contained in:
@@ -366,7 +366,7 @@ record_btrace_info (struct target_ops *self)
|
||||
struct btrace_thread_info *btinfo;
|
||||
const struct btrace_config *conf;
|
||||
struct thread_info *tp;
|
||||
unsigned int insns, calls;
|
||||
unsigned int insns, calls, gaps;
|
||||
|
||||
DEBUG ("info");
|
||||
|
||||
@@ -384,6 +384,7 @@ record_btrace_info (struct target_ops *self)
|
||||
|
||||
insns = 0;
|
||||
calls = 0;
|
||||
gaps = 0;
|
||||
|
||||
if (!btrace_is_empty (tp))
|
||||
{
|
||||
@@ -395,19 +396,86 @@ record_btrace_info (struct target_ops *self)
|
||||
calls = btrace_call_number (&call);
|
||||
|
||||
btrace_insn_end (&insn, btinfo);
|
||||
btrace_insn_prev (&insn, 1);
|
||||
|
||||
insns = btrace_insn_number (&insn);
|
||||
if (insns != 0)
|
||||
{
|
||||
/* The last instruction does not really belong to the trace. */
|
||||
insns -= 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned int steps;
|
||||
|
||||
/* Skip gaps at the end. */
|
||||
do
|
||||
{
|
||||
steps = btrace_insn_prev (&insn, 1);
|
||||
if (steps == 0)
|
||||
break;
|
||||
|
||||
insns = btrace_insn_number (&insn);
|
||||
}
|
||||
while (insns == 0);
|
||||
}
|
||||
|
||||
gaps = btinfo->ngaps;
|
||||
}
|
||||
|
||||
printf_unfiltered (_("Recorded %u instructions in %u functions for thread "
|
||||
"%d (%s).\n"), insns, calls, tp->num,
|
||||
target_pid_to_str (tp->ptid));
|
||||
printf_unfiltered (_("Recorded %u instructions in %u functions (%u gaps) "
|
||||
"for thread %d (%s).\n"), insns, calls, gaps,
|
||||
tp->num, target_pid_to_str (tp->ptid));
|
||||
|
||||
if (btrace_is_replaying (tp))
|
||||
printf_unfiltered (_("Replay in progress. At instruction %u.\n"),
|
||||
btrace_insn_number (btinfo->replay));
|
||||
}
|
||||
|
||||
/* Print a decode error. */
|
||||
|
||||
static void
|
||||
btrace_ui_out_decode_error (struct ui_out *uiout, int errcode,
|
||||
enum btrace_format format)
|
||||
{
|
||||
const char *errstr;
|
||||
int is_error;
|
||||
|
||||
errstr = _("unknown");
|
||||
is_error = 1;
|
||||
|
||||
switch (format)
|
||||
{
|
||||
default:
|
||||
break;
|
||||
|
||||
case BTRACE_FORMAT_BTS:
|
||||
switch (errcode)
|
||||
{
|
||||
default:
|
||||
break;
|
||||
|
||||
case BDE_BTS_OVERFLOW:
|
||||
errstr = _("instruction overflow");
|
||||
break;
|
||||
|
||||
case BDE_BTS_INSN_SIZE:
|
||||
errstr = _("unknown instruction");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
ui_out_text (uiout, _("["));
|
||||
if (is_error)
|
||||
{
|
||||
ui_out_text (uiout, _("decode error ("));
|
||||
ui_out_field_int (uiout, "errcode", errcode);
|
||||
ui_out_text (uiout, _("): "));
|
||||
}
|
||||
ui_out_text (uiout, errstr);
|
||||
ui_out_text (uiout, _("]\n"));
|
||||
}
|
||||
|
||||
/* Print an unsigned int. */
|
||||
|
||||
static void
|
||||
@@ -420,6 +488,7 @@ ui_out_field_uint (struct ui_out *uiout, const char *fld, unsigned int val)
|
||||
|
||||
static void
|
||||
btrace_insn_history (struct ui_out *uiout,
|
||||
const struct btrace_thread_info *btinfo,
|
||||
const struct btrace_insn_iterator *begin,
|
||||
const struct btrace_insn_iterator *end, int flags)
|
||||
{
|
||||
@@ -437,13 +506,30 @@ btrace_insn_history (struct ui_out *uiout,
|
||||
|
||||
insn = btrace_insn_get (&it);
|
||||
|
||||
/* Print the instruction index. */
|
||||
ui_out_field_uint (uiout, "index", btrace_insn_number (&it));
|
||||
ui_out_text (uiout, "\t");
|
||||
/* A NULL instruction indicates a gap in the trace. */
|
||||
if (insn == NULL)
|
||||
{
|
||||
const struct btrace_config *conf;
|
||||
|
||||
/* Disassembly with '/m' flag may not produce the expected result.
|
||||
See PR gdb/11833. */
|
||||
gdb_disassembly (gdbarch, uiout, NULL, flags, 1, insn->pc, insn->pc + 1);
|
||||
conf = btrace_conf (btinfo);
|
||||
|
||||
/* We have trace so we must have a configuration. */
|
||||
gdb_assert (conf != NULL);
|
||||
|
||||
btrace_ui_out_decode_error (uiout, it.function->errcode,
|
||||
conf->format);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Print the instruction index. */
|
||||
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, insn->pc,
|
||||
insn->pc + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -520,7 +606,7 @@ record_btrace_insn_history (struct target_ops *self, int size, int flags)
|
||||
}
|
||||
|
||||
if (covered > 0)
|
||||
btrace_insn_history (uiout, &begin, &end, flags);
|
||||
btrace_insn_history (uiout, btinfo, &begin, &end, flags);
|
||||
else
|
||||
{
|
||||
if (size < 0)
|
||||
@@ -580,7 +666,7 @@ record_btrace_insn_history_range (struct target_ops *self,
|
||||
btrace_insn_next (&end, 1);
|
||||
}
|
||||
|
||||
btrace_insn_history (uiout, &begin, &end, flags);
|
||||
btrace_insn_history (uiout, btinfo, &begin, &end, flags);
|
||||
btrace_set_insn_history (btinfo, &begin, &end);
|
||||
|
||||
do_cleanups (uiout_cleanup);
|
||||
@@ -721,6 +807,21 @@ btrace_call_history (struct ui_out *uiout,
|
||||
ui_out_field_uint (uiout, "index", bfun->number);
|
||||
ui_out_text (uiout, "\t");
|
||||
|
||||
/* Indicate gaps in the trace. */
|
||||
if (bfun->errcode != 0)
|
||||
{
|
||||
const struct btrace_config *conf;
|
||||
|
||||
conf = btrace_conf (btinfo);
|
||||
|
||||
/* We have trace so we must have a configuration. */
|
||||
gdb_assert (conf != NULL);
|
||||
|
||||
btrace_ui_out_decode_error (uiout, bfun->errcode, conf->format);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((flags & RECORD_PRINT_INDENT_CALLS) != 0)
|
||||
{
|
||||
int level = bfun->level + btinfo->level, i;
|
||||
@@ -1534,6 +1635,16 @@ record_btrace_start_replaying (struct thread_info *tp)
|
||||
replay = xmalloc (sizeof (*replay));
|
||||
btrace_insn_end (replay, btinfo);
|
||||
|
||||
/* Skip gaps at the end of the trace. */
|
||||
while (btrace_insn_get (replay) == NULL)
|
||||
{
|
||||
unsigned int steps;
|
||||
|
||||
steps = btrace_insn_prev (replay, 1);
|
||||
if (steps == 0)
|
||||
error (_("No trace."));
|
||||
}
|
||||
|
||||
/* We're not replaying, yet. */
|
||||
gdb_assert (btinfo->replay == NULL);
|
||||
btinfo->replay = replay;
|
||||
@@ -1729,9 +1840,17 @@ record_btrace_step_thread (struct thread_info *tp)
|
||||
if (replay == NULL)
|
||||
return btrace_step_no_history ();
|
||||
|
||||
/* We are always able to step at least once. */
|
||||
steps = btrace_insn_next (replay, 1);
|
||||
gdb_assert (steps == 1);
|
||||
/* Skip gaps during replay. */
|
||||
do
|
||||
{
|
||||
steps = btrace_insn_next (replay, 1);
|
||||
if (steps == 0)
|
||||
{
|
||||
record_btrace_stop_replaying (tp);
|
||||
return btrace_step_no_history ();
|
||||
}
|
||||
}
|
||||
while (btrace_insn_get (replay) == NULL);
|
||||
|
||||
/* Determine the end of the instruction trace. */
|
||||
btrace_insn_end (&end, btinfo);
|
||||
@@ -1747,10 +1866,16 @@ record_btrace_step_thread (struct thread_info *tp)
|
||||
if (replay == NULL)
|
||||
replay = record_btrace_start_replaying (tp);
|
||||
|
||||
/* If we can't step any further, we reached the end of the history. */
|
||||
steps = btrace_insn_prev (replay, 1);
|
||||
if (steps == 0)
|
||||
return btrace_step_no_history ();
|
||||
/* If we can't step any further, we reached the end of the history.
|
||||
Skip gaps during replay. */
|
||||
do
|
||||
{
|
||||
steps = btrace_insn_prev (replay, 1);
|
||||
if (steps == 0)
|
||||
return btrace_step_no_history ();
|
||||
|
||||
}
|
||||
while (btrace_insn_get (replay) == NULL);
|
||||
|
||||
return btrace_step_stopped ();
|
||||
|
||||
@@ -1769,9 +1894,19 @@ record_btrace_step_thread (struct thread_info *tp)
|
||||
{
|
||||
const struct btrace_insn *insn;
|
||||
|
||||
/* We are always able to step at least once. */
|
||||
steps = btrace_insn_next (replay, 1);
|
||||
gdb_assert (steps == 1);
|
||||
/* Skip gaps during replay. */
|
||||
do
|
||||
{
|
||||
steps = btrace_insn_next (replay, 1);
|
||||
if (steps == 0)
|
||||
{
|
||||
record_btrace_stop_replaying (tp);
|
||||
return btrace_step_no_history ();
|
||||
}
|
||||
|
||||
insn = btrace_insn_get (replay);
|
||||
}
|
||||
while (insn == NULL);
|
||||
|
||||
/* We stop replaying if we reached the end of the trace. */
|
||||
if (btrace_insn_cmp (replay, &end) == 0)
|
||||
@@ -1780,9 +1915,6 @@ record_btrace_step_thread (struct thread_info *tp)
|
||||
return btrace_step_no_history ();
|
||||
}
|
||||
|
||||
insn = btrace_insn_get (replay);
|
||||
gdb_assert (insn);
|
||||
|
||||
DEBUG ("stepping %d (%s) ... %s", tp->num,
|
||||
target_pid_to_str (tp->ptid),
|
||||
core_addr_to_string_nz (insn->pc));
|
||||
@@ -1803,13 +1935,17 @@ record_btrace_step_thread (struct thread_info *tp)
|
||||
{
|
||||
const struct btrace_insn *insn;
|
||||
|
||||
/* If we can't step any further, we're done. */
|
||||
steps = btrace_insn_prev (replay, 1);
|
||||
if (steps == 0)
|
||||
return btrace_step_no_history ();
|
||||
/* If we can't step any further, we reached the end of the history.
|
||||
Skip gaps during replay. */
|
||||
do
|
||||
{
|
||||
steps = btrace_insn_prev (replay, 1);
|
||||
if (steps == 0)
|
||||
return btrace_step_no_history ();
|
||||
|
||||
insn = btrace_insn_get (replay);
|
||||
gdb_assert (insn);
|
||||
insn = btrace_insn_get (replay);
|
||||
}
|
||||
while (insn == NULL);
|
||||
|
||||
DEBUG ("reverse-stepping %d (%s) ... %s", tp->num,
|
||||
target_pid_to_str (tp->ptid),
|
||||
|
||||
Reference in New Issue
Block a user