root/tools/objtool/include/objtool/trace.h
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
 * Copyright (c) 2025, Oracle and/or its affiliates.
 */

#ifndef _TRACE_H
#define _TRACE_H

#include <objtool/check.h>
#include <objtool/disas.h>

#ifdef DISAS

extern bool trace;
extern int trace_depth;

#define TRACE(fmt, ...)                                         \
({      if (trace)                                              \
                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,         \
                                 fmt, ##__VA_ARGS__);           \
                fprintf(stderr, "\n");                          \
                insn->trace = 1;                                \
        }                                                       \
})

#define TRACE_INSN_STATE(insn, sprev, snext)                    \
({                                                              \
        if (trace)                                              \
                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;
        trace_depth = 0;
}

static inline void trace_disable(void)
{
        trace = false;
}

static inline void trace_depth_inc(void)
{
        if (trace)
                trace_depth++;
}

static inline void trace_depth_dec(void)
{
        if (trace)
                trace_depth--;
}

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

#endif /* _TRACE_H */