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:
parent
afb778a2a8
commit
31fd9caad9
@ -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.
|
||||
|
196
gdb/btrace.c
196
gdb/btrace.c
@ -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;
|
||||
|
||||
|
40
gdb/btrace.h
40
gdb/btrace.h
@ -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 *);
|
||||
|
||||
|
@ -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),
|
||||
|
@ -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.
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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"]
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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"]
|
||||
}
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user