Skip to content

Commit

Permalink
Implement RISC-V tracing counter NOPs.
Browse files Browse the repository at this point in the history
Add NOP that guests can use to create and increment/decrement
counters.
The counters are emitted into the perfetto trace stream as normal
perfetto counter tracks.
  • Loading branch information
qwattash committed Apr 26, 2022
1 parent 9afdb4f commit 9b1e0ee
Show file tree
Hide file tree
Showing 14 changed files with 604 additions and 182 deletions.
3 changes: 3 additions & 0 deletions accel/tcg/log_instr_json.c
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,9 @@ void emit_json_entry(CPUArchState *env, cpu_log_entry_t *entry)
case LOG_EVENT_REGDUMP:
emit_json_evt_regdump(&event->reg_dump, js_evt);
break;
case LOG_EVENT_COUNTER:
/* TODO Unimpl */
break;
default:
assert(0 && "unknown event ID");
}
Expand Down
2 changes: 2 additions & 0 deletions accel/tcg/log_instr_protobuf.c
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,8 @@ static void emit_protobuf_event(log_event_t *evtinfo, QEMULogEntryEvt **pb_evt)
emit_protobuf_reginfo(rinfo, pb_regs);
}
} break;
case LOG_EVENT_COUNTER:
break;
default:
assert(0 && "unknown event ID");
}
Expand Down
9 changes: 9 additions & 0 deletions accel/tcg/log_instr_text.c
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ void emit_text_instr(CPUArchState *env, cpu_log_entry_t *entry)
const log_event_t *event;
const char *log_state_op;
int i, j;
bool incremental;

if (entry->flags & LI_FLAG_HAS_INSTR_DATA) {
/* Dump CPU-ID:ASID + address */
Expand Down Expand Up @@ -227,6 +228,14 @@ void emit_text_instr(CPUArchState *env, cpu_log_entry_t *entry)
&g_array_index(event->reg_dump.gpr, log_reginfo_t, j);
emit_text_reg(r);
}
break;
case LOG_EVENT_COUNTER:
incremental = log_event_counter_incremental(event->counter.flags);
qemu_log("Counter %s %s[%d]: %lx\n", (incremental) ? "INC" : "ABS",
event->counter.name,
log_event_counter_slot(event->counter.flags),
event->counter.value);
break;
default:
assert(0 && "unknown event ID");
}
Expand Down
3 changes: 2 additions & 1 deletion include/exec/log_instr_perfetto.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ PERFETTO_DEFINE_CATEGORIES(
perfetto::Category("trap").SetDescription("CPU trap events"),
perfetto::Category("sched").SetDescription("Scheduling events"),
perfetto::Category("marker").SetDescription(
"Guest trace timestamp markers"));
"Guest trace timestamp markers"),
perfetto::Category("counter").SetDescription("Guest-driven counters"));
#endif

/*
Expand Down
24 changes: 21 additions & 3 deletions include/qemu/log_instr.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@
#define QEMU_LOG_PRINTF_BUF_DEPTH 32
/* Early flush if buffer gets this full. */
#define QEMU_LOG_PRINTF_FLUSH_BARRIER 32
/* Max name size for qemu counters and other events */
#define QEMU_LOG_EVENT_MAX_NAMELEN 64

/*
* Instruction logging format
Expand Down Expand Up @@ -113,6 +115,7 @@ typedef enum {
LOG_EVENT_CTX_UPDATE = 1,
LOG_EVENT_MARKER = 2,
LOG_EVENT_REGDUMP = 3,
LOG_EVENT_COUNTER = 4,
} log_event_id_t;

/*
Expand Down Expand Up @@ -159,6 +162,20 @@ typedef struct {
GArray *gpr;
} log_event_regdump_t;

/*
* Counter event.
* This represents an arbitrary named counter. Generally driven from the
* guest OS via NOPs, but can also be generated internally.
*/
typedef struct {
char name[QEMU_LOG_EVENT_MAX_NAMELEN];
int64_t value;
uint64_t flags;
} log_event_counter_t;

#define log_event_counter_slot(flags) (flags & 0xffff)
#define log_event_counter_incremental(flags) ((flags >> 32) & 0x01)

