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

@ -1,3 +1,38 @@
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.
2015-02-09 Markus Metzger <markus.t.metzger@intel.com>
* common/btrace-common.h (btrace_cpu_vendor, btrace_cpu): New.

View File

@ -223,6 +223,7 @@ ftrace_new_function (struct btrace_function *prev,
bfun->number = prev->number + 1;
bfun->insn_offset = (prev->insn_offset
+ VEC_length (btrace_insn_s, prev->insn));
bfun->level = prev->level;
}
return bfun;
@ -276,7 +277,7 @@ ftrace_new_call (struct btrace_function *caller,
bfun = ftrace_new_function (caller, mfun, fun);
bfun->up = caller;
bfun->level = caller->level + 1;
bfun->level += 1;
ftrace_debug (bfun, "new call");
@ -296,7 +297,7 @@ ftrace_new_tailcall (struct btrace_function *caller,
bfun = ftrace_new_function (caller, mfun, fun);
bfun->up = caller;
bfun->level = caller->level + 1;
bfun->level += 1;
bfun->flags |= BFUN_UP_LINKS_TO_TAILCALL;
ftrace_debug (bfun, "new tail call");
@ -336,8 +337,9 @@ ftrace_find_call (struct btrace_function *bfun)
{
struct btrace_insn *last;
/* We do not allow empty function segments. */
gdb_assert (!VEC_empty (btrace_insn_s, bfun->insn));
/* Skip gaps. */
if (bfun->errcode != 0)
continue;
last = VEC_last (btrace_insn_s, bfun->insn);
@ -438,14 +440,34 @@ ftrace_new_switch (struct btrace_function *prev,
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");
return bfun;
}
/* Add a new function segment for a gap in the trace due to a decode error.
PREV is the chronologically preceding function segment.
ERRCODE is the format-specific error code. */
static struct btrace_function *
ftrace_new_gap (struct btrace_function *prev, int errcode)
{
struct btrace_function *bfun;
/* We hijack prev if it was empty. */
if (prev != NULL && prev->errcode == 0
&& VEC_empty (btrace_insn_s, prev->insn))
bfun = prev;
else
bfun = ftrace_new_function (prev, NULL, NULL);
bfun->errcode = errcode;
ftrace_debug (bfun, "new gap");
return bfun;
}
/* Update BFUN with respect to the instruction at PC. This may create new
function segments.
Return the chronologically latest function segment, never NULL. */
@ -468,8 +490,8 @@ ftrace_update_function (struct btrace_function *bfun, CORE_ADDR pc)
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)
/* If we didn't have a function or if we had a gap before, we create one. */
if (bfun == NULL || bfun->errcode != 0)
return ftrace_new_function (bfun, mfun, fun);
/* Check the last instruction, if we have one.
@ -597,13 +619,14 @@ btrace_compute_ftrace_bts (struct thread_info *tp,
struct btrace_thread_info *btinfo;
struct btrace_function *begin, *end;
struct gdbarch *gdbarch;
unsigned int blk;
unsigned int blk, ngaps;
int level;
gdbarch = target_gdbarch ();
btinfo = &tp->btrace;
begin = btinfo->begin;
end = btinfo->end;
ngaps = btinfo->ngaps;
level = begin != NULL ? -btinfo->level : INT_MAX;
blk = VEC_length (btrace_block_s, btrace->blocks);
@ -626,8 +649,16 @@ btrace_compute_ftrace_bts (struct thread_info *tp,
/* We should hit the end of the block. Warn if we went too far. */
if (block->end < pc)
{
warning (_("Recorded trace may be corrupted around %s."),
core_addr_to_string_nz (pc));
/* Indicate the gap in the trace - unless we're at the
beginning. */
if (begin != NULL)
{
warning (_("Recorded trace may be corrupted around %s."),
core_addr_to_string_nz (pc));
end = ftrace_new_gap (end, BDE_BTS_OVERFLOW);
ngaps += 1;
}
break;
}
@ -660,6 +691,12 @@ btrace_compute_ftrace_bts (struct thread_info *tp,
{
warning (_("Recorded trace may be incomplete around %s."),
core_addr_to_string_nz (pc));
/* Indicate the gap in the trace. We just added INSN so we're
not at the beginning. */
end = ftrace_new_gap (end, BDE_BTS_INSN_SIZE);
ngaps += 1;
break;
}
@ -678,6 +715,7 @@ btrace_compute_ftrace_bts (struct thread_info *tp,
btinfo->begin = begin;
btinfo->end = end;
btinfo->ngaps = ngaps;
/* LEVEL is the minimal function level of all btrace function segments.
Define the global level offset to -LEVEL so all function levels are
@ -808,20 +846,30 @@ btrace_teardown (struct thread_info *tp)
/* Stitch branch trace in BTS format. */
static int
btrace_stitch_bts (struct btrace_data_bts *btrace,
const struct btrace_thread_info *btinfo)
btrace_stitch_bts (struct btrace_data_bts *btrace, struct thread_info *tp)
{
struct btrace_thread_info *btinfo;
struct btrace_function *last_bfun;
struct btrace_insn *last_insn;
btrace_block_s *first_new_block;
btinfo = &tp->btrace;
last_bfun = btinfo->end;
gdb_assert (last_bfun != NULL);
gdb_assert (!VEC_empty (btrace_block_s, btrace->blocks));
/* If the existing trace ends with a gap, we just glue the traces
together. We need to drop the last (i.e. chronologically first) block
of the new trace, though, since we can't fill in the start address.*/
if (VEC_empty (btrace_insn_s, last_bfun->insn))
{
VEC_pop (btrace_block_s, btrace->blocks);
return 0;
}
/* Beware that block trace starts with the most recent block, so the
chronologically first block in the new trace is the last block in
the new trace's block vector. */
gdb_assert (!VEC_empty (btrace_block_s, btrace->blocks));
first_new_block = VEC_last (btrace_block_s, btrace->blocks);
last_insn = VEC_last (btrace_insn_s, last_bfun->insn);
@ -869,18 +917,25 @@ btrace_stitch_bts (struct btrace_data_bts *btrace,
been the only instruction in this function segment.
This violates the invariant but will be remedied shortly by
btrace_compute_ftrace when we add the new trace. */
/* The only case where this would hurt is if the entire trace consisted
of just that one instruction. If we remove it, we might turn the now
empty btrace function segment into a gap. But we don't want gaps at
the beginning. To avoid this, we remove the entire old trace. */
if (last_bfun == btinfo->begin && VEC_empty (btrace_insn_s, last_bfun->insn))
btrace_clear (tp);
return 0;
}
/* Adjust the block trace in order to stitch old and new trace together.
BTRACE is the new delta trace between the last and the current stop.
BTINFO is the old branch trace until the last stop.
May modifx BTRACE as well as the existing trace in BTINFO.
TP is the traced thread.
May modifx BTRACE as well as the existing trace in TP.
Return 0 on success, -1 otherwise. */
static int
btrace_stitch_trace (struct btrace_data *btrace,
const struct btrace_thread_info *btinfo)
btrace_stitch_trace (struct btrace_data *btrace, struct thread_info *tp)
{
/* If we don't have trace, there's nothing to do. */
if (btrace_data_empty (btrace))
@ -892,7 +947,7 @@ btrace_stitch_trace (struct btrace_data *btrace,
return 0;
case BTRACE_FORMAT_BTS:
return btrace_stitch_bts (&btrace->variant.bts, btinfo);
return btrace_stitch_bts (&btrace->variant.bts, tp);
}
internal_error (__FILE__, __LINE__, _("Unkown branch trace format."));
@ -946,7 +1001,7 @@ btrace_fetch (struct thread_info *tp)
if (errcode == 0)
{
/* Success. Let's try to stitch the traces together. */
errcode = btrace_stitch_trace (&btrace, btinfo);
errcode = btrace_stitch_trace (&btrace, tp);
}
else
{
@ -1009,6 +1064,7 @@ btrace_clear (struct thread_info *tp)
btinfo->begin = NULL;
btinfo->end = NULL;
btinfo->ngaps = 0;
btrace_clear_history (btinfo);
}
@ -1206,6 +1262,10 @@ btrace_insn_get (const struct btrace_insn_iterator *it)
index = it->index;
bfun = it->function;
/* Check if the iterator points to a gap in the trace. */
if (bfun->errcode != 0)
return NULL;
/* The index is within the bounds of this function's instruction vector. */
end = VEC_length (btrace_insn_s, bfun->insn);
gdb_assert (0 < end);
@ -1222,6 +1282,11 @@ btrace_insn_number (const struct btrace_insn_iterator *it)
const struct btrace_function *bfun;
bfun = it->function;
/* Return zero if the iterator points to a gap in the trace. */
if (bfun->errcode != 0)
return 0;
return bfun->insn_offset + it->index;
}
@ -1254,12 +1319,16 @@ btrace_insn_end (struct btrace_insn_iterator *it,
if (bfun == NULL)
error (_("No trace."));
/* The last instruction in the last function is the current instruction.
We point to it - it is one past the end of the execution trace. */
length = VEC_length (btrace_insn_s, bfun->insn);
/* The last function may either be a gap or it contains the current
instruction, which is one past the end of the execution trace; ignore
it. */
if (length > 0)
length -= 1;
it->function = bfun;
it->index = length - 1;
it->index = length;
}
/* See btrace.h. */
@ -1280,6 +1349,25 @@ btrace_insn_next (struct btrace_insn_iterator *it, unsigned int stride)
end = VEC_length (btrace_insn_s, bfun->insn);
/* An empty function segment represents a gap in the trace. We count
it as one instruction. */
if (end == 0)
{
const struct btrace_function *next;
next = bfun->flow.next;
if (next == NULL)
break;
stride -= 1;
steps += 1;
bfun = next;
index = 0;
continue;
}
gdb_assert (0 < end);
gdb_assert (index < end);
@ -1354,12 +1442,20 @@ btrace_insn_prev (struct btrace_insn_iterator *it, unsigned int stride)
bfun = prev;
index = VEC_length (btrace_insn_s, bfun->insn);
/* There is at least one instruction in this function segment. */
gdb_assert (index > 0);
/* An empty function segment represents a gap in the trace. We count
it as one instruction. */
if (index == 0)
{
stride -= 1;
steps += 1;
continue;
}
}
/* Advance the iterator as far as possible within this segment. */
adv = min (index, stride);
stride -= adv;
index -= adv;
steps += adv;
@ -1386,6 +1482,37 @@ btrace_insn_cmp (const struct btrace_insn_iterator *lhs,
lnum = btrace_insn_number (lhs);
rnum = btrace_insn_number (rhs);
/* A gap has an instruction number of zero. Things are getting more
complicated if gaps are involved.
We take the instruction number offset from the iterator's function.
This is the number of the first instruction after the gap.
This is OK as long as both lhs and rhs point to gaps. If only one of
them does, we need to adjust the number based on the other's regular
instruction number. Otherwise, a gap might compare equal to an
instruction. */
if (lnum == 0 && rnum == 0)
{
lnum = lhs->function->insn_offset;
rnum = rhs->function->insn_offset;
}
else if (lnum == 0)
{
lnum = lhs->function->insn_offset;
if (lnum == rnum)
lnum -= 1;
}
else if (rnum == 0)
{
rnum = rhs->function->insn_offset;
if (rnum == lnum)
rnum -= 1;
}
return (int) (lnum - rnum);
}
@ -1397,16 +1524,25 @@ btrace_find_insn_by_number (struct btrace_insn_iterator *it,
unsigned int number)
{
const struct btrace_function *bfun;
unsigned int end;
unsigned int end, length;
for (bfun = btinfo->end; bfun != NULL; bfun = bfun->flow.prev)
if (bfun->insn_offset <= number)
break;
{
/* Skip gaps. */
if (bfun->errcode != 0)
continue;
if (bfun->insn_offset <= number)
break;
}
if (bfun == NULL)
return 0;
end = bfun->insn_offset + VEC_length (btrace_insn_s, bfun->insn);
length = VEC_length (btrace_insn_s, bfun->insn);
gdb_assert (length > 0);
end = bfun->insn_offset + length;
if (end <= number)
return 0;

View File

@ -86,12 +86,25 @@ enum btrace_function_flag
BFUN_UP_LINKS_TO_TAILCALL = (1 << 1)
};
/* Decode errors for the BTS recording format. */
enum btrace_bts_error
{
/* The instruction trace overflowed the end of the trace block. */
BDE_BTS_OVERFLOW = 1,
/* The instruction size could not be determined. */
BDE_BTS_INSN_SIZE
};
/* 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.
We do not allow function segments without any instructions. */
In case of decode errors, we add an empty function segment to indicate
the gap in the trace.
We do not allow function segments without instructions otherwise. */
struct btrace_function
{
/* The full and minimal symbol for the function. Both may be NULL. */
@ -110,14 +123,23 @@ struct btrace_function
struct btrace_function *up;
/* The instructions in this function segment.
The instruction vector will never be empty. */
The instruction vector will be empty if the function segment
represents a decode error. */
VEC (btrace_insn_s) *insn;
/* The error code of a decode error that led to a gap.
Must be zero unless INSN is empty; non-zero otherwise. */
int errcode;
/* The instruction number offset for the first instruction in this
function segment. */
function segment.
If INSN is empty this is the insn_offset of the succeding function
segment in control-flow order. */
unsigned int insn_offset;
/* The function number in control-flow order. */
/* The function number in control-flow order.
If INSN is empty indicating a gap in the trace due to a decode error,
we still count the gap as a function. */
unsigned int number;
/* The function level in a back trace across the entire branch trace.
@ -223,6 +245,9 @@ struct btrace_thread_info
becomes zero. */
int level;
/* The number of gaps in the trace. */
unsigned int ngaps;
/* A bit-vector of btrace_thread_flag. */
enum btrace_thread_flag flags;
@ -232,7 +257,9 @@ struct btrace_thread_info
/* The function call history iterator. */
struct btrace_call_history *call_history;
/* The current replay position. NULL if not replaying. */
/* The current replay position. NULL if not replaying.
Gaps are skipped during replay, so REPLAY always points to a valid
instruction. */
struct btrace_insn_iterator *replay;
};
@ -270,7 +297,8 @@ extern void parse_xml_btrace (struct btrace_data *data, const char *xml);
extern void parse_xml_btrace_conf (struct btrace_config *conf, const char *xml);
/* Dereference a branch trace instruction iterator. Return a pointer to the
instruction the iterator points to. */
instruction the iterator points to.
May return NULL if the iterator points to a gap in the trace. */
extern const struct btrace_insn *
btrace_insn_get (const struct btrace_insn_iterator *);

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

View File

@ -1,3 +1,16 @@
2015-02-09 Markus Metzger <markus.t.metzger@intel.com>
* 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.
2015-02-09 Markus Metzger <markus.t.metzger@intel.com>
* gdb.btrace/buffer-size: New.

View File

@ -39,7 +39,7 @@ gdb_test "info record" [join [list \
"Active record target: record-btrace" \
"Recording format: Branch Trace Store\." \
"Buffer size: 4kB\." \
"Recorded 0 instructions in 0 functions for \[^\\\r\\\n\]*" \
"Recorded 0 instructions in 0 functions \\\(0 gaps\\\) for \[^\\\r\\\n\]*" \
] "\r\n"] "info record with small bts buffer"
gdb_test "record stop" ".*" "stop recording with small bts buffer"
@ -52,6 +52,6 @@ gdb_test "info record" [join [list \
"Active record target: record-btrace" \
"Recording format: Branch Trace Store\." \
"Buffer size: .*\." \
"Recorded 0 instructions in 0 functions for \[^\\\r\\\n\]*" \
"Recorded 0 instructions in 0 functions \\\(0 gaps\\\) for \[^\\\r\\\n\]*" \
] "\r\n"] "info record with unlimited bts buffer"
gdb_test "record stop" ".*" "stop recording with unlimited bts buffer"

View File

@ -40,7 +40,7 @@ with_test_prefix "no trace" {
gdb_test "info record" [join [list \
"Active record target: record-btrace" \
"Recording format: .*" \
"Recorded 0 instructions in 0 functions for .*" \
"Recorded 0 instructions in 0 functions \\\(0 gaps\\\) for .*" \
] "\r\n"]
gdb_test "record instruction-history" "No trace\."
gdb_test "record function-call-history" "No trace\."
@ -53,7 +53,7 @@ proc check_trace {} {
gdb_test "info record" [join [list \
"Active record target: record-btrace" \
"Recording format: .*" \
"Recorded 1 instructions in 1 functions for .*" \
"Recorded 1 instructions in 1 functions \\\(0 gaps\\\) for .*" \
] "\r\n"]
gdb_test "record instruction-history /f 1" \
"1\t 0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tmov *\\\$0x0,%eax\r"
@ -74,7 +74,7 @@ gdb_test "reverse-stepi"
gdb_test "info record" [join [list \
"Active record target: record-btrace" \
"Recording format: .*" \
"Recorded 1 instructions in 1 functions for .*" \
"Recorded 1 instructions in 1 functions \\\(0 gaps\\\) for .*" \
"Replay in progress\. At instruction 1\." \
] "\r\n"] "reverse-stepi"
@ -83,5 +83,5 @@ gdb_test "stepi"
gdb_test "info record" [join [list \
"Active record target: record-btrace" \
"Recording format: .*" \
"Recorded 1 instructions in 1 functions for .*" \
"Recorded 1 instructions in 1 functions \\\(0 gaps\\\) for .*" \
] "\r\n"] "and back"

View File

@ -58,7 +58,7 @@ gdb_test "record full" "The process is already being recorded\\. Use \"record s
# no trace recorded yet
gdb_test "info record" "Active record target: record-btrace\r
.*\r
Recorded 0 instructions in 0 functions for thread 1.*\\." "info record without trace"
Recorded 0 instructions in 0 functions \\\(0 gaps\\\) for thread 1.*\\." "info record without trace"
# stop btrace record
gdb_test "record stop" "Process record is stopped and all execution logs are deleted\\." "record stop"

View File

@ -38,7 +38,7 @@ proc check_replay_at { insn } {
gdb_test "info record" [join [list \
"Active record target: record-btrace" \
"Recording format: .*" \
"Recorded 40 instructions in 16 functions for .*" \
"Recorded 40 instructions in 16 functions \\\(0 gaps\\\) for .*" \
"Replay in progress\. At instruction $insn\." \
] "\r\n"]
}

View File

@ -50,7 +50,7 @@ gdb_continue_to_breakpoint "cont to $bp_location" ".*$srcfile2:$bp_location.*"
set traced {}
set testname "determine number of recorded instructions"
gdb_test_multiple "info record" $testname {
-re "Active record target: record-btrace\r\n.*\r\nRecorded \(\[0-9\]*\) instructions in \(\[0-9\]*\) functions for thread 1 .*\\.\r\n$gdb_prompt $" {
-re "Active record target: record-btrace\r\n.*\r\nRecorded \(\[0-9\]*\) instructions in \(\[0-9\]*\) functions \\\(0 gaps\\\) for thread 1 .*\\.\r\n$gdb_prompt $" {
set traced $expect_out(1,string)
pass $testname
}

View File

@ -38,7 +38,7 @@ proc check_replay_at { insn } {
gdb_test "info record" [join [list \
"Active record target: record-btrace" \
"Recording format: .*" \
"Recorded 40 instructions in 16 functions for .*" \
"Recorded 40 instructions in 16 functions \\\(0 gaps\\\) for .*" \
"Replay in progress\. At instruction $insn\." \
] "\r\n"]
}
@ -57,7 +57,7 @@ gdb_test "next" ".*main\.3.*"
gdb_test "info record" [join [list \
"Active record target: record-btrace" \
"Recording format: .*" \
"Recorded 40 instructions in 16 functions for \[^\\\r\\\n\]*" \
"Recorded 40 instructions in 16 functions \\\(0 gaps\\\) for \[^\\\r\\\n\]*" \
] "\r\n"] "next back"
# let's go somewhere where we can step some more

View File

@ -38,7 +38,7 @@ proc check_replay_at { insn } {
gdb_test "info record" [join [list \
"Active record target: record-btrace" \
"Recording format: .*" \
"Recorded 40 instructions in 16 functions for .*" \
"Recorded 40 instructions in 16 functions \\\(0 gaps\\\) for .*" \
"Replay in progress\. At instruction $insn\." \
] "\r\n"]
}
@ -57,7 +57,7 @@ gdb_test "nexti" ".*main\.3.*" "next, 1.5"
gdb_test "info record" [join [list \
"Active record target: record-btrace" \
"Recording format: .*" \
"Recorded 40 instructions in 16 functions for \[^\\\r\\\n\]*" \
"Recorded 40 instructions in 16 functions \\\(0 gaps\\\) for \[^\\\r\\\n\]*" \
] "\r\n"] "nexti back"
# let's go somewhere where we can step some more

View File

@ -34,7 +34,7 @@ proc check_not_replaying {} {
gdb_test "info record" [join [list \
"Active record target: record-btrace" \
"Recording format: .*" \
"Recorded 0 instructions in 0 functions for \[^\\\r\\\n\]*" \
"Recorded 0 instructions in 0 functions \\\(0 gaps\\\) for \[^\\\r\\\n\]*" \
] "\r\n"]
}

View File

@ -38,7 +38,7 @@ proc check_replay_at { insn } {
gdb_test "info record" [join [list \
"Active record target: record-btrace" \
"Recording format: .*" \
"Recorded 40 instructions in 16 functions for .*" \
"Recorded 40 instructions in 16 functions \\\(0 gaps\\\) for .*" \
"Replay in progress\. At instruction $insn\." \
] "\r\n"]
}
@ -87,5 +87,5 @@ gdb_test "step" ".*main\.3.*"
gdb_test "info record" [join [list \
"Active record target: record-btrace" \
"Recording format: .*" \
"Recorded 40 instructions in 16 functions for \[^\\\r\\\n\]*" \
"Recorded 40 instructions in 16 functions \\\(0 gaps\\\) for \[^\\\r\\\n\]*" \
] "\r\n"] "step to live"

View File

@ -36,7 +36,7 @@ proc check_replay_at { insn } {
gdb_test "info record" [join [list \
"Active record target: record-btrace" \
"Recording format: .*" \
"Recorded 40 instructions in 16 functions for .*" \
"Recorded 40 instructions in 16 functions \\\(0 gaps\\\) for .*" \
"Replay in progress\. At instruction $insn\." \
] "\r\n"]
}
@ -61,7 +61,7 @@ gdb_test "stepi" ".*main\.3.*"
gdb_test "info record" [join [list \
"Active record target: record-btrace" \
"Recording format: .*" \
"Recorded 40 instructions in 16 functions for \[^\\\r\\\n\]*" \
"Recorded 40 instructions in 16 functions \\\(0 gaps\\\) for \[^\\\r\\\n\]*" \
] "\r\n"] "stepi to live"
# let's step from a goto position somewhere in the middle