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.anen...@yadro.com> Signed-off-by: Mikhail Tyutin <m.tyu...@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