[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PATCH] Add support of callbacks after instructions to plugin api
From: |
Mikhail Tyutin |
Subject: |
[PATCH] Add support of callbacks after instructions to plugin api |
Date: |
Tue, 8 Aug 2023 16:44:35 +0300 |
Initially, we can only call the callback BEFORE instructions. This commit adds
the ability to insert the callback AFTER instructions.
No callback call for control-flow instructions.
Signed-off-by: Aleksandr Anenkov <a.anenkov@yadro.com>
Signed-off-by: Mikhail Tyutin <m.tyutin@yadro.com>
---
accel/tcg/plugin-gen.c | 25 ++++++++++++++++++++-----
accel/tcg/translator.c | 18 +++++++++++++-----
include/qemu/plugin.h | 1 +
include/qemu/qemu-plugin.h | 33 ++++++++++++++++++++++++++++++++-
plugins/api.c | 26 ++++++++++++++++++++++++--
plugins/qemu-plugins.symbols | 2 ++
tcg/tcg-op.c | 16 ++++++++++++++++
7 files changed, 108 insertions(+), 13 deletions(-)
diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c
index 5c13615112..88dcbda651 100644
--- a/accel/tcg/plugin-gen.c
+++ b/accel/tcg/plugin-gen.c
@@ -180,6 +180,8 @@ static void plugin_gen_empty_callback(enum plugin_gen_from
from)
{
switch (from) {
case PLUGIN_GEN_AFTER_INSN:
+ gen_wrapped(from, PLUGIN_GEN_CB_UDATA, gen_empty_udata_cb);
+ gen_wrapped(from, PLUGIN_GEN_CB_INLINE, gen_empty_inline_cb);
gen_wrapped(from, PLUGIN_GEN_DISABLE_MEM_HELPER,
gen_empty_mem_helper);
break;
@@ -598,18 +600,21 @@ static void plugin_gen_tb_inline(const struct
qemu_plugin_tb *ptb,
}
static void plugin_gen_insn_udata(const struct qemu_plugin_tb *ptb,
+ enum plugin_dyn_cb_type cb_type,
TCGOp *begin_op, int insn_idx)
{
+ g_assert(cb_type == PLUGIN_CB_INSN || cb_type == PLUGIN_CB_AFTER_INSN);
struct qemu_plugin_insn *insn = g_ptr_array_index(ptb->insns, insn_idx);
-
- inject_udata_cb(insn->cbs[PLUGIN_CB_INSN][PLUGIN_CB_REGULAR], begin_op);
+ inject_udata_cb(insn->cbs[cb_type][PLUGIN_CB_REGULAR], begin_op);
}
static void plugin_gen_insn_inline(const struct qemu_plugin_tb *ptb,
+ enum plugin_dyn_cb_type cb_type,
TCGOp *begin_op, int insn_idx)
{
+ g_assert(cb_type == PLUGIN_CB_INSN || cb_type == PLUGIN_CB_AFTER_INSN);
struct qemu_plugin_insn *insn = g_ptr_array_index(ptb->insns, insn_idx);
- inject_inline_cb(insn->cbs[PLUGIN_CB_INSN][PLUGIN_CB_INLINE],
+ inject_inline_cb(insn->cbs[cb_type][PLUGIN_CB_INLINE],
begin_op, op_ok);
}
@@ -738,10 +743,12 @@ static void plugin_gen_inject(struct qemu_plugin_tb
*plugin_tb)
switch (type) {
case PLUGIN_GEN_CB_UDATA:
- plugin_gen_insn_udata(plugin_tb, op, insn_idx);
+ plugin_gen_insn_udata(plugin_tb, PLUGIN_CB_INSN,
+ op, insn_idx);
break;
case PLUGIN_GEN_CB_INLINE:
- plugin_gen_insn_inline(plugin_tb, op, insn_idx);
+ plugin_gen_insn_inline(plugin_tb, PLUGIN_CB_INSN,
+ op, insn_idx);
break;
case PLUGIN_GEN_ENABLE_MEM_HELPER:
plugin_gen_enable_mem_helper(plugin_tb, op, insn_idx);
@@ -773,6 +780,14 @@ static void plugin_gen_inject(struct qemu_plugin_tb
*plugin_tb)
g_assert(insn_idx >= 0);
switch (type) {
+ case PLUGIN_GEN_CB_UDATA:
+ plugin_gen_insn_udata(plugin_tb, PLUGIN_CB_AFTER_INSN,
+ op, insn_idx);
+ break;
+ case PLUGIN_GEN_CB_INLINE:
+ plugin_gen_insn_inline(plugin_tb, PLUGIN_CB_AFTER_INSN,
+ op, insn_idx);
+ break;
case PLUGIN_GEN_DISABLE_MEM_HELPER:
plugin_gen_disable_mem_helper(plugin_tb, op, insn_idx);
break;
diff --git a/accel/tcg/translator.c b/accel/tcg/translator.c
index 1a6a5448c8..5e57dc754e 100644
--- a/accel/tcg/translator.c
+++ b/accel/tcg/translator.c
@@ -180,6 +180,12 @@ void translator_loop(CPUState *cpu, TranslationBlock *tb,
int *max_insns,
ops->translate_insn(db, cpu);
}
+
+ /* Stop translation if translate_insn so indicated. */
+ if (db->is_jmp != DISAS_NEXT) {
+ break;
+ }
+
/*
* We can't instrument after instructions that change control
* flow although this only really affects post-load operations.
@@ -193,11 +199,6 @@ void translator_loop(CPUState *cpu, TranslationBlock *tb,
int *max_insns,
plugin_gen_insn_end();
}
- /* Stop translation if translate_insn so indicated. */
- if (db->is_jmp != DISAS_NEXT) {
- break;
- }
-
/* Stop translation if the output buffer is full,
or we have executed all of the allowed instructions. */
if (tcg_op_buf_full() || db->num_insns >= db->max_insns) {
@@ -211,6 +212,13 @@ void translator_loop(CPUState *cpu, TranslationBlock *tb,
int *max_insns,
gen_tb_end(tb, cflags, icount_start_insn, db->num_insns);
if (plugin_enabled) {
+ /*
+ * Last chance to call plugin_gen_insn_end() if is skipped in
translation
+ * loop above.
+ */
+ if (db->is_jmp != DISAS_NEXT && tcg_ctx->exitreq_label == NULL) {
+ plugin_gen_insn_end();
+ }
plugin_gen_tb_end(cpu);
}
diff --git a/include/qemu/plugin.h b/include/qemu/plugin.h
index bc0781cab8..b221650281 100644
--- a/include/qemu/plugin.h
+++ b/include/qemu/plugin.h
@@ -68,6 +68,7 @@ union qemu_plugin_cb_sig {
enum plugin_dyn_cb_type {
PLUGIN_CB_INSN,
PLUGIN_CB_MEM,
+ PLUGIN_CB_AFTER_INSN,
PLUGIN_N_CB_TYPES,
};
diff --git a/include/qemu/qemu-plugin.h b/include/qemu/qemu-plugin.h
index 50a9957279..21e25e895d 100644
--- a/include/qemu/qemu-plugin.h
+++ b/include/qemu/qemu-plugin.h
@@ -51,7 +51,7 @@ typedef uint64_t qemu_plugin_id_t;
extern QEMU_PLUGIN_EXPORT int qemu_plugin_version;
-#define QEMU_PLUGIN_VERSION 1
+#define QEMU_PLUGIN_VERSION 2
/**
* struct qemu_info_t - system information for plugins
@@ -314,6 +314,21 @@ void qemu_plugin_register_vcpu_insn_exec_cb(struct
qemu_plugin_insn *insn,
enum qemu_plugin_cb_flags flags,
void *userdata);
+/**
+ * qemu_plugin_register_vcpu_after_insn_exec_cb() - register cb
+ * after insn execution
+ * @insn: the opaque qemu_plugin_insn handle for an instruction
+ * @cb: callback function
+ * @flags: does the plugin read or write the CPU's registers?
+ * @userdata: any plugin data to pass to the @cb?
+ *
+ * The @cb function is called every time after a non-control-flow
+ * instruction is executed
+ */
+void qemu_plugin_register_vcpu_after_insn_exec_cb(
+ struct qemu_plugin_insn *insn, qemu_plugin_vcpu_udata_cb_t cb,
+ enum qemu_plugin_cb_flags flags, void *userdata);
+
/**
* qemu_plugin_register_vcpu_insn_exec_inline() - insn execution inline op
* @insn: the opaque qemu_plugin_insn handle for an instruction
@@ -328,6 +343,22 @@ void qemu_plugin_register_vcpu_insn_exec_inline(struct
qemu_plugin_insn *insn,
enum qemu_plugin_op op,
void *ptr, uint64_t imm);
+/**
+ * qemu_plugin_register_vcpu_after_insn_exec_inline() - after insn execution
+ * inline op
+ * @insn: the opaque qemu_plugin_insn handle for an instruction
+ * @op: the type of qemu_plugin_op (e.g. ADD_U64)
+ * @ptr: the target memory location for the op
+ * @imm: the op data (e.g. 1)
+ *
+ * Insert an inline op to every time after a non-control-flow
+ * instruction executes.
+ * Useful if you just want to increment a single counter somewhere in memory.
+ */
+void qemu_plugin_register_vcpu_after_insn_exec_inline(
+ struct qemu_plugin_insn *insn, enum qemu_plugin_op op,
+ void *ptr, uint64_t imm);
+
/**
* qemu_plugin_tb_n_insns() - query helper for number of insns in TB
* @tb: opaque handle to TB passed to callback
diff --git a/plugins/api.c b/plugins/api.c
index 2078b16edb..5d4aedc0b5 100644
--- a/plugins/api.c
+++ b/plugins/api.c
@@ -114,16 +114,38 @@ void qemu_plugin_register_vcpu_insn_exec_cb(struct
qemu_plugin_insn *insn,
}
}
+void qemu_plugin_register_vcpu_after_insn_exec_cb(
+ struct qemu_plugin_insn *insn, qemu_plugin_vcpu_udata_cb_t cb,
+ enum qemu_plugin_cb_flags flags, void *udata)
+{
+ if (!insn->mem_only) {
+ plugin_register_dyn_cb__udata(
+ &insn->cbs[PLUGIN_CB_AFTER_INSN][PLUGIN_CB_REGULAR],
+ cb, flags, udata);
+ }
+}
+
void qemu_plugin_register_vcpu_insn_exec_inline(struct qemu_plugin_insn *insn,
enum qemu_plugin_op op,
void *ptr, uint64_t imm)
{
if (!insn->mem_only) {
- plugin_register_inline_op(&insn->cbs[PLUGIN_CB_INSN][PLUGIN_CB_INLINE],
- 0, op, ptr, imm);
+ plugin_register_inline_op(
+ &insn->cbs[PLUGIN_CB_INSN][PLUGIN_CB_INLINE],
+ 0, op, ptr, imm);
}
}
+void qemu_plugin_register_vcpu_after_insn_exec_inline(
+ struct qemu_plugin_insn *insn, enum qemu_plugin_op op,
+ void *ptr, uint64_t imm)
+{
+ if (!insn->mem_only) {
+ plugin_register_inline_op(
+ &insn->cbs[PLUGIN_CB_AFTER_INSN][PLUGIN_CB_INLINE],
+ 0, op, ptr, imm);
+ }
+}
/*
* We always plant memory instrumentation because they don't finalise until
diff --git a/plugins/qemu-plugins.symbols b/plugins/qemu-plugins.symbols
index 71f6c90549..d6c25521d1 100644
--- a/plugins/qemu-plugins.symbols
+++ b/plugins/qemu-plugins.symbols
@@ -26,7 +26,9 @@
qemu_plugin_register_vcpu_idle_cb;
qemu_plugin_register_vcpu_init_cb;
qemu_plugin_register_vcpu_insn_exec_cb;
+ qemu_plugin_register_vcpu_after_insn_exec_cb;
qemu_plugin_register_vcpu_insn_exec_inline;
+ qemu_plugin_register_vcpu_after_insn_exec_inline;
qemu_plugin_register_vcpu_mem_cb;
qemu_plugin_register_vcpu_mem_inline;
qemu_plugin_register_vcpu_resume_cb;
diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c
index 7aadb37756..566da1cb04 100644
--- a/tcg/tcg-op.c
+++ b/tcg/tcg-op.c
@@ -2819,6 +2819,22 @@ void tcg_gen_exit_tb(const TranslationBlock *tb,
unsigned idx)
tcg_debug_assert(idx == TB_EXIT_REQUESTED);
}
+#ifdef CONFIG_PLUGIN
+ /*
+ * Some of instruction generators insert exit_tb explicitelly to
+ * trigger early exit from translation block. On the other hand
+ * translation loop (translator_loop()) inserts plugin callbacks
+ * after instruction is generated, but it appears as dead code
+ * because of the explicit exit_tb insert.
+ *
+ * Calling plugin_gen_insn_end() here before the exit allows
+ * plugins to receive control before translation block exits.
+ */
+ if (tcg_ctx->plugin_insn) {
+ plugin_gen_insn_end();
+ }
+#endif
+
tcg_gen_op1i(INDEX_op_exit_tb, val);
}
--
2.34.1
- [PATCH] Add support of callbacks after instructions to plugin api,
Mikhail Tyutin <=