/*
* Trace event.
* This records arbitrary higher-level events associated with instruction
Expand All @@ -180,6 +197,7 @@ typedef struct {
log_event_ctx_update_t ctx_update;
log_event_regdump_t reg_dump;
uint64_t marker;
log_event_counter_t counter;
};
} log_event_t;

Expand Down Expand Up @@ -241,9 +259,9 @@ typedef struct qemu_log_instr_stats {
uint64_t trace_stop;
} qemu_log_instr_stats_t;

#define QEMU_LOG_INSTR_INC_STAT(cpu_state, stat) \
do { \
cpu_state->stats.stat++; \
#define QEMU_LOG_INSTR_INC_STAT(cpu_state, stat) \
do { \
cpu_state->stats.stat++; \
} while (0)

/*
Expand Down
1 change: 0 additions & 1 deletion target/riscv/helper.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@ DEF_HELPER_3(sc_c_cap, tl, env, i32, i32)
DEF_HELPER_FLAGS_3(riscv_log_gpr_write, TCG_CALL_NO_RWG, void, env, i32, tl)
DEF_HELPER_FLAGS_4(riscv_log_instr, TCG_CALL_NO_RWG, void, env, tl, i32, i32)
DEF_HELPER_FLAGS_2(riscv_log_instr_event, TCG_CALL_NO_RWG, void, env, tl)
DEF_HELPER_FLAGS_1(riscv_log_instr_regdump, TCG_CALL_NO_RWG, void, env)
#endif

/* Special functions */
Expand Down
52 changes: 38 additions & 14 deletions target/riscv/op_helper_log_instr.c
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,29 @@
#define get_gpr_value(env, regnum) (env->gpr[regnum])
#endif

/*
* Read a null-terminated guest string without requiring a strlen() in
* the guest. This is probably slower but does not perturb guest call-graph.
*/
static int read_guest_cstring(CPURISCVState *env, target_ulong vaddr,
char *buffer, size_t maxlen)
{
int ret = 0;

buffer[maxlen - 1] = '\0';
while (maxlen > 1) {
ret = cpu_memory_rw_debug(env_cpu(env), vaddr, buffer, 1, false);
if (ret != 0 || *buffer == '\0') {
break;
}
maxlen--;
vaddr++;
buffer++;
}

return ret;
}

void HELPER(riscv_log_gpr_write)(CPURISCVState *env, uint32_t regnum,
target_ulong value)
{
Expand Down Expand Up @@ -78,6 +101,8 @@ void HELPER(riscv_log_instr)(CPURISCVState *env, target_ulong pc,
void HELPER(riscv_log_instr_event)(CPURISCVState *env, target_ulong pc)
{
log_event_t event;
target_ulong vaddr;
int ret;

event.id = get_gpr_value(env, 10);

Expand All @@ -96,6 +121,19 @@ void HELPER(riscv_log_instr_event)(CPURISCVState *env, target_ulong pc)
case LOG_EVENT_REGDUMP:
cpu_log_instr_event_regdump(env, &event);
break;
case LOG_EVENT_COUNTER:
vaddr = get_gpr_value(env, 11);
ret = read_guest_cstring(env, vaddr, (char *)event.counter.name,
sizeof(event.counter.name));
if (ret != 0) {
warn_report("LOG EVENT HELPER: Could not read guest string at"
" vaddr 0x" TARGET_FMT_lx "\r\n",
vaddr);
return;
}
event.counter.value = (int64_t)get_gpr_value(env, 12);
event.counter.flags = get_gpr_value(env, 13);
break;
default:
warn_report("Unsupported event ID for TCG instr logging %d",
event.id);
Expand Down Expand Up @@ -139,17 +177,3 @@ bool cpu_log_instr_event_regdump(CPURISCVState *env, log_event_t *event)

return false;
}

/*
* Emit a register dump event.
* Only dump general purpose registers for now. No fpu, vector or CPU registers.
*/
void HELPER(riscv_log_instr_regdump)(CPURISCVState *env)
{
log_event_t event;

if (qemu_log_instr_enabled(env)) {
cpu_log_instr_event_regdump(env, &event);
qemu_log_instr_event(env, &event);
}
}
2 changes: 1 addition & 1 deletion trace_extra/cheri-perfetto
Loading

0 comments on commit 9b1e0ee

Please sign in to comment.