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:
Markus Metzger
2014-01-30 09:51:10 +01:00
parent afb778a2a8
commit 31fd9caad9
15 changed files with 434 additions and 86 deletions

View File

@@ -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),