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.com
pull/1354/merge
Alexandre Chartre 2025-11-21 10:53:25 +01:00 committed by Peter Zijlstra
parent 9b580accac
commit 350c7ab857
3 changed files with 125 additions and 13 deletions

View File

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

View File

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

View File

@ -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);
}