objtool: Improve tracing of alternative instructions
When tracing function validation, improve the reporting of alternative instruction by more clearly showing the different alternatives beginning and end. Signed-off-by: Alexandre Chartre <alexandre.chartre@oracle.com> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Acked-by: Josh Poimboeuf <jpoimboe@kernel.org> Link: https://patch.msgid.link/20251121095340.464045-16-alexandre.chartre@oracle.compull/1354/merge
parent
9b580accac
commit
350c7ab857
|
|
@ -3564,7 +3564,7 @@ static bool skip_alt_group(struct instruction *insn)
|
|||
|
||||
/* ANNOTATE_IGNORE_ALTERNATIVE */
|
||||
if (insn->alt_group->ignore) {
|
||||
TRACE_INSN(insn, "alt group ignored");
|
||||
TRACE_ALT(insn, "alt group ignored");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -3680,8 +3680,9 @@ static int validate_insn(struct objtool_file *file, struct symbol *func,
|
|||
struct instruction *prev_insn, struct instruction *next_insn,
|
||||
bool *dead_end)
|
||||
{
|
||||
/* prev_state is not used if there is no disassembly support */
|
||||
/* prev_state and alt_name are not used if there is no disassembly support */
|
||||
struct insn_state prev_state __maybe_unused;
|
||||
char *alt_name __maybe_unused = NULL;
|
||||
struct alternative *alt;
|
||||
u8 visited;
|
||||
int ret;
|
||||
|
|
@ -3768,23 +3769,16 @@ static int validate_insn(struct objtool_file *file, struct symbol *func,
|
|||
return 1;
|
||||
|
||||
if (insn->alts) {
|
||||
int i, num_alts;
|
||||
|
||||
num_alts = 0;
|
||||
for (alt = insn->alts; alt; alt = alt->next)
|
||||
num_alts++;
|
||||
|
||||
i = 1;
|
||||
for (alt = insn->alts; alt; alt = alt->next) {
|
||||
TRACE_INSN(insn, "alternative %d/%d", i, num_alts);
|
||||
TRACE_ALT_BEGIN(insn, alt, alt_name);
|
||||
ret = validate_branch(file, func, alt->insn, *statep);
|
||||
TRACE_ALT_END(insn, alt, alt_name);
|
||||
if (ret) {
|
||||
BT_INSN(insn, "(alt)");
|
||||
return ret;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
TRACE_INSN(insn, "alternative DEFAULT");
|
||||
TRACE_ALT_INFO_NOADDR(insn, "/ ", "DEFAULT");
|
||||
}
|
||||
|
||||
if (skip_alt_group(insn))
|
||||
|
|
|
|||
|
|
@ -19,11 +19,26 @@ extern int trace_depth;
|
|||
fprintf(stderr, fmt, ##__VA_ARGS__); \
|
||||
})
|
||||
|
||||
/*
|
||||
* Print the instruction address and a message. The instruction
|
||||
* itself is not printed.
|
||||
*/
|
||||
#define TRACE_ADDR(insn, fmt, ...) \
|
||||
({ \
|
||||
if (trace) { \
|
||||
disas_print_info(stderr, insn, trace_depth - 1, \
|
||||
fmt "\n", ##__VA_ARGS__); \
|
||||
} \
|
||||
})
|
||||
|
||||
/*
|
||||
* Print the instruction address, the instruction and a message.
|
||||
*/
|
||||
#define TRACE_INSN(insn, fmt, ...) \
|
||||
({ \
|
||||
if (trace) { \
|
||||
disas_print_insn(stderr, objtool_disas_ctx, \
|
||||
insn, trace_depth - 1, \
|
||||
insn, trace_depth - 1, \
|
||||
fmt, ##__VA_ARGS__); \
|
||||
fprintf(stderr, "\n"); \
|
||||
insn->trace = 1; \
|
||||
|
|
@ -36,6 +51,37 @@ extern int trace_depth;
|
|||
trace_insn_state(insn, sprev, snext); \
|
||||
})
|
||||
|
||||
#define TRACE_ALT_FMT(pfx, fmt) pfx "<%s.%lx> " fmt
|
||||
#define TRACE_ALT_ARG(insn) disas_alt_type_name(insn), (insn)->offset
|
||||
|
||||
#define TRACE_ALT(insn, fmt, ...) \
|
||||
TRACE_INSN(insn, TRACE_ALT_FMT("", fmt), \
|
||||
TRACE_ALT_ARG(insn), ##__VA_ARGS__)
|
||||
|
||||
#define TRACE_ALT_INFO(insn, pfx, fmt, ...) \
|
||||
TRACE_ADDR(insn, TRACE_ALT_FMT(pfx, fmt), \
|
||||
TRACE_ALT_ARG(insn), ##__VA_ARGS__)
|
||||
|
||||
#define TRACE_ALT_INFO_NOADDR(insn, pfx, fmt, ...) \
|
||||
TRACE_ADDR(NULL, TRACE_ALT_FMT(pfx, fmt), \
|
||||
TRACE_ALT_ARG(insn), ##__VA_ARGS__)
|
||||
|
||||
#define TRACE_ALT_BEGIN(insn, alt, alt_name) \
|
||||
({ \
|
||||
if (trace) { \
|
||||
alt_name = disas_alt_name(alt); \
|
||||
trace_alt_begin(insn, alt, alt_name); \
|
||||
} \
|
||||
})
|
||||
|
||||
#define TRACE_ALT_END(insn, alt, alt_name) \
|
||||
({ \
|
||||
if (trace) { \
|
||||
trace_alt_end(insn, alt, alt_name); \
|
||||
free(alt_name); \
|
||||
} \
|
||||
})
|
||||
|
||||
static inline void trace_enable(void)
|
||||
{
|
||||
trace = true;
|
||||
|
|
@ -61,17 +107,34 @@ static inline void trace_depth_dec(void)
|
|||
|
||||
void trace_insn_state(struct instruction *insn, struct insn_state *sprev,
|
||||
struct insn_state *snext);
|
||||
void trace_alt_begin(struct instruction *orig_insn, struct alternative *alt,
|
||||
char *alt_name);
|
||||
void trace_alt_end(struct instruction *orig_insn, struct alternative *alt,
|
||||
char *alt_name);
|
||||
|
||||
#else /* DISAS */
|
||||
|
||||
#define TRACE(fmt, ...) ({})
|
||||
#define TRACE_ADDR(insn, fmt, ...) ({})
|
||||
#define TRACE_INSN(insn, fmt, ...) ({})
|
||||
#define TRACE_INSN_STATE(insn, sprev, snext) ({})
|
||||
#define TRACE_ALT(insn, fmt, ...) ({})
|
||||
#define TRACE_ALT_INFO(insn, fmt, ...) ({})
|
||||
#define TRACE_ALT_INFO_NOADDR(insn, fmt, ...) ({})
|
||||
#define TRACE_ALT_BEGIN(insn, alt, alt_name) ({})
|
||||
#define TRACE_ALT_END(insn, alt, alt_name) ({})
|
||||
|
||||
|
||||
static inline void trace_enable(void) {}
|
||||
static inline void trace_disable(void) {}
|
||||
static inline void trace_depth_inc(void) {}
|
||||
static inline void trace_depth_dec(void) {}
|
||||
static inline void trace_alt_begin(struct instruction *orig_insn,
|
||||
struct alternative *alt,
|
||||
char *alt_name) {};
|
||||
static inline void trace_alt_end(struct instruction *orig_insn,
|
||||
struct alternative *alt,
|
||||
char *alt_name) {};
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -146,3 +146,58 @@ void trace_insn_state(struct instruction *insn, struct insn_state *sprev,
|
|||
|
||||
insn->trace = 1;
|
||||
}
|
||||
|
||||
void trace_alt_begin(struct instruction *orig_insn, struct alternative *alt,
|
||||
char *alt_name)
|
||||
{
|
||||
struct instruction *alt_insn;
|
||||
char suffix[2];
|
||||
|
||||
alt_insn = alt->insn;
|
||||
|
||||
if (alt->type == ALT_TYPE_EX_TABLE) {
|
||||
/*
|
||||
* When there is an exception table then the instruction
|
||||
* at the original location is executed but it can cause
|
||||
* an exception. In that case, the execution will be
|
||||
* redirected to the alternative instruction.
|
||||
*
|
||||
* The instruction at the original location can have
|
||||
* instruction alternatives, so we just print the location
|
||||
* of the instruction that can cause the exception and
|
||||
* not the instruction itself.
|
||||
*/
|
||||
TRACE_ALT_INFO_NOADDR(orig_insn, "/ ", "%s for instruction at 0x%lx <%s+0x%lx>",
|
||||
alt_name,
|
||||
orig_insn->offset, orig_insn->sym->name,
|
||||
orig_insn->offset - orig_insn->sym->offset);
|
||||
} else {
|
||||
TRACE_ALT_INFO_NOADDR(orig_insn, "/ ", "%s", alt_name);
|
||||
}
|
||||
|
||||
if (alt->type == ALT_TYPE_JUMP_TABLE) {
|
||||
/*
|
||||
* For a jump alternative, if the default instruction is
|
||||
* a NOP then it is replaced with the jmp instruction,
|
||||
* otherwise it is replaced with a NOP instruction.
|
||||
*/
|
||||
trace_depth++;
|
||||
if (orig_insn->type == INSN_NOP) {
|
||||
suffix[0] = (orig_insn->len == 5) ? 'q' : '\0';
|
||||
TRACE_ADDR(orig_insn, "jmp%-3s %lx <%s+0x%lx>", suffix,
|
||||
alt_insn->offset, alt_insn->sym->name,
|
||||
alt_insn->offset - alt_insn->sym->offset);
|
||||
} else {
|
||||
TRACE_ADDR(orig_insn, "nop%d", orig_insn->len);
|
||||
trace_depth--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void trace_alt_end(struct instruction *orig_insn, struct alternative *alt,
|
||||
char *alt_name)
|
||||
{
|
||||
if (alt->type == ALT_TYPE_JUMP_TABLE && orig_insn->type == INSN_NOP)
|
||||
trace_depth--;
|
||||
TRACE_ALT_INFO_NOADDR(orig_insn, "\\ ", "%s", alt_name);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue