All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [RFC v2 00/38] Plugin support
@ 2018-12-09 19:37 Emilio G. Cota
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 01/38] trace: expand mem_info:size_shift to 3 bits Emilio G. Cota
                   ` (38 more replies)
  0 siblings, 39 replies; 63+ messages in thread
From: Emilio G. Cota @ 2018-12-09 19:37 UTC (permalink / raw)
  To: qemu-devel; +Cc: Alex Bennée, Richard Henderson, Pavel Dovgalyuk

v1: https://lists.gnu.org/archive/html/qemu-devel/2018-10/msg05682.html

Changes since v1:

- Drop the 2-pass translation. Instead, empty instrumentation
  is injected during translation. If it turns out that this
  empty instrumentation is not needed, it is removed from
  the output. For this, add 2 TCG ops that mark the beginning
  and end of this empty instrumentation.

  This is cleaner than 2-pass translation, although it
  ends up being quite a bit more code, since we have
  to copy backend TCG ops, which is tedious. Performance-wise,
  it is at worst ~9% slower (~1.3% avg) than 2-pass for SPEC06int:

    https://imgur.com/a/bUNox3H

  This is for an "empty" plugin (also added to tests/plugin/empty.c).
  That is, it subscribes to TB translation events and does nothing
  with them (i.e. no execution-time subscriptions).
  This means the empty instrumentation has to be injected and then
  removed, which is the worst-case scenario since all the injection
  work is wasted.

- Add QTAILQ_REMOVE_SEVERAL, which helps speed up the removal
  of empty instrumentation.

- Drop the "TCG runtime helper" support. We do not need it
  for empty instrumentation; we just replace the function pointer
  in the copied "call" op directly.
  + To detect when an instruction uses helpers, just strncmp
  the helper's name against "plugin_".

- Drop tb->plugin_mask. Instead, read cpu->plugin_mask from
  translator_loop.

- Drop the xxhash patches, since I submitted those as a separate
  series.

- Move a lot of plugin-related code from translator.c to
  plugin-gen.c, leaving only a few function calls in translator.c.

- Add support for only subscribing to an instruction's reads or
  writes. This is implemented via a flag added to the memory
  registration functions of the public API.

- Disentangle callbacks into separate arrays. Instead of just
  having 3 arrays (tb, insn and mem callbacks), have 5 arrays
  (tb, insn, virt. mem, hostaddr mem) of 2 arrays each (udata_cb
  and inline). This takes a bit more space per TB, but note that
  this struct is allocated only once in each TCGContext. OTOH,
  it makes the code much simpler. The union in struct dyn_cb
  remains, since for instrumenting memory accesses from helpers
  we still coalesce all types of memory callbacks into a single
  array.

- Add get_page_addr_code_hostp to get the host address of code
  from common code. Use this to export the host address of
  instructions (qemu_plugin_insn_haddr() added to the public API).

- Define TCGMemOp MO_HADDR. If set, the TCG backend copies on
  a TLB hit the corresponding host address to env->hostaddr.
  This allows us to only do this copy when needed.

- Use helpers for reading and setting env->hostaddr, so that
  we minimize the use of #ifdef CONFIG_PLUGIN.

- Only define env->hostaddr if CONFIG_PLUGIN.

- Drop the trailing 'S' in CONFIG_PLUGINS: it is now CONFIG_PLUGIN.

- Drop a few optional features from the RFC:
  + lockstep execution
  + plugin-chan + guest hooks
  + virtual clock control

- Define translator_ld* helpers and use them, as suggested
  by Alex and rth. All target ISAs that use translator_loop
  have been converted, except s390x and mips.

- Do not bloat TCGContext if !CONFIG_PLUGIN.

- Define TCGContext.plugin_tb as a pointer, instead of the
  whole struct.

- Test on 32-bit and 64-bit hosts (i386, x86_64, ppc64, aarch64).

- Add cpu_in_exclusive_work_context() and use it in tb_flush(),
  as suggested by Alex.

- configure fixes, including MacOSX builds thanks to Roman's help.

- Remove macros in atomic_template.h, as suggested by Alex.
  Turns out they aren't needed, inlines are enough.

- Fixed a bug by which cpu->plugin_mem was not being cleared
  if the instruction that used helpers was the last one in
  a TB (e.g. an exception). Fix it by adding checks (1) when
  returning from longjmp, and (2) when finishing a TB from
  tcg, so that we're sure to leave cpu->plugin_mem
  in a good state. (I noticed the bug by uninstalling a plugin
  that had registered memory callbacks, which resulted in
  callbacks to the uninstalled [dlclose'd] plugin.)

- Make sure tcg_ctx->plugin_mem_cb is always NULL after finishing
  the translation of a TB. This fixes a bug on uninstall.

- Do not abort when qemu_plugin_uninstall is called more than
  once. This is actually quite common, so just silently return
  on subsequent calls to uninstall.

- Drop the "qemu"/QEMU from some overly long function/macro
  names. This applies to qemu-internal files, of course.

- Keep the plugin's argument array in memory until the plugin
  is uninstalled, so that plugins don't have to strdup their
  arguments.

- Drop nargs argument from tcg_op_insert_before/after; it's
  unused.

- Rename plugin-api.h to qemu-plugin.h, which is the same name
  it gets in the final destination (after `make install').

- Add insn_inline function to the API.

- Add some sample plugins to tests/plugin.

You can fetch this series from:
  https://github.com/cota/qemu/tree/plugin-v2

Thanks,

		Emilio
---
 .gitignore                                |    2 +
 Makefile                                  |    8 +-
 Makefile.target                           |   18 +
 accel/tcg/Makefile.objs                   |    1 +
 accel/tcg/atomic_template.h               |  117 +++-
 accel/tcg/cpu-exec.c                      |    2 +
 accel/tcg/cputlb.c                        |   23 +-
 accel/tcg/plugin-gen.c                    | 1085 +++++++++++++++++++++++++++++
 accel/tcg/plugin-helpers.h                |    6 +
 accel/tcg/softmmu_template.h              |   43 +-
 accel/tcg/translate-all.c                 |   15 +-
 accel/tcg/translator.c                    |   16 +
 bsd-user/syscall.c                        |   12 +
 configure                                 |   86 ++-
 cpus-common.c                             |    2 +
 cpus.c                                    |   10 +
 exec.c                                    |    2 +
 include/exec/cpu-defs.h                   |    9 +
 include/exec/cpu_ldst.h                   |    9 +
 include/exec/cpu_ldst_template.h          |   43 +-
 include/exec/cpu_ldst_useronly_template.h |   42 +-
 include/exec/exec-all.h                   |   13 +
 include/exec/helper-gen.h                 |    1 +
 include/exec/helper-proto.h               |    1 +
 include/exec/helper-tcg.h                 |    1 +
 include/exec/plugin-gen.h                 |   75 ++
 include/exec/translator.h                 |   28 +
 include/qemu/plugin.h                     |  253 +++++++
 include/qemu/qemu-plugin.h                |  241 +++++++
 include/qemu/queue.h                      |   10 +
 include/qom/cpu.h                         |   19 +
 linux-user/exit.c                         |    1 +
 linux-user/main.c                         |   18 +
 linux-user/syscall.c                      |    3 +
 plugin.c                                  | 1030 +++++++++++++++++++++++++++
 qemu-options.hx                           |   17 +
 qemu-plugins.symbols                      |   34 +
 qom/cpu.c                                 |    2 +
 target/alpha/translate.c                  |    2 +-
 target/arm/translate-a64.c                |    2 +
 target/arm/translate.c                    |    8 +-
 target/hppa/translate.c                   |    2 +-
 target/i386/translate.c                   |   10 +-
 target/m68k/translate.c                   |    2 +-
 target/openrisc/translate.c               |    2 +-
 target/ppc/translate.c                    |    8 +-
 target/riscv/translate.c                  |    2 +-
 target/sh4/translate.c                    |    2 +-
 target/sparc/translate.c                  |    2 +-
 target/xtensa/translate.c                 |    4 +-
 tcg/README                                |    2 +-
 tcg/i386/tcg-target.inc.c                 |    7 +
 tcg/optimize.c                            |    4 +-
 tcg/tcg-op.c                              |   44 +-
 tcg/tcg-op.h                              |   16 +
 tcg/tcg-opc.h                             |    3 +
 tcg/tcg.c                                 |   27 +-
 tcg/tcg.h                                 |   32 +-
 tests/plugin/Makefile                     |   28 +
 tests/plugin/bb.c                         |   66 ++
 tests/plugin/empty.c                      |   30 +
 tests/plugin/insn.c                       |   63 ++
 tests/plugin/mem.c                        |   93 +++
 trace-events                              |    2 +-
 vl.c                                      |   11 +
 65 files changed, 3653 insertions(+), 119 deletions(-)

^ permalink raw reply	[flat|nested] 63+ messages in thread

* [Qemu-devel] [RFC v2 01/38] trace: expand mem_info:size_shift to 3 bits
  2018-12-09 19:37 [Qemu-devel] [RFC v2 00/38] Plugin support Emilio G. Cota
@ 2018-12-09 19:37 ` Emilio G. Cota
  2019-01-24 14:42   ` Alex Bennée
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 02/38] tcg/README: fix typo s/afterwise/afterwards/ Emilio G. Cota
                   ` (37 subsequent siblings)
  38 siblings, 1 reply; 63+ messages in thread
From: Emilio G. Cota @ 2018-12-09 19:37 UTC (permalink / raw)
  To: qemu-devel; +Cc: Alex Bennée, Richard Henderson, Pavel Dovgalyuk

This will allow us to trace 16B-long memory accesses.

While at it, add some defines for the mem_info bits and simplify
trace_mem_get_info by making it a wrapper around trace_mem_build_info.

Signed-off-by: Emilio G. Cota <cota@braap.org>
---
 trace-events | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/trace-events b/trace-events
index 4fd2cb4b97..9d65d472d2 100644
--- a/trace-events
+++ b/trace-events
@@ -151,7 +151,7 @@ vcpu guest_cpu_reset(void)
 # Access information can be parsed as:
 #
 # struct mem_info {
-#     uint8_t size_shift : 2; /* interpreted as "1 << size_shift" bytes */
+#     uint8_t size_shift : 3; /* interpreted as "1 << size_shift" bytes */
 #     bool    sign_extend: 1; /* sign-extended */
 #     uint8_t endianness : 1; /* 0: little, 1: big */
 #     bool    store      : 1; /* wheter it's a store operation */
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [Qemu-devel] [RFC v2 02/38] tcg/README: fix typo s/afterwise/afterwards/
  2018-12-09 19:37 [Qemu-devel] [RFC v2 00/38] Plugin support Emilio G. Cota
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 01/38] trace: expand mem_info:size_shift to 3 bits Emilio G. Cota
@ 2018-12-09 19:37 ` Emilio G. Cota
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 03/38] cpu: introduce cpu_in_exclusive_work_context() Emilio G. Cota
                   ` (36 subsequent siblings)
  38 siblings, 0 replies; 63+ messages in thread
From: Emilio G. Cota @ 2018-12-09 19:37 UTC (permalink / raw)
  To: qemu-devel; +Cc: Alex Bennée, Richard Henderson, Pavel Dovgalyuk

Afterwise is "wise after the fact", as in "hindsight".
Here we meant "afterwards" (as in "subsequently"). Fix it.

Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
Signed-off-by: Emilio G. Cota <cota@braap.org>
---
 tcg/README | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tcg/README b/tcg/README
index d22ee084b8..3fa8a7059f 100644
--- a/tcg/README
+++ b/tcg/README
@@ -101,7 +101,7 @@ This can be overridden using the following function modifiers:
   canonical locations before calling the helper.
 - TCG_CALL_NO_WRITE_GLOBALS means that the helper does not modify any globals.
   They will only be saved to their canonical location before calling helpers,
-  but they won't be reloaded afterwise.
+  but they won't be reloaded afterwards.
 - TCG_CALL_NO_SIDE_EFFECTS means that the call to the function is removed if
   the return value is not used.
 
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [Qemu-devel] [RFC v2 03/38] cpu: introduce cpu_in_exclusive_work_context()
  2018-12-09 19:37 [Qemu-devel] [RFC v2 00/38] Plugin support Emilio G. Cota
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 01/38] trace: expand mem_info:size_shift to 3 bits Emilio G. Cota
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 02/38] tcg/README: fix typo s/afterwise/afterwards/ Emilio G. Cota
@ 2018-12-09 19:37 ` Emilio G. Cota
  2019-01-24 14:44   ` Alex Bennée
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 04/38] translate-all: use cpu_in_exclusive_work_context() in tb_flush Emilio G. Cota
                   ` (35 subsequent siblings)
  38 siblings, 1 reply; 63+ messages in thread
From: Emilio G. Cota @ 2018-12-09 19:37 UTC (permalink / raw)
  To: qemu-devel; +Cc: Alex Bennée, Richard Henderson, Pavel Dovgalyuk

Suggested-by: Alex Bennée <alex.bennee@linaro.org>
Signed-off-by: Emilio G. Cota <cota@braap.org>
---
 include/qom/cpu.h | 13 +++++++++++++
 cpus-common.c     |  2 ++
 2 files changed, 15 insertions(+)

diff --git a/include/qom/cpu.h b/include/qom/cpu.h
index 772cc960fe..fab18089db 100644
--- a/include/qom/cpu.h
+++ b/include/qom/cpu.h
@@ -349,6 +349,7 @@ struct CPUState {
     bool thread_kicked;
     bool crash_occurred;
     bool exit_request;
+    bool in_exclusive_work_context;
     uint32_t cflags_next_tb;
     /* updates protected by BQL */
     uint32_t interrupt_request;
@@ -913,6 +914,18 @@ void async_run_on_cpu_no_bql(CPUState *cpu, run_on_cpu_func func,
  */
 void async_safe_run_on_cpu(CPUState *cpu, run_on_cpu_func func, run_on_cpu_data data);
 
+/**
+ * cpu_in_exclusive_work_context()
+ * @cpu: The vCPU to check
+ *
+ * Returns true if @cpu is an exclusive work context, which has
+ * previously been queued via async_safe_run_on_cpu().
+ */
+static inline bool cpu_in_exclusive_work_context(const CPUState *cpu)
+{
+    return cpu->in_exclusive_work_context;
+}
+
 /**
  * qemu_get_cpu:
  * @index: The CPUState@cpu_index value of the CPU to obtain.
diff --git a/cpus-common.c b/cpus-common.c
index 232cb12c46..d6ea42c80c 100644
--- a/cpus-common.c
+++ b/cpus-common.c
@@ -370,7 +370,9 @@ static void process_queued_cpu_work_locked(CPUState *cpu)
                 qemu_mutex_unlock_iothread();
             }
             start_exclusive();
+            cpu->in_exclusive_work_context = true;
             wi->func(cpu, wi->data);
+            cpu->in_exclusive_work_context = false;
             end_exclusive();
             if (has_bql) {
                 qemu_mutex_lock_iothread();
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [Qemu-devel] [RFC v2 04/38] translate-all: use cpu_in_exclusive_work_context() in tb_flush
  2018-12-09 19:37 [Qemu-devel] [RFC v2 00/38] Plugin support Emilio G. Cota
                   ` (2 preceding siblings ...)
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 03/38] cpu: introduce cpu_in_exclusive_work_context() Emilio G. Cota
@ 2018-12-09 19:37 ` Emilio G. Cota
  2019-01-24 14:44   ` Alex Bennée
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 05/38] plugin: add user-facing API Emilio G. Cota
                   ` (34 subsequent siblings)
  38 siblings, 1 reply; 63+ messages in thread
From: Emilio G. Cota @ 2018-12-09 19:37 UTC (permalink / raw)
  To: qemu-devel; +Cc: Alex Bennée, Richard Henderson, Pavel Dovgalyuk

tb_flush will be called by the plugin module from a safe
work environment. Prepare for that.

Suggested-by: Alex Bennée <alex.bennee@linaro.org>
Signed-off-by: Emilio G. Cota <cota@braap.org>
---
 accel/tcg/translate-all.c | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c
index 038d82fdb5..62d5e13185 100644
--- a/accel/tcg/translate-all.c
+++ b/accel/tcg/translate-all.c
@@ -1269,8 +1269,13 @@ void tb_flush(CPUState *cpu)
 {
     if (tcg_enabled()) {
         unsigned tb_flush_count = atomic_mb_read(&tb_ctx.tb_flush_count);
-        async_safe_run_on_cpu(cpu, do_tb_flush,
-                              RUN_ON_CPU_HOST_INT(tb_flush_count));
+
+        if (cpu_in_exclusive_work_context(cpu)) {
+            do_tb_flush(cpu, RUN_ON_CPU_HOST_INT(tb_flush_count));
+        } else {
+            async_safe_run_on_cpu(cpu, do_tb_flush,
+                                  RUN_ON_CPU_HOST_INT(tb_flush_count));
+        }
     }
 }
 
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [Qemu-devel] [RFC v2 05/38] plugin: add user-facing API
  2018-12-09 19:37 [Qemu-devel] [RFC v2 00/38] Plugin support Emilio G. Cota
                   ` (3 preceding siblings ...)
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 04/38] translate-all: use cpu_in_exclusive_work_context() in tb_flush Emilio G. Cota
@ 2018-12-09 19:37 ` Emilio G. Cota
  2018-12-14 15:57   ` Aaron Lindsay
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 06/38] plugin: add core code Emilio G. Cota
                   ` (33 subsequent siblings)
  38 siblings, 1 reply; 63+ messages in thread
From: Emilio G. Cota @ 2018-12-09 19:37 UTC (permalink / raw)
  To: qemu-devel; +Cc: Alex Bennée, Richard Henderson, Pavel Dovgalyuk

Add the API first to ease review.

Signed-off-by: Emilio G. Cota <cota@braap.org>
---
 include/qemu/qemu-plugin.h | 241 +++++++++++++++++++++++++++++++++++++
 1 file changed, 241 insertions(+)
 create mode 100644 include/qemu/qemu-plugin.h

diff --git a/include/qemu/qemu-plugin.h b/include/qemu/qemu-plugin.h
new file mode 100644
index 0000000000..6c67211900
--- /dev/null
+++ b/include/qemu/qemu-plugin.h
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2017, Emilio G. Cota <cota@braap.org>
+ *
+ * License: GNU GPL, version 2 or later.
+ *   See the COPYING file in the top-level directory.
+ */
+#ifndef QEMU_PLUGIN_API_H
+#define QEMU_PLUGIN_API_H
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+/*
+ * For best performance, build the plugin with -fvisibility=hidden so that
+ * QEMU_PLUGIN_LOCAL is implicit. Then, just mark qemu_plugin_install with
+ * QEMU_PLUGIN_EXPORT. For more info, see
+ *   https://gcc.gnu.org/wiki/Visibility
+ */
+#if defined _WIN32 || defined __CYGWIN__
+  #ifdef BUILDING_DLL
+    #define QEMU_PLUGIN_EXPORT __declspec(dllexport)
+  #else
+    #define QEMU_PLUGIN_EXPORT __declspec(dllimport)
+  #endif
+  #define QEMU_PLUGIN_LOCAL
+#else
+  #if __GNUC__ >= 4
+    #define QEMU_PLUGIN_EXPORT __attribute__((visibility("default")))
+    #define QEMU_PLUGIN_LOCAL  __attribute__((visibility("hidden")))
+  #else
+    #define QEMU_PLUGIN_EXPORT
+    #define QEMU_PLUGIN_LOCAL
+  #endif
+#endif
+
+typedef uint64_t qemu_plugin_id_t;
+
+/**
+ * qemu_plugin_install - Install a plugin
+ * @id: this plugin's opaque ID
+ * @argc: number of arguments
+ * @argv: array of arguments (@argc elements)
+ *
+ * All plugins must export this symbol.
+ *
+ * Note: @argv is freed after this function returns.
+ */
+QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, int argc,
+                                           char **argv);
+
+typedef void (*qemu_plugin_uninstall_cb_t)(qemu_plugin_id_t id);
+
+/**
+ * qemu_plugin_uninstall - Uninstall a plugin
+ * @id: this plugin's opaque ID
+ * @cb: callback to be called once the plugin has been removed
+ *
+ * Do NOT assume that the plugin has been uninstalled once this
+ * function returns. Plugins are uninstalled asynchronously,
+ * and therefore the given plugin might still receive callbacks
+ * from prior subscriptions _until_ @cb is called.
+ */
+void qemu_plugin_uninstall(qemu_plugin_id_t id, qemu_plugin_uninstall_cb_t cb);
+
+typedef void (*qemu_plugin_simple_cb_t)(qemu_plugin_id_t id);
+
+typedef void (*qemu_plugin_udata_cb_t)(qemu_plugin_id_t id, void *userdata);
+
+typedef void (*qemu_plugin_vcpu_simple_cb_t)(qemu_plugin_id_t id,
+                                             unsigned int vcpu_index);
+
+typedef void (*qemu_plugin_vcpu_udata_cb_t)(unsigned int vcpu_index,
+                                            void *userdata);
+
+/**
+ * qemu_plugin_register_vcpu_init_cb - register a vCPU initialization callback
+ * @id: plugin ID
+ * @cb: callback function
+ *
+ * The @cb function is called every time a vCPU is initialized.
+ *
+ * See also: qemu_plugin_register_vcpu_exit_cb()
+ */
+void qemu_plugin_register_vcpu_init_cb(qemu_plugin_id_t id,
+                                       qemu_plugin_vcpu_simple_cb_t cb);
+
+/**
+ * qemu_plugin_register_vcpu_exit_cb - register a vCPU exit callback
+ * @id: plugin ID
+ * @cb: callback function
+ *
+ * The @cb function is called every time a vCPU exits.
+ *
+ * See also: qemu_plugin_register_vcpu_init_cb()
+ */
+void qemu_plugin_register_vcpu_exit_cb(qemu_plugin_id_t id,
+                                       qemu_plugin_vcpu_simple_cb_t cb);
+
+void qemu_plugin_register_vcpu_idle_cb(qemu_plugin_id_t id,
+                                       qemu_plugin_vcpu_simple_cb_t cb);
+
+void qemu_plugin_register_vcpu_resume_cb(qemu_plugin_id_t id,
+                                         qemu_plugin_vcpu_simple_cb_t cb);
+
+struct qemu_plugin_tb;
+struct qemu_plugin_insn;
+
+enum qemu_plugin_cb_flags {
+    QEMU_PLUGIN_CB_NO_REGS, /* callback does not access the CPU's regs */
+    QEMU_PLUGIN_CB_R_REGS,  /* callback reads the CPU's regs */
+    QEMU_PLUGIN_CB_RW_REGS, /* callback reads and writes the CPU's regs */
+};
+
+enum qemu_plugin_mem_rw {
+    QEMU_PLUGIN_MEM_R = 1,
+    QEMU_PLUGIN_MEM_W,
+    QEMU_PLUGIN_MEM_RW,
+};
+
+typedef void (*qemu_plugin_vcpu_tb_trans_cb_t)(qemu_plugin_id_t id,
+                                               unsigned int vcpu_index,
+                                               struct qemu_plugin_tb *tb);
+
+void qemu_plugin_register_vcpu_tb_trans_cb(qemu_plugin_id_t id,
+                                           qemu_plugin_vcpu_tb_trans_cb_t cb);
+
+/* can only call from tb_trans_cb callback */
+void qemu_plugin_register_vcpu_tb_exec_cb(struct qemu_plugin_tb *tb,
+                                          qemu_plugin_vcpu_udata_cb_t cb,
+                                          enum qemu_plugin_cb_flags flags,
+                                          void *userdata);
+
+enum qemu_plugin_op {
+    QEMU_PLUGIN_INLINE_ADD_U64,
+};
+
+void qemu_plugin_register_vcpu_tb_exec_inline(struct qemu_plugin_tb *tb,
+                                              enum qemu_plugin_op op,
+                                              void *ptr, uint64_t imm);
+
+void qemu_plugin_register_vcpu_insn_exec_cb(struct qemu_plugin_insn *insn,
+                                            qemu_plugin_vcpu_udata_cb_t cb,
+                                            enum qemu_plugin_cb_flags flags,
+                                            void *userdata);
+
+void qemu_plugin_register_vcpu_insn_exec_inline(struct qemu_plugin_insn *insn,
+                                                enum qemu_plugin_op op,
+                                                void *ptr, uint64_t imm);
+
+typedef uint32_t qemu_plugin_meminfo_t;
+
+unsigned qemu_plugin_mem_size_shift(qemu_plugin_meminfo_t info);
+bool qemu_plugin_mem_is_sign_extended(qemu_plugin_meminfo_t info);
+bool qemu_plugin_mem_is_big_endian(qemu_plugin_meminfo_t info);
+bool qemu_plugin_mem_is_store(qemu_plugin_meminfo_t info);
+
+typedef void
+(*qemu_plugin_vcpu_mem_cb_t)(unsigned int vcpu_index,
+                             qemu_plugin_meminfo_t info, uint64_t vaddr,
+                             void *userdata);
+
+typedef void
+(*qemu_plugin_vcpu_mem_haddr_cb_t)(unsigned int vcpu_index,
+                                   qemu_plugin_meminfo_t info, uint64_t vaddr,
+                                   void *haddr, void *userdata);
+
+void qemu_plugin_register_vcpu_mem_cb(struct qemu_plugin_insn *insn,
+                                      qemu_plugin_vcpu_mem_cb_t cb,
+                                      enum qemu_plugin_cb_flags flags,
+                                      enum qemu_plugin_mem_rw rw,
+                                      void *userdata);
+
+void qemu_plugin_register_vcpu_mem_haddr_cb(struct qemu_plugin_insn *insn,
+                                            qemu_plugin_vcpu_mem_haddr_cb_t cb,
+                                            enum qemu_plugin_cb_flags flags,
+                                            enum qemu_plugin_mem_rw rw,
+                                            void *userdata);
+
+void qemu_plugin_register_vcpu_mem_inline(struct qemu_plugin_insn *insn,
+                                          enum qemu_plugin_mem_rw rw,
+                                          enum qemu_plugin_op op, void *ptr,
+                                          uint64_t imm);
+
+uint64_t qemu_plugin_ram_addr_from_host(void *haddr);
+
+typedef void
+(*qemu_plugin_vcpu_syscall_cb_t)(qemu_plugin_id_t id, unsigned int vcpu_index,
+                                 int64_t num, uint64_t a1, uint64_t a2,
+                                 uint64_t a3, uint64_t a4, uint64_t a5,
+                                 uint64_t a6, uint64_t a7, uint64_t a8);
+
+void qemu_plugin_register_vcpu_syscall_cb(qemu_plugin_id_t id,
+                                          qemu_plugin_vcpu_syscall_cb_t cb);
+
+typedef void
+(*qemu_plugin_vcpu_syscall_ret_cb_t)(qemu_plugin_id_t id, unsigned int vcpu_idx,
+                                     int64_t num, int64_t ret);
+
+void
+qemu_plugin_register_vcpu_syscall_ret_cb(qemu_plugin_id_t id,
+                                         qemu_plugin_vcpu_syscall_ret_cb_t cb);
+
+size_t qemu_plugin_tb_n_insns(const struct qemu_plugin_tb *tb);
+
+uint64_t qemu_plugin_tb_vaddr(const struct qemu_plugin_tb *tb);
+
+struct qemu_plugin_insn *
+qemu_plugin_tb_get_insn(const struct qemu_plugin_tb *tb, size_t idx);
+
+const void *qemu_plugin_insn_data(const struct qemu_plugin_insn *insn);
+
+size_t qemu_plugin_insn_size(const struct qemu_plugin_insn *insn);
+
+uint64_t qemu_plugin_insn_vaddr(const struct qemu_plugin_insn *insn);
+void *qemu_plugin_insn_haddr(const struct qemu_plugin_insn *insn);
+
+/**
+ * qemu_plugin_vcpu_for_each - iterate over the existing vCPU
+ * @id: plugin ID
+ * @cb: callback function
+ *
+ * The @cb function is called once for each existing vCPU.
+ *
+ * See also: qemu_plugin_register_vcpu_init_cb()
+ */
+void qemu_plugin_vcpu_for_each(qemu_plugin_id_t id,
+                               qemu_plugin_vcpu_simple_cb_t cb);
+
+void qemu_plugin_register_flush_cb(qemu_plugin_id_t id,
+                                   qemu_plugin_simple_cb_t cb);
+
+void qemu_plugin_register_atexit_cb(qemu_plugin_id_t id,
+                                    qemu_plugin_udata_cb_t cb, void *userdata);
+
+/* returns -1 in user-mode */
+int qemu_plugin_n_vcpus(void);
+
+/* returns -1 in user-mode */
+int qemu_plugin_n_max_vcpus(void);
+
+#endif /* QEMU_PLUGIN_API_H */
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [Qemu-devel] [RFC v2 06/38] plugin: add core code
  2018-12-09 19:37 [Qemu-devel] [RFC v2 00/38] Plugin support Emilio G. Cota
                   ` (4 preceding siblings ...)
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 05/38] plugin: add user-facing API Emilio G. Cota
@ 2018-12-09 19:37 ` Emilio G. Cota
  2018-12-10 11:37   ` Pavel Dovgalyuk
  2019-01-24 15:57   ` Alex Bennée
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 07/38] queue: add QTAILQ_REMOVE_SEVERAL Emilio G. Cota
                   ` (32 subsequent siblings)
  38 siblings, 2 replies; 63+ messages in thread
From: Emilio G. Cota @ 2018-12-09 19:37 UTC (permalink / raw)
  To: qemu-devel; +Cc: Alex Bennée, Richard Henderson, Pavel Dovgalyuk

Some design requirements/goals:

- Make sure we cannot deadlock, particularly under MTTCG. For this,
  we acquire a lock when called from plugin code, and keep
  RCU lists of callbacks so that we do not have to hold the lock
  when calling the callbacks. This is also for performance, since
  some callbacks (e.g. memory access callbacks) might be called very
  frequently.
  * A consequence of this is that we keep our own list of CPUs, so that
    we do not have to worry about locking order wrt cpu_list_lock.
  * Use a recursive lock, since we can get registration calls from
    callbacks.

- Support as many plugins as the user wants (e.g. -plugin foo -plugin bar),
  just like other tools (e.g. dynamorio) do.

- Support the uninstallation of a plugin at any time (e.g. from plugin
  callbacks).

- Avoid malicious plugins from abusing the API. This is done by:
  * Adding a qemu_plugin_id_t that all calls need to use. This is a unique
    id per plugin.
  * Hiding CPUState * under cpu_index. Plugin code can keep per-vcpu
    data by using said index (say to index an array).
  * Only exporting the relevant qemu_plugin symbols to the plugins by
    passing --dynamic-file to the linker (when supported), instead of
    exporting all symbols with -rdynamic.

- Performance: registering/unregistering callbacks is "slow", since
  it takes a lock. But this is very infrequent; we want performance when
  calling (or not calling) callbacks, not when registering them.
  Using RCU is great for this. The only difficulty is when uninstalling
  a plugin, where some callbacks might still be called after the
  uninstall returns. An alternative would be to use r/w locks, but that
  would complicate code quite a bit for very little gain. In any case,
  I suspect most plugins will just run until QEMU exits.

Some design decisions:
- I considered registering callbacks per-vcpu, but really I don't see the
  use case for it (would complicate the API and 99% of plugins won't care, so
  I'd rather make that 1% slower by letting them discard unwanted callbacks).

- Last, 'plugin' vs. 'instrumentation' naming: I think instrumentation is a
  subset of the functionality that plugins can provide. IOW, in the future
  not all plugins might be considered instrumentation, even if currently
  my goal is to use them for that purpose.

Signed-off-by: Emilio G. Cota <cota@braap.org>
---
 Makefile              |    7 +-
 Makefile.target       |    2 +
 include/qemu/plugin.h |  253 ++++++++++
 include/qom/cpu.h     |    6 +
 plugin.c              | 1030 +++++++++++++++++++++++++++++++++++++++++
 5 files changed, 1297 insertions(+), 1 deletion(-)
 create mode 100644 include/qemu/plugin.h
 create mode 100644 plugin.c

diff --git a/Makefile b/Makefile
index f2947186a4..9cb3076d84 100644
--- a/Makefile
+++ b/Makefile
@@ -862,8 +862,10 @@ ifneq (,$(findstring qemu-ga,$(TOOLS)))
 endif
 endif
 
+install-includedir:
+	$(INSTALL_DIR) "$(DESTDIR)$(includedir)"
 
-install: all $(if $(BUILD_DOCS),install-doc) install-datadir install-localstatedir
+install: all $(if $(BUILD_DOCS),install-doc) install-datadir install-localstatedir install-includedir
 ifneq ($(TOOLS),)
 	$(call install-prog,$(subst qemu-ga,qemu-ga$(EXESUF),$(TOOLS)),$(DESTDIR)$(bindir))
 endif
@@ -885,6 +887,9 @@ ifneq ($(BLOBS),)
 endif
 ifdef CONFIG_GTK
 	$(MAKE) -C po $@
+endif
+ifeq ($(CONFIG_PLUGIN),y)
+	$(INSTALL_DATA) $(SRC_PATH)/include/qemu/qemu-plugin.h "$(DESTDIR)$(includedir)/qemu-plugin.h"
 endif
 	$(INSTALL_DIR) "$(DESTDIR)$(qemu_datadir)/keymaps"
 	set -e; for x in $(KEYMAPS); do \
diff --git a/Makefile.target b/Makefile.target
index 4d56298bbf..75637c285c 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -107,6 +107,8 @@ obj-y += target/$(TARGET_BASE_ARCH)/
 obj-y += disas.o
 obj-$(call notempty,$(TARGET_XML_FILES)) += gdbstub-xml.o
 
+obj-$(CONFIG_PLUGINS) += plugin.o
+
 #########################################################
 # Linux user emulator target
 
diff --git a/include/qemu/plugin.h b/include/qemu/plugin.h
new file mode 100644
index 0000000000..e483d0eaa8
--- /dev/null
+++ b/include/qemu/plugin.h
@@ -0,0 +1,253 @@
+/*
+ * Copyright (C) 2017, Emilio G. Cota <cota@braap.org>
+ *
+ * License: GNU GPL, version 2 or later.
+ *   See the COPYING file in the top-level directory.
+ */
+#ifndef QEMU_PLUGIN_H
+#define QEMU_PLUGIN_H
+
+#include "qemu/config-file.h"
+#include "qemu/qemu-plugin.h"
+#include "qemu/error-report.h"
+#include "qemu/queue.h"
+#include "qemu/option.h"
+
+/*
+ * Option parsing/processing.
+ * Note that we can load an arbitrary number of plugins.
+ */
+struct qemu_plugin_desc;
+QTAILQ_HEAD(qemu_plugin_list, qemu_plugin_desc);
+
+#ifdef CONFIG_PLUGIN
+extern QemuOptsList qemu_plugin_opts;
+
+static inline void qemu_plugin_add_opts(void)
+{
+    qemu_add_opts(&qemu_plugin_opts);
+}
+
+void qemu_plugin_opt_parse(const char *optarg, struct qemu_plugin_list *head);
+int qemu_plugin_load_list(struct qemu_plugin_list *head);
+#else /* !CONFIG_PLUGIN */
+static inline void qemu_plugin_add_opts(void)
+{ }
+
+static inline void qemu_plugin_opt_parse(const char *optarg,
+                                         struct qemu_plugin_list *head)
+{
+    error_report("plugin interface not enabled in this build");
+    exit(1);
+}
+
+static inline int qemu_plugin_load_list(struct qemu_plugin_list *head)
+{
+    return 0;
+}
+#endif /* !CONFIG_PLUGIN */
+
+/*
+ * Events that plugins can subscribe to.
+ */
+enum qemu_plugin_event {
+    QEMU_PLUGIN_EV_VCPU_INIT,
+    QEMU_PLUGIN_EV_VCPU_EXIT,
+    QEMU_PLUGIN_EV_VCPU_TB_TRANS,
+    QEMU_PLUGIN_EV_VCPU_IDLE,
+    QEMU_PLUGIN_EV_VCPU_RESUME,
+    QEMU_PLUGIN_EV_VCPU_SYSCALL,
+    QEMU_PLUGIN_EV_VCPU_SYSCALL_RET,
+    QEMU_PLUGIN_EV_FLUSH,
+    QEMU_PLUGIN_EV_ATEXIT,
+    QEMU_PLUGIN_EV_MAX,
+};
+
+union qemu_plugin_cb_sig {
+    qemu_plugin_simple_cb_t          simple;
+    qemu_plugin_udata_cb_t           udata;
+    qemu_plugin_vcpu_simple_cb_t     vcpu_simple;
+    qemu_plugin_vcpu_udata_cb_t      vcpu_udata;
+    qemu_plugin_vcpu_tb_trans_cb_t   vcpu_tb_trans;
+    qemu_plugin_vcpu_mem_cb_t        vcpu_mem;
+    qemu_plugin_vcpu_mem_haddr_cb_t  vcpu_mem_haddr;
+    qemu_plugin_vcpu_syscall_cb_t    vcpu_syscall;
+    qemu_plugin_vcpu_syscall_ret_cb_t vcpu_syscall_ret;
+    void *generic;
+};
+
+enum plugin_dyn_cb_type {
+    PLUGIN_CB_INSN,
+    PLUGIN_CB_MEM,
+    PLUGIN_CB_HADDR,
+    PLUGIN_N_CB_TYPES,
+};
+
+enum plugin_dyn_cb_subtype {
+    PLUGIN_CB_REGULAR,
+    PLUGIN_CB_INLINE,
+    PLUGIN_N_CB_SUBTYPES,
+};
+
+/*
+ * A dynamic callback has an insertion point that is determined at run-time.
+ * Usually the insertion point is somewhere in the code cache; think for
+ * instance of a callback to be called upon the execution of a particular TB.
+ */
+struct qemu_plugin_dyn_cb {
+    union qemu_plugin_cb_sig f;
+    void *userp;
+    unsigned tcg_flags;
+    enum plugin_dyn_cb_type type;
+    /* @rw applies to mem callbacks only (both regular and inline) */
+    enum qemu_plugin_mem_rw rw;
+    /* fields specific to each dyn_cb type go here */
+    union {
+        struct {
+            bool haddr;
+        } mem;
+        struct {
+            enum qemu_plugin_op op;
+            uint64_t imm;
+        } inline_insn;
+    };
+};
+
+struct qemu_plugin_dyn_cb_arr {
+    struct qemu_plugin_dyn_cb *data;
+    size_t n;
+    size_t capacity;
+};
+
+struct qemu_plugin_insn {
+    void *data;
+    size_t size;
+    size_t capacity;
+    uint64_t vaddr;
+    void *haddr;
+    struct qemu_plugin_dyn_cb_arr cbs[PLUGIN_N_CB_TYPES][PLUGIN_N_CB_SUBTYPES];
+    bool calls_helpers;
+    bool mem_helper;
+};
+
+struct qemu_plugin_tb {
+    struct qemu_plugin_insn *insns;
+    size_t n;
+    size_t capacity;
+    uint64_t vaddr;
+    uint64_t vaddr2;
+    void *haddr1;
+    void *haddr2;
+    struct qemu_plugin_dyn_cb_arr cbs[PLUGIN_N_CB_SUBTYPES];
+};
+
+static inline
+void qemu_plugin_dyn_cb_arr_init(struct qemu_plugin_dyn_cb_arr *arr)
+{
+    arr->data = NULL;
+    arr->capacity = 0;
+}
+
+static inline
+struct qemu_plugin_insn *qemu_plugin_tb_insn_get(struct qemu_plugin_tb *tb)
+{
+    struct qemu_plugin_insn *insn;
+    int i, j;
+
+    if (unlikely(tb->n == tb->capacity)) {
+        tb->insns = g_renew(struct qemu_plugin_insn, tb->insns, ++tb->capacity);
+        insn = &tb->insns[tb->capacity - 1];
+        insn->data = NULL;
+        insn->capacity = 0;
+        for (i = 0; i < PLUGIN_N_CB_TYPES; i++) {
+            for (j = 0; j < PLUGIN_N_CB_SUBTYPES; j++) {
+                qemu_plugin_dyn_cb_arr_init(&insn->cbs[i][j]);
+            }
+        }
+    }
+    insn = &tb->insns[tb->n++];
+    insn->size = 0;
+    insn->calls_helpers = false;
+    insn->mem_helper = false;
+
+    for (i = 0; i < PLUGIN_N_CB_TYPES; i++) {
+        for (j = 0; j < PLUGIN_N_CB_SUBTYPES; j++) {
+            insn->cbs[i][j].n = 0;
+        }
+    }
+
+    return insn;
+}
+
+#ifdef CONFIG_PLUGIN
+
+void qemu_plugin_vcpu_init_hook(CPUState *cpu);
+void qemu_plugin_vcpu_exit_hook(CPUState *cpu);
+void qemu_plugin_tb_trans_cb(CPUState *cpu, struct qemu_plugin_tb *tb);
+void qemu_plugin_vcpu_idle_cb(CPUState *cpu);
+void qemu_plugin_vcpu_resume_cb(CPUState *cpu);
+void
+qemu_plugin_vcpu_syscall(CPUState *cpu, int64_t num, uint64_t a1,
+                         uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5,
+                         uint64_t a6, uint64_t a7, uint64_t a8);
+void qemu_plugin_vcpu_syscall_ret(CPUState *cpu, int64_t num, int64_t ret);
+
+void qemu_plugin_vcpu_mem_cb(CPUState *cpu, uint64_t vaddr, void *haddr,
+                             uint32_t meminfo);
+
+void qemu_plugin_flush_cb(void);
+
+void qemu_plugin_atexit_cb(void);
+
+void qemu_plugin_add_dyn_cb_arr(struct qemu_plugin_dyn_cb_arr *arr);
+
+void qemu_plugin_disable_mem_helpers(CPUState *cpu);
+
+#else /* !CONFIG_PLUGIN */
+
+static inline void qemu_plugin_vcpu_init_hook(CPUState *cpu)
+{ }
+
+static inline void qemu_plugin_vcpu_exit_hook(CPUState *cpu)
+{ }
+
+static inline void qemu_plugin_tb_trans_cb(CPUState *cpu,
+                                           struct qemu_plugin_tb *tb)
+{ }
+
+static inline void qemu_plugin_vcpu_idle_cb(CPUState *cpu)
+{ }
+
+static inline void qemu_plugin_vcpu_resume_cb(CPUState *cpu)
+{ }
+
+static inline void
+qemu_plugin_vcpu_syscall(CPUState *cpu, int64_t num, uint64_t a1, uint64_t a2,
+                         uint64_t a3, uint64_t a4, uint64_t a5, uint64_t a6,
+                         uint64_t a7, uint64_t a8)
+{ }
+
+static inline
+void qemu_plugin_vcpu_syscall_ret(CPUState *cpu, int64_t num, int64_t ret)
+{ }
+
+static inline void qemu_plugin_vcpu_mem_cb(CPUState *cpu, uint64_t vaddr,
+                                           void *haddr, uint32_t meminfo)
+{ }
+
+static inline void qemu_plugin_flush_cb(void)
+{ }
+
+static inline void qemu_plugin_atexit_cb(void)
+{ }
+
+static inline
+void qemu_plugin_add_dyn_cb_arr(struct qemu_plugin_dyn_cb_arr *arr)
+{ }
+
+static inline void qemu_plugin_disable_mem_helpers(CPUState *cpu)
+{ }
+
+#endif /* !CONFIG_PLUGIN */
+
+#endif /* QEMU_PLUGIN_H */
diff --git a/include/qom/cpu.h b/include/qom/cpu.h
index fab18089db..4002a911af 100644
--- a/include/qom/cpu.h
+++ b/include/qom/cpu.h
@@ -30,6 +30,7 @@
 #include "qemu/rcu_queue.h"
 #include "qemu/queue.h"
 #include "qemu/thread.h"
+#include "qemu/plugin.h"
 
 typedef int (*WriteCoreDumpFunction)(const void *buf, size_t size,
                                      void *opaque);
@@ -326,6 +327,7 @@ struct qemu_work_item;
  * @trace_dstate_delayed: Delayed changes to trace_dstate (includes all changes
  *                        to @trace_dstate).
  * @trace_dstate: Dynamic tracing state of events for this vCPU (bitmask).
+ * @plugin_mask: Plugin event bitmap. Modified only via async work.
  * @ignore_memory_transaction_failures: Cached copy of the MachineState
  *    flag of the same name: allows the board to suppress calling of the
  *    CPU do_transaction_failed hook function.
@@ -412,6 +414,10 @@ struct CPUState {
     DECLARE_BITMAP(trace_dstate_delayed, CPU_TRACE_DSTATE_MAX_EVENTS);
     DECLARE_BITMAP(trace_dstate, CPU_TRACE_DSTATE_MAX_EVENTS);
 
+    DECLARE_BITMAP(plugin_mask, QEMU_PLUGIN_EV_MAX);
+
+    struct qemu_plugin_dyn_cb_arr *plugin_mem_cbs;
+
     /* TODO Move common fields from CPUArchState here. */
     int cpu_index;
     uint32_t can_do_io;
diff --git a/plugin.c b/plugin.c
new file mode 100644
index 0000000000..dade6dc39f
--- /dev/null
+++ b/plugin.c
@@ -0,0 +1,1030 @@
+/* plugin.c - QEMU Plugin interface
+ *
+ * Copyright (C) 2017, Emilio G. Cota <cota@braap.org>
+ *
+ * License: GNU GPL, version 2 or later.
+ *   See the COPYING file in the top-level directory.
+ */
+#include "qemu/osdep.h"
+#include "qemu/error-report.h"
+#include "qemu/config-file.h"
+#include "qapi/error.h"
+#include "qemu/option.h"
+#include "qemu/rcu_queue.h"
+#include "qemu/xxhash.h"
+#include "qemu/rcu.h"
+#include "qom/cpu.h"
+#include "exec/cpu-common.h"
+#include <dlfcn.h>
+
+#include "cpu.h"
+#include "exec/exec-all.h"
+#include "exec/helper-proto.h"
+#include "exec/plugin-gen.h"
+#include "sysemu/sysemu.h"
+#include "tcg/tcg.h"
+#include "tcg/tcg-op.h"
+#include "trace/mem-internal.h" /* mem_info macros */
+
+struct qemu_plugin_cb {
+    struct qemu_plugin_ctx *ctx;
+    union qemu_plugin_cb_sig f;
+    void *udata;
+    QLIST_ENTRY(qemu_plugin_cb) entry;
+};
+
+QLIST_HEAD(qemu_plugin_cb_head, qemu_plugin_cb);
+
+struct qemu_plugin_ctx {
+    void *handle; /* dlopen */
+    qemu_plugin_id_t id;
+    struct qemu_plugin_cb *callbacks[QEMU_PLUGIN_EV_MAX];
+    QTAILQ_ENTRY(qemu_plugin_ctx) entry;
+    qemu_plugin_uninstall_cb_t uninstall_cb;
+    /*
+     * keep a reference to @desc until uninstall, so that plugins do not have
+     * to strdup plugin args.
+     */
+    struct qemu_plugin_desc *desc;
+    bool uninstalling; /* protected by plugin.lock */
+};
+
+/* global state */
+struct qemu_plugin_state {
+    QTAILQ_HEAD(, qemu_plugin_ctx) ctxs;
+    QLIST_HEAD(, qemu_plugin_cb) cb_lists[QEMU_PLUGIN_EV_MAX];
+    /*
+     * Use the HT as a hash map by inserting k == v, which saves memory as
+     * documented by GLib. The parent struct is obtained with container_of().
+     */
+    GHashTable *id_ht;
+    /*
+     * Use the HT as a hash map. Note that we could use a list here,
+     * but with the HT we avoid adding a field to CPUState.
+     */
+    GHashTable *cpu_ht;
+    DECLARE_BITMAP(mask, QEMU_PLUGIN_EV_MAX);
+    /*
+     * @lock protects the struct as well as ctx->uninstalling.
+     * The lock must be acquired by all API ops.
+     * The lock is recursive, which greatly simplifies things, e.g.
+     * callback registration from qemu_plugin_vcpu_for_each().
+     */
+    QemuRecMutex lock;
+    /*
+     * HT of callbacks invoked from helpers. All entries are freed when
+     * the code cache is flushed.
+     */
+    struct qht dyn_cb_arr_ht;
+};
+
+/*
+ * For convenience we use a bitmap for plugin.mask, but really all we need is a
+ * u32, which is what we store in TranslationBlock.
+ */
+QEMU_BUILD_BUG_ON(QEMU_PLUGIN_EV_MAX > 32);
+
+struct qemu_plugin_desc {
+    char *path;
+    char **argv;
+    QTAILQ_ENTRY(qemu_plugin_desc) entry;
+    int argc;
+};
+
+struct qemu_plugin_parse_arg {
+    struct qemu_plugin_list *head;
+    struct qemu_plugin_desc *curr;
+};
+
+QemuOptsList qemu_plugin_opts = {
+    .name = "plugin",
+    .implied_opt_name = "file",
+    .head = QTAILQ_HEAD_INITIALIZER(qemu_plugin_opts.head),
+    .desc = {
+        /* do our own parsing to support multiple plugins */
+        { /* end of list */ }
+    },
+};
+
+typedef int (*qemu_plugin_install_func_t)(qemu_plugin_id_t, int, char **);
+
+static struct qemu_plugin_state plugin;
+
+static bool plugin_dyn_cb_arr_cmp(const void *ap, const void *bp)
+{
+    return ap == bp;
+}
+
+void qemu_plugin_add_dyn_cb_arr(struct qemu_plugin_dyn_cb_arr *arr)
+{
+    uint32_t hash = qemu_xxhash2((uint64_t)(uintptr_t)arr);
+    bool inserted;
+
+    inserted = qht_insert(&plugin.dyn_cb_arr_ht, arr, hash, NULL);
+    g_assert(inserted);
+}
+
+static bool free_dyn_cb_arr(void *p, uint32_t h, void *userp)
+{
+    struct qemu_plugin_dyn_cb_arr *arr = p;
+
+    g_free(arr->data);
+    g_free(arr);
+    return true;
+}
+
+static struct qemu_plugin_desc *plugin_find_desc(struct qemu_plugin_list *head,
+                                                 const char *path)
+{
+    struct qemu_plugin_desc *desc;
+
+    QTAILQ_FOREACH(desc, head, entry) {
+        if (strcmp(desc->path, path) == 0) {
+            return desc;
+        }
+    }
+    return NULL;
+}
+
+static int plugin_add(void *opaque, const char *name, const char *value,
+                      Error **errp)
+{
+    struct qemu_plugin_parse_arg *arg = opaque;
+    struct qemu_plugin_desc *p;
+
+    if (strcmp(name, "file") == 0) {
+        if (strcmp(value, "") == 0) {
+            error_setg(errp, "requires a non-empty argument");
+            return 1;
+        }
+        p = plugin_find_desc(arg->head, value);
+        if (p == NULL) {
+            p = g_new0(struct qemu_plugin_desc, 1);
+            p->path = g_strdup(value);
+            QTAILQ_INSERT_TAIL(arg->head, p, entry);
+        }
+        arg->curr = p;
+    } else if (strcmp(name, "arg") == 0) {
+        if (arg->curr == NULL) {
+            error_setg(errp, "missing earlier '-plugin file=' option");
+            return 1;
+        }
+        p = arg->curr;
+        p->argc++;
+        p->argv = g_realloc_n(p->argv, p->argc, sizeof(char *));
+        p->argv[p->argc - 1] = g_strdup(value);
+    } else {
+        error_setg(errp, "-plugin: unexpected parameter '%s'; ignored", name);
+    }
+    return 0;
+}
+
+void qemu_plugin_opt_parse(const char *optarg, struct qemu_plugin_list *head)
+{
+    struct qemu_plugin_parse_arg arg;
+    QemuOpts *opts;
+
+    opts = qemu_opts_parse_noisily(qemu_find_opts("plugin"), optarg, true);
+    if (opts == NULL) {
+        exit(1);
+    }
+    arg.head = head;
+    arg.curr = NULL;
+    qemu_opt_foreach(opts, plugin_add, &arg, &error_fatal);
+    qemu_opts_del(opts);
+}
+
+/*
+ * From: https://en.wikipedia.org/wiki/Xorshift
+ * This is faster than rand_r(), and gives us a wider range (RAND_MAX is only
+ * guaranteed to be >= INT_MAX).
+ */
+static uint64_t xorshift64star(uint64_t x)
+{
+    x ^= x >> 12; /* a */
+    x ^= x << 25; /* b */
+    x ^= x >> 27; /* c */
+    return x * UINT64_C(2685821657736338717);
+}
+
+static int plugin_load(struct qemu_plugin_desc *desc)
+{
+    qemu_plugin_install_func_t install;
+    struct qemu_plugin_ctx *ctx;
+    char *err;
+    int rc;
+
+    ctx = qemu_memalign(qemu_dcache_linesize, sizeof(*ctx));
+    memset(ctx, 0, sizeof(*ctx));
+    ctx->desc = desc;
+
+    ctx->handle = dlopen(desc->path, RTLD_NOW);
+    if (ctx->handle == NULL) {
+        error_report("%s: %s", __func__, dlerror());
+        goto err_dlopen;
+    }
+
+    /* clear any previous dlerror, call dlsym, then check dlerror */
+    dlerror();
+    install = dlsym(ctx->handle, "qemu_plugin_install");
+    err = dlerror();
+    if (err) {
+        error_report("%s: %s", __func__, err);
+        goto err_symbol;
+    }
+    /* symbol was found; it could be NULL though */
+    if (install == NULL) {
+        error_report("%s: %s: qemu_plugin_install is NULL",
+                     __func__, desc->path);
+        goto err_symbol;
+    }
+
+    qemu_rec_mutex_lock(&plugin.lock);
+
+    /* find an unused random id with &ctx as the seed */
+    ctx->id = (uint64_t)(uintptr_t)ctx;
+    for (;;) {
+        void *existing;
+
+        ctx->id = xorshift64star(ctx->id);
+        existing = g_hash_table_lookup(plugin.id_ht, &ctx->id);
+        if (likely(existing == NULL)) {
+            bool success;
+
+            success = g_hash_table_insert(plugin.id_ht, &ctx->id, &ctx->id);
+            g_assert(success);
+            break;
+        }
+    }
+    QTAILQ_INSERT_TAIL(&plugin.ctxs, ctx, entry);
+    qemu_rec_mutex_unlock(&plugin.lock);
+
+    rc = install(ctx->id, desc->argc, desc->argv);
+    if (rc) {
+        error_report("%s: qemu_plugin_install returned error code %d",
+                     __func__, rc);
+        /*
+         * we cannot rely on the plugin doing its own cleanup, so
+         * call a full uninstall if the plugin did not already call it.
+         */
+        qemu_rec_mutex_lock(&plugin.lock);
+        if (!ctx->uninstalling) {
+            qemu_plugin_uninstall(ctx->id, NULL);
+        }
+        qemu_rec_mutex_unlock(&plugin.lock);
+        return 1;
+    }
+    return 0;
+
+ err_symbol:
+    if (dlclose(ctx->handle)) {
+        warn_report("%s: %s", __func__, dlerror());
+    }
+ err_dlopen:
+    qemu_vfree(ctx);
+    return 1;
+}
+
+/* call after having removed @desc from the list */
+static void plugin_desc_free(struct qemu_plugin_desc *desc)
+{
+    int i;
+
+    for (i = 0; i < desc->argc; i++) {
+        g_free(desc->argv[i]);
+    }
+    g_free(desc->argv);
+    g_free(desc->path);
+    g_free(desc);
+}
+
+/**
+ * qemu_plugin_load_list - load a list of plugins
+ * @head: head of the list of descriptors of the plugins to be loaded
+ *
+ * Returns 0 if all plugins in the list are installed, !0 otherwise.
+ *
+ * Note: the descriptor of each successfully installed plugin is removed
+ * from the list given by @head.
+ */
+int qemu_plugin_load_list(struct qemu_plugin_list *head)
+{
+    struct qemu_plugin_desc *desc, *next;
+
+    QTAILQ_FOREACH_SAFE(desc, head, entry, next) {
+        int err;
+
+        err = plugin_load(desc);
+        if (err) {
+            return err;
+        }
+        QTAILQ_REMOVE(head, desc, entry);
+    }
+    return 0;
+}
+
+static struct qemu_plugin_ctx *id_to_ctx__locked(qemu_plugin_id_t id)
+{
+    struct qemu_plugin_ctx *ctx;
+    qemu_plugin_id_t *id_p;
+
+    id_p = g_hash_table_lookup(plugin.id_ht, &id);
+    ctx = container_of(id_p, struct qemu_plugin_ctx, id);
+    if (ctx == NULL) {
+        error_report("plugin: invalid plugin id %" PRIu64, id);
+        abort();
+    }
+    return ctx;
+}
+
+static void plugin_cpu_update__async(CPUState *cpu, run_on_cpu_data data)
+{
+    bitmap_copy(cpu->plugin_mask, &data.host_ulong, QEMU_PLUGIN_EV_MAX);
+    cpu_tb_jmp_cache_clear(cpu);
+}
+
+static void plugin_cpu_update__locked(gpointer k, gpointer v, gpointer udata)
+{
+    CPUState *cpu = container_of(k, CPUState, cpu_index);
+    run_on_cpu_data mask = RUN_ON_CPU_HOST_ULONG(*plugin.mask);
+
+    if (cpu->created) {
+        async_run_on_cpu(cpu, plugin_cpu_update__async, mask);
+    } else {
+        plugin_cpu_update__async(cpu, mask);
+    }
+}
+
+static void plugin_unregister_cb__locked(struct qemu_plugin_ctx *ctx,
+                                         enum qemu_plugin_event ev)
+{
+    struct qemu_plugin_cb *cb = ctx->callbacks[ev];
+
+    if (cb == NULL) {
+        return;
+    }
+    QLIST_REMOVE_RCU(cb, entry);
+    g_free(cb);
+    ctx->callbacks[ev] = NULL;
+    if (QLIST_EMPTY_RCU(&plugin.cb_lists[ev])) {
+        clear_bit(ev, plugin.mask);
+        g_hash_table_foreach(plugin.cpu_ht, plugin_cpu_update__locked, NULL);
+    }
+}
+
+struct qemu_plugin_uninstall_data {
+    struct qemu_plugin_ctx *ctx;
+    unsigned tb_flush_count;
+};
+
+static void plugin_destroy(CPUState *cpu, run_on_cpu_data arg)
+{
+    struct qemu_plugin_uninstall_data *data = arg.host_ptr;
+    struct qemu_plugin_ctx *ctx = data->ctx;
+    bool success;
+
+    tb_flush(cpu);
+
+    qemu_rec_mutex_lock(&plugin.lock);
+    g_assert(ctx->uninstalling);
+    success = g_hash_table_remove(plugin.id_ht, &ctx->id);
+    g_assert(success);
+
+    QTAILQ_REMOVE(&plugin.ctxs, ctx, entry);
+    qemu_rec_mutex_unlock(&plugin.lock);
+
+    if (ctx->uninstall_cb) {
+        ctx->uninstall_cb(ctx->id);
+    }
+    if (dlclose(ctx->handle)) {
+        warn_report("%s: %s", __func__, dlerror());
+    }
+    plugin_desc_free(ctx->desc);
+    qemu_vfree(ctx);
+    g_free(data);
+}
+
+void qemu_plugin_uninstall(qemu_plugin_id_t id, qemu_plugin_uninstall_cb_t cb)
+{
+    struct qemu_plugin_uninstall_data *data;
+    struct qemu_plugin_ctx *ctx;
+    enum qemu_plugin_event ev;
+
+    qemu_rec_mutex_lock(&plugin.lock);
+    ctx = id_to_ctx__locked(id);
+    if (unlikely(ctx->uninstalling)) {
+        qemu_rec_mutex_unlock(&plugin.lock);
+        return;
+    }
+    ctx->uninstalling = true;
+    ctx->uninstall_cb = cb;
+    /*
+     * Unregister all callbacks. This is an RCU list so it is possible that some
+     * callbacks will still be called in this RCU grace period. For this reason
+     * we cannot yet uninstall the plugin.
+     */
+    for (ev = 0; ev < QEMU_PLUGIN_EV_MAX; ev++) {
+        plugin_unregister_cb__locked(ctx, ev);
+    }
+    qemu_rec_mutex_unlock(&plugin.lock);
+
+    /* XXX how to flush when we're not in a vCPU thread? */
+    if (current_cpu) {
+        data = g_new(struct qemu_plugin_uninstall_data, 1);
+        data->ctx = ctx;
+        data->tb_flush_count = atomic_mb_read(&tb_ctx.tb_flush_count);
+        async_safe_run_on_cpu(current_cpu, plugin_destroy,
+                              RUN_ON_CPU_HOST_PTR(data));
+    }
+}
+
+static void plugin_vcpu_cb__simple(CPUState *cpu, enum qemu_plugin_event ev)
+{
+    struct qemu_plugin_cb *cb, *next;
+
+    switch (ev) {
+    case QEMU_PLUGIN_EV_VCPU_INIT:
+    case QEMU_PLUGIN_EV_VCPU_EXIT:
+    case QEMU_PLUGIN_EV_VCPU_IDLE:
+    case QEMU_PLUGIN_EV_VCPU_RESUME:
+        /* iterate safely; plugins might uninstall themselves at any time */
+        QLIST_FOREACH_SAFE_RCU(cb, &plugin.cb_lists[ev], entry, next) {
+            qemu_plugin_vcpu_simple_cb_t func = cb->f.vcpu_simple;
+
+            func(cb->ctx->id, cpu->cpu_index);
+        }
+        break;
+    default:
+        g_assert_not_reached();
+    }
+}
+
+static void plugin_cb__simple(enum qemu_plugin_event ev)
+{
+    struct qemu_plugin_cb *cb, *next;
+
+    switch (ev) {
+    case QEMU_PLUGIN_EV_FLUSH:
+        QLIST_FOREACH_SAFE_RCU(cb, &plugin.cb_lists[ev], entry, next) {
+            qemu_plugin_simple_cb_t func = cb->f.simple;
+
+            func(cb->ctx->id);
+        }
+        break;
+    default:
+        g_assert_not_reached();
+    }
+}
+
+static void plugin_cb__udata(enum qemu_plugin_event ev)
+{
+    struct qemu_plugin_cb *cb, *next;
+
+    switch (ev) {
+    case QEMU_PLUGIN_EV_ATEXIT:
+        QLIST_FOREACH_SAFE_RCU(cb, &plugin.cb_lists[ev], entry, next) {
+            qemu_plugin_udata_cb_t func = cb->f.udata;
+
+            func(cb->ctx->id, cb->udata);
+        }
+        break;
+    default:
+        g_assert_not_reached();
+    }
+}
+
+static void
+do_plugin_register_cb(qemu_plugin_id_t id, enum qemu_plugin_event ev,
+                      void *func, void *udata)
+{
+    struct qemu_plugin_ctx *ctx;
+
+    qemu_rec_mutex_lock(&plugin.lock);
+    ctx = id_to_ctx__locked(id);
+    /* if the plugin is on its way out, ignore this request */
+    if (unlikely(ctx->uninstalling)) {
+        goto out_unlock;
+    }
+    if (func) {
+        struct qemu_plugin_cb *cb = ctx->callbacks[ev];
+
+        if (cb) {
+            cb->f.generic = func;
+            cb->udata = udata;
+        } else {
+            cb = g_new(struct qemu_plugin_cb, 1);
+            cb->ctx = ctx;
+            cb->f.generic = func;
+            cb->udata = udata;
+            ctx->callbacks[ev] = cb;
+            QLIST_INSERT_HEAD_RCU(&plugin.cb_lists[ev], cb, entry);
+            if (!test_bit(ev, plugin.mask)) {
+                set_bit(ev, plugin.mask);
+                g_hash_table_foreach(plugin.cpu_ht, plugin_cpu_update__locked,
+                                     NULL);
+            }
+        }
+    } else {
+        plugin_unregister_cb__locked(ctx, ev);
+    }
+ out_unlock:
+    qemu_rec_mutex_unlock(&plugin.lock);
+}
+
+static void plugin_register_cb(qemu_plugin_id_t id, enum qemu_plugin_event ev,
+                               void *func)
+{
+    do_plugin_register_cb(id, ev, func, NULL);
+}
+
+static void
+plugin_register_cb_udata(qemu_plugin_id_t id, enum qemu_plugin_event ev,
+                         void *func, void *udata)
+{
+    do_plugin_register_cb(id, ev, func, udata);
+}
+
+void qemu_plugin_register_vcpu_init_cb(qemu_plugin_id_t id,
+                                       qemu_plugin_vcpu_simple_cb_t cb)
+{
+    plugin_register_cb(id, QEMU_PLUGIN_EV_VCPU_INIT, cb);
+}
+
+void qemu_plugin_register_vcpu_exit_cb(qemu_plugin_id_t id,
+                                       qemu_plugin_vcpu_simple_cb_t cb)
+{
+    plugin_register_cb(id, QEMU_PLUGIN_EV_VCPU_EXIT, cb);
+}
+
+void qemu_plugin_vcpu_init_hook(CPUState *cpu)
+{
+    bool success;
+
+    qemu_rec_mutex_lock(&plugin.lock);
+    plugin_cpu_update__locked(&cpu->cpu_index, NULL, NULL);
+    success = g_hash_table_insert(plugin.cpu_ht, &cpu->cpu_index,
+                                  &cpu->cpu_index);
+    g_assert(success);
+    qemu_rec_mutex_unlock(&plugin.lock);
+
+    plugin_vcpu_cb__simple(cpu, QEMU_PLUGIN_EV_VCPU_INIT);
+}
+
+void qemu_plugin_vcpu_exit_hook(CPUState *cpu)
+{
+    bool success;
+
+    plugin_vcpu_cb__simple(cpu, QEMU_PLUGIN_EV_VCPU_EXIT);
+
+    qemu_rec_mutex_lock(&plugin.lock);
+    success = g_hash_table_remove(plugin.cpu_ht, &cpu->cpu_index);
+    g_assert(success);
+    qemu_rec_mutex_unlock(&plugin.lock);
+}
+
+struct plugin_for_each_args {
+    struct qemu_plugin_ctx *ctx;
+    qemu_plugin_vcpu_simple_cb_t cb;
+};
+
+static void plugin_vcpu_for_each(gpointer k, gpointer v, gpointer udata)
+{
+    struct plugin_for_each_args *args = udata;
+    int cpu_index = *(int *)k;
+
+    args->cb(args->ctx->id, cpu_index);
+}
+
+void qemu_plugin_vcpu_for_each(qemu_plugin_id_t id,
+                               qemu_plugin_vcpu_simple_cb_t cb)
+{
+    struct plugin_for_each_args args;
+
+    if (cb == NULL) {
+        return;
+    }
+    qemu_rec_mutex_lock(&plugin.lock);
+    args.ctx = id_to_ctx__locked(id);
+    args.cb = cb;
+    g_hash_table_foreach(plugin.cpu_ht, plugin_vcpu_for_each, &args);
+    qemu_rec_mutex_unlock(&plugin.lock);
+}
+
+static struct qemu_plugin_dyn_cb *
+plugin_get_dyn_cb(struct qemu_plugin_dyn_cb_arr *arr)
+{
+    if (arr->n == arr->capacity) {
+        arr->data = g_renew(struct qemu_plugin_dyn_cb, arr->data, arr->n + 1);
+        arr->capacity++;
+    }
+
+    return &arr->data[arr->n++];
+}
+
+static void plugin_register_inline_op(struct qemu_plugin_dyn_cb_arr *arr,
+                                      enum qemu_plugin_mem_rw rw,
+                                      enum qemu_plugin_op op, void *ptr,
+                                      uint64_t imm)
+{
+    struct qemu_plugin_dyn_cb *dyn_cb;
+
+    dyn_cb = plugin_get_dyn_cb(arr);
+    dyn_cb->userp = ptr;
+    dyn_cb->type = PLUGIN_CB_INLINE;
+    dyn_cb->rw = rw;
+    dyn_cb->inline_insn.op = op;
+    dyn_cb->inline_insn.imm = imm;
+}
+
+static inline uint32_t cb_to_tcg_flags(enum qemu_plugin_cb_flags flags)
+{
+    uint32_t ret;
+
+    switch (flags) {
+    case QEMU_PLUGIN_CB_RW_REGS:
+        ret = 0;
+    case QEMU_PLUGIN_CB_R_REGS:
+        ret = TCG_CALL_NO_WG;
+        break;
+    case QEMU_PLUGIN_CB_NO_REGS:
+    default:
+        ret = TCG_CALL_NO_RWG;
+    }
+    return ret;
+}
+
+static inline void
+plugin_register_dyn_cb__udata(struct qemu_plugin_dyn_cb_arr *arr,
+                              qemu_plugin_vcpu_udata_cb_t cb,
+                              enum qemu_plugin_cb_flags flags, void *udata)
+{
+    struct qemu_plugin_dyn_cb *dyn_cb = plugin_get_dyn_cb(arr);
+
+    dyn_cb->userp = udata;
+    dyn_cb->tcg_flags = cb_to_tcg_flags(flags);
+    dyn_cb->f.vcpu_udata = cb;
+    dyn_cb->type = PLUGIN_CB_REGULAR;
+}
+
+void qemu_plugin_register_vcpu_tb_exec_cb(struct qemu_plugin_tb *tb,
+                                          qemu_plugin_vcpu_udata_cb_t cb,
+                                          enum qemu_plugin_cb_flags flags,
+                                          void *udata)
+{
+    plugin_register_dyn_cb__udata(&tb->cbs[PLUGIN_CB_REGULAR],
+                                  cb, flags, udata);
+}
+
+void qemu_plugin_register_vcpu_tb_exec_inline(struct qemu_plugin_tb *tb,
+                                              enum qemu_plugin_op op,
+                                              void *ptr, uint64_t imm)
+{
+    plugin_register_inline_op(&tb->cbs[PLUGIN_CB_INLINE], 0, op, ptr, imm);
+}
+
+void qemu_plugin_register_vcpu_insn_exec_cb(struct qemu_plugin_insn *insn,
+                                            qemu_plugin_vcpu_udata_cb_t cb,
+                                            enum qemu_plugin_cb_flags flags,
+                                            void *udata)
+{
+    plugin_register_dyn_cb__udata(&insn->cbs[PLUGIN_CB_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)
+{
+    plugin_register_inline_op(&insn->cbs[PLUGIN_CB_INSN][PLUGIN_CB_INLINE],
+                              0, op, ptr, imm);
+}
+
+static void plugin_register_vcpu_mem_cb(struct qemu_plugin_dyn_cb_arr *arr,
+                                        void *cb,
+                                        enum qemu_plugin_cb_flags flags,
+                                        enum qemu_plugin_mem_rw rw,
+                                        void *udata, bool haddr)
+{
+    struct qemu_plugin_dyn_cb *dyn_cb;
+
+    dyn_cb = plugin_get_dyn_cb(arr);
+    dyn_cb->userp = udata;
+    dyn_cb->tcg_flags = cb_to_tcg_flags(flags);
+    dyn_cb->type = PLUGIN_CB_REGULAR;
+    dyn_cb->rw = rw;
+    dyn_cb->mem.haddr = haddr;
+    dyn_cb->f.generic = cb;
+}
+
+void qemu_plugin_register_vcpu_mem_cb(struct qemu_plugin_insn *insn,
+                                      qemu_plugin_vcpu_mem_cb_t cb,
+                                      enum qemu_plugin_cb_flags flags,
+                                      enum qemu_plugin_mem_rw rw,
+                                      void *udata)
+{
+    plugin_register_vcpu_mem_cb(&insn->cbs[PLUGIN_CB_MEM][PLUGIN_CB_REGULAR],
+        cb, flags, rw, udata, false);
+}
+
+void qemu_plugin_register_vcpu_mem_haddr_cb(struct qemu_plugin_insn *insn,
+                                            qemu_plugin_vcpu_mem_haddr_cb_t cb,
+                                            enum qemu_plugin_cb_flags flags,
+                                            enum qemu_plugin_mem_rw rw,
+                                            void *udata)
+{
+    plugin_register_vcpu_mem_cb(&insn->cbs[PLUGIN_CB_HADDR][PLUGIN_CB_REGULAR],
+        cb, flags, rw, udata, true);
+}
+
+void qemu_plugin_register_vcpu_mem_inline(struct qemu_plugin_insn *insn,
+                                          enum qemu_plugin_mem_rw rw,
+                                          enum qemu_plugin_op op, void *ptr,
+                                          uint64_t imm)
+{
+    plugin_register_inline_op(&insn->cbs[PLUGIN_CB_MEM][PLUGIN_CB_INLINE],
+        rw, op, ptr, imm);
+}
+
+void qemu_plugin_tb_trans_cb(CPUState *cpu, struct qemu_plugin_tb *tb)
+{
+    struct qemu_plugin_cb *cb, *next;
+    enum qemu_plugin_event ev = QEMU_PLUGIN_EV_VCPU_TB_TRANS;
+
+    /* no plugin_mask check here; caller should have checked */
+
+    QLIST_FOREACH_SAFE_RCU(cb, &plugin.cb_lists[ev], entry, next) {
+        qemu_plugin_vcpu_tb_trans_cb_t func = cb->f.vcpu_tb_trans;
+
+        func(cb->ctx->id, cpu->cpu_index, tb);
+    }
+}
+
+void qemu_plugin_register_vcpu_tb_trans_cb(qemu_plugin_id_t id,
+                                           qemu_plugin_vcpu_tb_trans_cb_t cb)
+{
+    plugin_register_cb(id, QEMU_PLUGIN_EV_VCPU_TB_TRANS, cb);
+}
+
+void
+qemu_plugin_vcpu_syscall(CPUState *cpu, int64_t num, uint64_t a1, uint64_t a2,
+                         uint64_t a3, uint64_t a4, uint64_t a5,
+                         uint64_t a6, uint64_t a7, uint64_t a8)
+{
+    struct qemu_plugin_cb *cb, *next;
+    enum qemu_plugin_event ev = QEMU_PLUGIN_EV_VCPU_SYSCALL;
+
+    if (!test_bit(ev, cpu->plugin_mask)) {
+        return;
+    }
+
+    QLIST_FOREACH_SAFE_RCU(cb, &plugin.cb_lists[ev], entry, next) {
+        qemu_plugin_vcpu_syscall_cb_t func = cb->f.vcpu_syscall;
+
+        func(cb->ctx->id, cpu->cpu_index, num, a1, a2, a3, a4, a5, a6, a7, a8);
+    }
+}
+
+void qemu_plugin_register_vcpu_syscall_cb(qemu_plugin_id_t id,
+                                          qemu_plugin_vcpu_syscall_cb_t cb)
+{
+    plugin_register_cb(id, QEMU_PLUGIN_EV_VCPU_SYSCALL, cb);
+}
+
+void qemu_plugin_vcpu_syscall_ret(CPUState *cpu, int64_t num, int64_t ret)
+{
+    struct qemu_plugin_cb *cb, *next;
+    enum qemu_plugin_event ev = QEMU_PLUGIN_EV_VCPU_SYSCALL_RET;
+
+    if (!test_bit(ev, cpu->plugin_mask)) {
+        return;
+    }
+
+    QLIST_FOREACH_SAFE_RCU(cb, &plugin.cb_lists[ev], entry, next) {
+        qemu_plugin_vcpu_syscall_ret_cb_t func = cb->f.vcpu_syscall_ret;
+
+        func(cb->ctx->id, cpu->cpu_index, num, ret);
+    }
+}
+
+void
+qemu_plugin_register_vcpu_syscall_ret_cb(qemu_plugin_id_t id,
+                                         qemu_plugin_vcpu_syscall_ret_cb_t cb)
+{
+    plugin_register_cb(id, QEMU_PLUGIN_EV_VCPU_SYSCALL_RET, cb);
+}
+
+size_t qemu_plugin_tb_n_insns(const struct qemu_plugin_tb *tb)
+{
+    return tb->n;
+}
+
+uint64_t qemu_plugin_tb_vaddr(const struct qemu_plugin_tb *tb)
+{
+    return tb->vaddr;
+}
+
+struct qemu_plugin_insn *
+qemu_plugin_tb_get_insn(const struct qemu_plugin_tb *tb, size_t idx)
+{
+    if (unlikely(idx >= tb->n)) {
+        return NULL;
+    }
+    return &tb->insns[idx];
+}
+
+const void *qemu_plugin_insn_data(const struct qemu_plugin_insn *insn)
+{
+    return insn->data;
+}
+
+size_t qemu_plugin_insn_size(const struct qemu_plugin_insn *insn)
+{
+    return insn->size;
+}
+
+uint64_t qemu_plugin_insn_vaddr(const struct qemu_plugin_insn *insn)
+{
+    return insn->vaddr;
+}
+
+void *qemu_plugin_insn_haddr(const struct qemu_plugin_insn *insn)
+{
+    return insn->haddr;
+}
+
+void qemu_plugin_vcpu_idle_cb(CPUState *cpu)
+{
+    plugin_vcpu_cb__simple(cpu, QEMU_PLUGIN_EV_VCPU_IDLE);
+}
+
+void qemu_plugin_vcpu_resume_cb(CPUState *cpu)
+{
+    plugin_vcpu_cb__simple(cpu, QEMU_PLUGIN_EV_VCPU_RESUME);
+}
+
+void qemu_plugin_register_vcpu_idle_cb(qemu_plugin_id_t id,
+                                       qemu_plugin_vcpu_simple_cb_t cb)
+{
+    plugin_register_cb(id, QEMU_PLUGIN_EV_VCPU_IDLE, cb);
+}
+
+void qemu_plugin_register_vcpu_resume_cb(qemu_plugin_id_t id,
+                                         qemu_plugin_vcpu_simple_cb_t cb)
+{
+    plugin_register_cb(id, QEMU_PLUGIN_EV_VCPU_RESUME, cb);
+}
+
+void qemu_plugin_register_flush_cb(qemu_plugin_id_t id,
+                                   qemu_plugin_simple_cb_t cb)
+{
+    plugin_register_cb(id, QEMU_PLUGIN_EV_FLUSH, cb);
+}
+
+void qemu_plugin_flush_cb(void)
+{
+    qht_iter_remove(&plugin.dyn_cb_arr_ht, free_dyn_cb_arr, NULL);
+    qht_reset(&plugin.dyn_cb_arr_ht);
+
+    plugin_cb__simple(QEMU_PLUGIN_EV_FLUSH);
+}
+
+static void exec_inline_op(struct qemu_plugin_dyn_cb *cb)
+{
+    uint64_t *val = cb->userp;
+
+    switch (cb->inline_insn.op) {
+    case QEMU_PLUGIN_INLINE_ADD_U64:
+        *val += cb->inline_insn.imm;
+        break;
+    default:
+        g_assert_not_reached();
+    }
+}
+
+void qemu_plugin_vcpu_mem_cb(CPUState *cpu, uint64_t vaddr, void *haddr,
+                             uint32_t info)
+{
+    struct qemu_plugin_dyn_cb_arr *arr = cpu->plugin_mem_cbs;
+    size_t i;
+
+    if (arr == NULL) {
+        return;
+    }
+    for (i = 0; i < arr->n; i++) {
+        struct qemu_plugin_dyn_cb *cb = &arr->data[i];
+        int w = !!(info & TRACE_MEM_ST) + 1;
+
+        if (!(w & cb->rw)) {
+                break;
+        }
+        switch (cb->type) {
+        case PLUGIN_CB_REGULAR:
+
+            if (cb->mem.haddr) {
+                cb->f.vcpu_mem_haddr(cpu->cpu_index, info, vaddr, haddr,
+                                     cb->userp);
+            } else {
+                cb->f.vcpu_mem(cpu->cpu_index, info, vaddr, cb->userp);
+            }
+            break;
+        case PLUGIN_CB_INLINE:
+            exec_inline_op(cb);
+            break;
+        default:
+            g_assert_not_reached();
+        }
+    }
+}
+
+unsigned qemu_plugin_mem_size_shift(qemu_plugin_meminfo_t info)
+{
+    return info & TRACE_MEM_SZ_SHIFT_MASK;
+}
+
+bool qemu_plugin_mem_is_sign_extended(qemu_plugin_meminfo_t info)
+{
+    return !!(info & TRACE_MEM_SE);
+}
+
+bool qemu_plugin_mem_is_big_endian(qemu_plugin_meminfo_t info)
+{
+    return !!(info & TRACE_MEM_BE);
+}
+
+bool qemu_plugin_mem_is_store(qemu_plugin_meminfo_t info)
+{
+    return !!(info & TRACE_MEM_ST);
+}
+
+int qemu_plugin_n_vcpus(void)
+{
+#ifdef CONFIG_USER_ONLY
+    return -1;
+#else
+    return smp_cpus;
+#endif
+}
+
+int qemu_plugin_n_max_vcpus(void)
+{
+#ifdef CONFIG_USER_ONLY
+    return -1;
+#else
+    return max_cpus;
+#endif
+}
+
+void qemu_plugin_atexit_cb(void)
+{
+    plugin_cb__udata(QEMU_PLUGIN_EV_ATEXIT);
+}
+
+void qemu_plugin_register_atexit_cb(qemu_plugin_id_t id,
+                                    qemu_plugin_udata_cb_t cb,
+                                    void *udata)
+{
+    plugin_register_cb_udata(id, QEMU_PLUGIN_EV_ATEXIT, cb, udata);
+}
+
+uint64_t qemu_plugin_ram_addr_from_host(void *haddr)
+{
+#ifdef CONFIG_SOFTMMU
+    ram_addr_t ram_addr;
+
+    g_assert(haddr);
+    ram_addr = qemu_ram_addr_from_host(haddr);
+    if (ram_addr == RAM_ADDR_INVALID) {
+        error_report("Bad ram pointer %p", haddr);
+        abort();
+    }
+    return ram_addr;
+#else
+    return 0;
+#endif
+}
+
+/*
+ * Call this function after longjmp'ing to the main loop. It's possible that the
+ * last instruction of a TB might have used helpers, and therefore the
+ * "disable" instruction will never execute because it ended up as dead code.
+ */
+void qemu_plugin_disable_mem_helpers(CPUState *cpu)
+{
+    cpu->plugin_mem_cbs = NULL;
+}
+
+static void __attribute__((__constructor__)) plugin_init(void)
+{
+    int i;
+
+    for (i = 0; i < QEMU_PLUGIN_EV_MAX; i++) {
+        QLIST_INIT(&plugin.cb_lists[i]);
+    }
+    qemu_rec_mutex_init(&plugin.lock);
+    plugin.id_ht = g_hash_table_new(g_int64_hash, g_int64_equal);
+    plugin.cpu_ht = g_hash_table_new(g_int_hash, g_int_equal);
+    QTAILQ_INIT(&plugin.ctxs);
+    qht_init(&plugin.dyn_cb_arr_ht, plugin_dyn_cb_arr_cmp, 16,
+             QHT_MODE_AUTO_RESIZE);
+    atexit(qemu_plugin_atexit_cb);
+}
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [Qemu-devel] [RFC v2 07/38] queue: add QTAILQ_REMOVE_SEVERAL
  2018-12-09 19:37 [Qemu-devel] [RFC v2 00/38] Plugin support Emilio G. Cota
                   ` (5 preceding siblings ...)
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 06/38] plugin: add core code Emilio G. Cota
@ 2018-12-09 19:37 ` Emilio G. Cota
  2019-02-25 16:22   ` Alex Bennée
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 08/38] tcg: drop nargs from tcg_op_insert_{before, after} Emilio G. Cota
                   ` (31 subsequent siblings)
  38 siblings, 1 reply; 63+ messages in thread
From: Emilio G. Cota @ 2018-12-09 19:37 UTC (permalink / raw)
  To: qemu-devel; +Cc: Alex Bennée, Richard Henderson, Pavel Dovgalyuk

This is faster than removing elements one by one.

Will gain a user soon.

Signed-off-by: Emilio G. Cota <cota@braap.org>
---
 include/qemu/queue.h | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/include/qemu/queue.h b/include/qemu/queue.h
index ac418efc43..0283c2dd7d 100644
--- a/include/qemu/queue.h
+++ b/include/qemu/queue.h
@@ -419,6 +419,16 @@ struct {                                                                \
         (elm)->field.tqe_prev = NULL;                                   \
 } while (/*CONSTCOND*/0)
 
+/* remove @left, @right and all elements in between from @head */
+#define QTAILQ_REMOVE_SEVERAL(head, left, right, field) do {    \
+        if (((right)->field.tqe_next) != NULL)                  \
+            (right)->field.tqe_next->field.tqe_prev =           \
+                (left)->field.tqe_prev;                         \
+        else                                                    \
+            (head)->tqh_last = (left)->field.tqe_prev;          \
+        *(left)->field.tqe_prev = (right)->field.tqe_next;      \
+    } while (/*CONSTCOND*/0)
+
 #define QTAILQ_FOREACH(var, head, field)                                \
         for ((var) = ((head)->tqh_first);                               \
                 (var);                                                  \
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [Qemu-devel] [RFC v2 08/38] tcg: drop nargs from tcg_op_insert_{before, after}
  2018-12-09 19:37 [Qemu-devel] [RFC v2 00/38] Plugin support Emilio G. Cota
                   ` (6 preceding siblings ...)
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 07/38] queue: add QTAILQ_REMOVE_SEVERAL Emilio G. Cota
@ 2018-12-09 19:37 ` Emilio G. Cota
  2018-12-13 23:52   ` Richard Henderson
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 09/38] cputlb: introduce get_page_addr_code_hostp Emilio G. Cota
                   ` (30 subsequent siblings)
  38 siblings, 1 reply; 63+ messages in thread
From: Emilio G. Cota @ 2018-12-09 19:37 UTC (permalink / raw)
  To: qemu-devel; +Cc: Alex Bennée, Richard Henderson, Pavel Dovgalyuk

It's unused.

Signed-off-by: Emilio G. Cota <cota@braap.org>
---
 tcg/tcg.h      |  4 ++--
 tcg/optimize.c |  4 ++--
 tcg/tcg.c      | 10 ++++------
 3 files changed, 8 insertions(+), 10 deletions(-)

diff --git a/tcg/tcg.h b/tcg/tcg.h
index f4efbaa680..a745e926bb 100644
--- a/tcg/tcg.h
+++ b/tcg/tcg.h
@@ -1073,8 +1073,8 @@ void tcg_gen_callN(void *func, TCGTemp *ret, int nargs, TCGTemp **args);
 
 TCGOp *tcg_emit_op(TCGOpcode opc);
 void tcg_op_remove(TCGContext *s, TCGOp *op);
-TCGOp *tcg_op_insert_before(TCGContext *s, TCGOp *op, TCGOpcode opc, int narg);
-TCGOp *tcg_op_insert_after(TCGContext *s, TCGOp *op, TCGOpcode opc, int narg);
+TCGOp *tcg_op_insert_before(TCGContext *s, TCGOp *op, TCGOpcode opc);
+TCGOp *tcg_op_insert_after(TCGContext *s, TCGOp *op, TCGOpcode opc);
 
 void tcg_optimize(TCGContext *s);
 
diff --git a/tcg/optimize.c b/tcg/optimize.c
index 5dbe11c3c8..a2247f6de3 100644
--- a/tcg/optimize.c
+++ b/tcg/optimize.c
@@ -1249,7 +1249,7 @@ void tcg_optimize(TCGContext *s)
                 uint64_t a = ((uint64_t)ah << 32) | al;
                 uint64_t b = ((uint64_t)bh << 32) | bl;
                 TCGArg rl, rh;
-                TCGOp *op2 = tcg_op_insert_before(s, op, INDEX_op_movi_i32, 2);
+                TCGOp *op2 = tcg_op_insert_before(s, op, INDEX_op_movi_i32);
 
                 if (opc == INDEX_op_add2_i32) {
                     a += b;
@@ -1271,7 +1271,7 @@ void tcg_optimize(TCGContext *s)
                 uint32_t b = arg_info(op->args[3])->val;
                 uint64_t r = (uint64_t)a * b;
                 TCGArg rl, rh;
-                TCGOp *op2 = tcg_op_insert_before(s, op, INDEX_op_movi_i32, 2);
+                TCGOp *op2 = tcg_op_insert_before(s, op, INDEX_op_movi_i32);
 
                 rl = op->args[0];
                 rh = op->args[1];
diff --git a/tcg/tcg.c b/tcg/tcg.c
index e85133ef05..e87c662a18 100644
--- a/tcg/tcg.c
+++ b/tcg/tcg.c
@@ -2203,16 +2203,14 @@ TCGOp *tcg_emit_op(TCGOpcode opc)
     return op;
 }
 
-TCGOp *tcg_op_insert_before(TCGContext *s, TCGOp *old_op,
-                            TCGOpcode opc, int nargs)
+TCGOp *tcg_op_insert_before(TCGContext *s, TCGOp *old_op, TCGOpcode opc)
 {
     TCGOp *new_op = tcg_op_alloc(opc);
     QTAILQ_INSERT_BEFORE(old_op, new_op, link);
     return new_op;
 }
 
-TCGOp *tcg_op_insert_after(TCGContext *s, TCGOp *old_op,
-                           TCGOpcode opc, int nargs)
+TCGOp *tcg_op_insert_after(TCGContext *s, TCGOp *old_op, TCGOpcode opc)
 {
     TCGOp *new_op = tcg_op_alloc(opc);
     QTAILQ_INSERT_AFTER(&s->ops, old_op, new_op, link);
@@ -2550,7 +2548,7 @@ static bool liveness_pass_2(TCGContext *s)
                     TCGOpcode lopc = (arg_ts->type == TCG_TYPE_I32
                                       ? INDEX_op_ld_i32
                                       : INDEX_op_ld_i64);
-                    TCGOp *lop = tcg_op_insert_before(s, op, lopc, 3);
+                    TCGOp *lop = tcg_op_insert_before(s, op, lopc);
 
                     lop->args[0] = temp_arg(dir_ts);
                     lop->args[1] = temp_arg(arg_ts->mem_base);
@@ -2619,7 +2617,7 @@ static bool liveness_pass_2(TCGContext *s)
                 TCGOpcode sopc = (arg_ts->type == TCG_TYPE_I32
                                   ? INDEX_op_st_i32
                                   : INDEX_op_st_i64);
-                TCGOp *sop = tcg_op_insert_after(s, op, sopc, 3);
+                TCGOp *sop = tcg_op_insert_after(s, op, sopc);
 
                 sop->args[0] = temp_arg(dir_ts);
                 sop->args[1] = temp_arg(arg_ts->mem_base);
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [Qemu-devel] [RFC v2 09/38] cputlb: introduce get_page_addr_code_hostp
  2018-12-09 19:37 [Qemu-devel] [RFC v2 00/38] Plugin support Emilio G. Cota
                   ` (7 preceding siblings ...)
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 08/38] tcg: drop nargs from tcg_op_insert_{before, after} Emilio G. Cota
@ 2018-12-09 19:37 ` Emilio G. Cota
  2019-01-24 14:51   ` Alex Bennée
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 10/38] plugin-gen: add module for TCG-related code Emilio G. Cota
                   ` (29 subsequent siblings)
  38 siblings, 1 reply; 63+ messages in thread
From: Emilio G. Cota @ 2018-12-09 19:37 UTC (permalink / raw)
  To: qemu-devel; +Cc: Alex Bennée, Richard Henderson, Pavel Dovgalyuk

This will be used by plugins to get the host address
of instructions.

Signed-off-by: Emilio G. Cota <cota@braap.org>
---
 include/exec/exec-all.h | 13 +++++++++++++
 accel/tcg/cputlb.c      | 14 +++++++++++++-
 2 files changed, 26 insertions(+), 1 deletion(-)

diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h
index 815e5b1e83..afcc01e0e3 100644
--- a/include/exec/exec-all.h
+++ b/include/exec/exec-all.h
@@ -22,6 +22,7 @@
 
 #include "qemu-common.h"
 #include "exec/tb-context.h"
+#include "exec/cpu_ldst.h"
 #include "sysemu/cpus.h"
 
 /* allow to see translation results - the slowdown should be negligible, so we leave it */
@@ -487,12 +488,24 @@ static inline tb_page_addr_t get_page_addr_code(CPUArchState *env1, target_ulong
 {
     return addr;
 }
+
+static inline tb_page_addr_t get_page_addr_code_hostp(CPUArchState *env1,
+                                                      target_ulong addr,
+                                                      void **hostp)
+{
+    if (hostp) {
+        *hostp = g2h(addr);
+    }
+    return addr;
+}
 #else
 static inline void mmap_lock(void) {}
 static inline void mmap_unlock(void) {}
 
 /* cputlb.c */
 tb_page_addr_t get_page_addr_code(CPUArchState *env1, target_ulong addr);
+tb_page_addr_t get_page_addr_code_hostp(CPUArchState *env1, target_ulong addr,
+                                        void **hostp);
 
 void tlb_reset_dirty(CPUState *cpu, ram_addr_t start1, ram_addr_t length);
 void tlb_set_dirty(CPUState *cpu, target_ulong vaddr);
diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c
index e3582f2f1d..5c61908084 100644
--- a/accel/tcg/cputlb.c
+++ b/accel/tcg/cputlb.c
@@ -1069,7 +1069,8 @@ static bool victim_tlb_hit(CPUArchState *env, size_t mmu_idx, size_t index,
  * is actually a ram_addr_t (in system mode; the user mode emulation
  * version of this function returns a guest virtual address).
  */
-tb_page_addr_t get_page_addr_code(CPUArchState *env, target_ulong addr)
+tb_page_addr_t get_page_addr_code_hostp(CPUArchState *env, target_ulong addr,
+                                        void **hostp)
 {
     uintptr_t mmu_idx = cpu_mmu_index(env, true);
     uintptr_t index = tlb_index(env, mmu_idx, addr);
@@ -1092,13 +1093,24 @@ tb_page_addr_t get_page_addr_code(CPUArchState *env, target_ulong addr)
          *    than a target page, so we must redo the MMU check every insn
          *  - TLB_MMIO: region is not backed by RAM
          */
+        if (hostp) {
+            *hostp = NULL;
+        }
         return -1;
     }
 
     p = (void *)((uintptr_t)addr + entry->addend);
+    if (hostp) {
+        *hostp = p;
+    }
     return qemu_ram_addr_from_host_nofail(p);
 }
 
+tb_page_addr_t get_page_addr_code(CPUArchState *env, target_ulong addr)
+{
+    return get_page_addr_code_hostp(env, addr, NULL);
+}
+
 /* Probe for whether the specified guest write access is permitted.
  * If it is not permitted then an exception will be taken in the same
  * way as if this were a real write access (and we will not return).
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [Qemu-devel] [RFC v2 10/38] plugin-gen: add module for TCG-related code
  2018-12-09 19:37 [Qemu-devel] [RFC v2 00/38] Plugin support Emilio G. Cota
                   ` (8 preceding siblings ...)
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 09/38] cputlb: introduce get_page_addr_code_hostp Emilio G. Cota
@ 2018-12-09 19:37 ` Emilio G. Cota
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 11/38] tcg: add tcg_gen_st_ptr Emilio G. Cota
                   ` (28 subsequent siblings)
  38 siblings, 0 replies; 63+ messages in thread
From: Emilio G. Cota @ 2018-12-09 19:37 UTC (permalink / raw)
  To: qemu-devel; +Cc: Alex Bennée, Richard Henderson, Pavel Dovgalyuk

We first inject empty instrumentation from translator_loop.
After translation, we go through the plugins to see what
they want to register for, filling in the empty instrumentation.
If if turns out that some instrumentation remains unused, we
remove it.

This approach supports the following features:

- Inlining TCG code for simple operations. Note that we do not
  export TCG ops to plugins. Instead, we give them a C API to
  insert inlined ops. So far we only support adding an immediate
  to a u64, e.g. to count events.

- "Direct" callbacks. These are callbacks that do not go via
  a helper. Instead, the helper is defined at run-time, so that
  the plugin code is directly called from TCG. This makes direct
  callbacks as efficient as possible; they are therefore used
  for very frequent events, e.g. memory callbacks.

- Passing the host address to memory callbacks. Most of this
  is implemented in a later patch though.

- Instrumentation of memory accesses performed from helpers.
  See the corresponding comment, as well as a later patch.

Signed-off-by: Emilio G. Cota <cota@braap.org>
---
 accel/tcg/plugin-helpers.h  |    6 +
 include/exec/helper-gen.h   |    1 +
 include/exec/helper-proto.h |    1 +
 include/exec/helper-tcg.h   |    1 +
 include/exec/plugin-gen.h   |   66 +++
 tcg/tcg-op.h                |   11 +
 tcg/tcg-opc.h               |    3 +
 tcg/tcg.h                   |   20 +
 accel/tcg/plugin-gen.c      | 1077 +++++++++++++++++++++++++++++++++++
 tcg/tcg.c                   |   17 +
 accel/tcg/Makefile.objs     |    1 +
 11 files changed, 1204 insertions(+)
 create mode 100644 accel/tcg/plugin-helpers.h
 create mode 100644 include/exec/plugin-gen.h
 create mode 100644 accel/tcg/plugin-gen.c

diff --git a/accel/tcg/plugin-helpers.h b/accel/tcg/plugin-helpers.h
new file mode 100644
index 0000000000..5457a3a577
--- /dev/null
+++ b/accel/tcg/plugin-helpers.h
@@ -0,0 +1,6 @@
+#ifdef CONFIG_PLUGIN
+/* Note: no TCG flags because those are overwritten later */
+DEF_HELPER_2(plugin_vcpu_udata_cb, void, i32, ptr)
+DEF_HELPER_4(plugin_vcpu_mem_cb, void, i32, i32, i64, ptr)
+DEF_HELPER_5(plugin_vcpu_mem_haddr_cb, void, i32, i32, i64, ptr, ptr)
+#endif
diff --git a/include/exec/helper-gen.h b/include/exec/helper-gen.h
index 22381a1708..236ff40524 100644
--- a/include/exec/helper-gen.h
+++ b/include/exec/helper-gen.h
@@ -70,6 +70,7 @@ static inline void glue(gen_helper_, name)(dh_retvar_decl(ret)          \
 #include "trace/generated-helpers.h"
 #include "trace/generated-helpers-wrappers.h"
 #include "tcg-runtime.h"
+#include "plugin-helpers.h"
 
 #undef DEF_HELPER_FLAGS_0
 #undef DEF_HELPER_FLAGS_1
diff --git a/include/exec/helper-proto.h b/include/exec/helper-proto.h
index 74943edb13..1c4ba9bc78 100644
--- a/include/exec/helper-proto.h
+++ b/include/exec/helper-proto.h
@@ -33,6 +33,7 @@ dh_ctype(ret) HELPER(name) (dh_ctype(t1), dh_ctype(t2), dh_ctype(t3), \
 #include "helper.h"
 #include "trace/generated-helpers.h"
 #include "tcg-runtime.h"
+#include "plugin-helpers.h"
 
 #undef DEF_HELPER_FLAGS_0
 #undef DEF_HELPER_FLAGS_1
diff --git a/include/exec/helper-tcg.h b/include/exec/helper-tcg.h
index b3bdb0c399..3977b4c606 100644
--- a/include/exec/helper-tcg.h
+++ b/include/exec/helper-tcg.h
@@ -48,6 +48,7 @@
 #include "helper.h"
 #include "trace/generated-helpers.h"
 #include "tcg-runtime.h"
+#include "plugin-helpers.h"
 
 #undef str
 #undef DEF_HELPER_FLAGS_0
diff --git a/include/exec/plugin-gen.h b/include/exec/plugin-gen.h
new file mode 100644
index 0000000000..449ea16034
--- /dev/null
+++ b/include/exec/plugin-gen.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2017, Emilio G. Cota <cota@braap.org>
+ *
+ * License: GNU GPL, version 2 or later.
+ *   See the COPYING file in the top-level directory.
+ *
+ * plugin-gen.h - TCG-dependent definitions for generating plugin code
+ *
+ * This header should be included only from plugin.c and C files that emit
+ * TCG code.
+ */
+#ifndef QEMU_PLUGIN_GEN_H
+#define QEMU_PLUGIN_GEN_H
+
+#include "qemu/plugin.h"
+#include "tcg/tcg.h"
+
+/* used by plugin_callback_start and plugin_callback_end TCG ops */
+enum plugin_gen_from {
+    PLUGIN_GEN_FROM_TB,
+    PLUGIN_GEN_FROM_INSN,
+    PLUGIN_GEN_FROM_MEM,
+    PLUGIN_GEN_AFTER_INSN,
+    PLUGIN_GEN_N_FROMS,
+};
+
+struct DisasContextBase;
+
+#ifdef CONFIG_PLUGIN
+
+bool plugin_gen_tb_start(CPUState *cpu, const TranslationBlock *tb);
+void plugin_gen_tb_end(CPUState *cpu);
+void plugin_gen_insn_start(CPUState *cpu, const struct DisasContextBase *db);
+void plugin_gen_insn_end(void);
+
+void plugin_gen_disable_mem_helpers(void);
+void plugin_gen_empty_mem_callback(TCGv addr, uint8_t info);
+
+#else /* !CONFIG_PLUGIN */
+
+static inline
+bool plugin_gen_tb_start(CPUState *cpu, const TranslationBlock *tb)
+{
+    return false;
+}
+
+static inline
+void plugin_gen_insn_start(CPUState *cpu, const struct DisasContextBase *db)
+{ }
+
+static inline void plugin_gen_insn_end(void)
+{ }
+
+static inline void plugin_gen_tb_end(CPUState *cpu)
+{ }
+
+static inline void plugin_gen_disable_mem_helpers(void)
+{ }
+
+static inline void plugin_gen_empty_mem_callback(TCGv addr, uint8_t info)
+{ }
+
+#endif /* CONFIG_PLUGIN */
+
+#endif /* QEMU_PLUGIN_GEN_H */
+
diff --git a/tcg/tcg-op.h b/tcg/tcg-op.h
index 7513c1eb7c..e2948b10a2 100644
--- a/tcg/tcg-op.h
+++ b/tcg/tcg-op.h
@@ -822,6 +822,17 @@ void tcg_gen_goto_tb(unsigned idx);
  */
 void tcg_gen_lookup_and_goto_ptr(void);
 
+static inline void tcg_gen_plugin_cb_start(unsigned from, unsigned type,
+                                           unsigned wr)
+{
+    tcg_gen_op3(INDEX_op_plugin_cb_start, from, type, wr);
+}
+
+static inline void tcg_gen_plugin_cb_end(void)
+{
+    tcg_emit_op(INDEX_op_plugin_cb_end);
+}
+
 #if TARGET_LONG_BITS == 32
 #define tcg_temp_new() tcg_temp_new_i32()
 #define tcg_global_reg_new tcg_global_reg_new_i32
diff --git a/tcg/tcg-opc.h b/tcg/tcg-opc.h
index e3a43aabb6..d15d0b9874 100644
--- a/tcg/tcg-opc.h
+++ b/tcg/tcg-opc.h
@@ -195,6 +195,9 @@ DEF(exit_tb, 0, 0, 1, TCG_OPF_BB_END)
 DEF(goto_tb, 0, 0, 1, TCG_OPF_BB_END)
 DEF(goto_ptr, 0, 1, 0, TCG_OPF_BB_END | IMPL(TCG_TARGET_HAS_goto_ptr))
 
+DEF(plugin_cb_start, 0, 0, 3, TCG_OPF_NOT_PRESENT)
+DEF(plugin_cb_end, 0, 0, 0, TCG_OPF_NOT_PRESENT)
+
 DEF(qemu_ld_i32, 1, TLADDR_ARGS, 1,
     TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
 DEF(qemu_st_i32, 0, TLADDR_ARGS + 1, 1,
diff --git a/tcg/tcg.h b/tcg/tcg.h
index a745e926bb..6fd525023b 100644
--- a/tcg/tcg.h
+++ b/tcg/tcg.h
@@ -615,6 +615,9 @@ typedef struct TCGOp {
 
     /* Next and previous opcodes.  */
     QTAILQ_ENTRY(TCGOp) link;
+#ifdef CONFIG_PLUGIN
+    QSIMPLEQ_ENTRY(TCGOp) plugin_link;
+#endif
 
     /* Arguments for the opcode.  */
     TCGArg args[MAX_OPC_PARAM];
@@ -712,6 +715,23 @@ struct TCGContext {
 
     TCGLabel *exitreq_label;
 
+#ifdef CONFIG_PLUGIN
+    /*
+     * We keep one plugin_tb struct per TCGContext. Note that on every TB
+     * translation we clear but do not free its contents; this way we
+     * avoid a lot of malloc/free churn, since after a few TB's it's
+     * unlikely that we'll need to allocate either more instructions or more
+     * space for instructions (for variable-instruction-length ISAs).
+     */
+    struct qemu_plugin_tb *plugin_tb;
+
+    /* descriptor of the instruction being translated */
+    struct qemu_plugin_insn *plugin_insn;
+
+    /* list to quickly access the injected ops */
+    QSIMPLEQ_HEAD(, TCGOp) plugin_ops;
+#endif
+
     TCGTempSet free_temps[TCG_TYPE_COUNT * 2];
     TCGTemp temps[TCG_MAX_TEMPS]; /* globals first, temps after */
 
diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c
new file mode 100644
index 0000000000..06ec23e9f5
--- /dev/null
+++ b/accel/tcg/plugin-gen.c
@@ -0,0 +1,1077 @@
+/*
+ * plugin-gen.c - TCG-related bits of plugin infrastructure
+ *
+ * Copyright (C) 2018, Emilio G. Cota <cota@braap.org>
+ * License: GNU GPL, version 2 or later.
+ *   See the COPYING file in the top-level directory.
+ *
+ * We support instrumentation at an instruction granularity. That is,
+ * if a plugin wants to instrument the memory accesses performed by a
+ * particular instruction, it can just do that instead of instrumenting
+ * all memory accesses. Thus, in order to do this we first have to
+ * translate a TB, so that plugins can decide what/where to instrument.
+ *
+ * Injecting the desired instrumentation could be done with a second
+ * translation pass that combined the instrumentation requests, but that
+ * would be ugly and inefficient since we would decode the guest code twice.
+ * Instead, during TB translation we add "empty" instrumentation calls for all
+ * possible instrumentation events, and then once we collect the instrumentation
+ * requests from plugins, we either "fill in" those empty events or remove them
+ * if they have no requests.
+ *
+ * When "filling in" an event we first copy the empty callback's TCG ops. This
+ * might seem unnecessary, but it is done to support an arbitrary number
+ * of callbacks per event. Take for example a regular instruction callback.
+ * We first generate a callback to an empty helper function. Then, if two
+ * plugins register one callback each for this instruction, we make two copies
+ * of the TCG ops generated for the empty callback, substituting the function
+ * pointer that points to the empty helper function with the plugins' desired
+ * callback functions. After that we remove the empty callback's ops.
+ *
+ * Note that the location in TCGOp.args[] of the pointer to a helper function
+ * varies across different guest and host architectures. Instead of duplicating
+ * the logic that figures this out, we rely on the fact that the empty
+ * callbacks point to empty functions that are unique pointers in the program.
+ * Thus, to find the right location we just have to look for a match in
+ * TCGOp.args[]. This is the main reason why we first copy an empty callback's
+ * TCG ops and then fill them in; regardless of whether we have one or many
+ * callbacks for that event, the logic to add all of them is the same.
+ *
+ * When generating more than one callback per event, we make a small
+ * optimization to avoid generating redundant operations. For instance, for the
+ * second and all subsequent callbacks of an event, we do not need to reload the
+ * CPU's index into a TCG temp, since the first callback did it already.
+ */
+#include "qemu/osdep.h"
+#include "cpu.h"
+#include "tcg/tcg.h"
+#include "tcg/tcg-op.h"
+#include "trace/mem.h"
+#include "exec/exec-all.h"
+#include "exec/plugin-gen.h"
+#include "exec/translator.h"
+
+#ifdef CONFIG_SOFTMMU
+# define CONFIG_SOFTMMU_GATE 1
+#else
+# define CONFIG_SOFTMMU_GATE 0
+#endif
+
+/*
+ * plugin_cb_start TCG op args[]:
+ * 0: enum plugin_gen_from
+ * 1: enum plugin_gen_cb (defined below)
+ * 2: set to 1 if it's a mem callback and it's a write, 0 otherwise.
+ */
+enum plugin_gen_cb {
+    PLUGIN_GEN_CB_UDATA,
+    PLUGIN_GEN_CB_INLINE,
+    PLUGIN_GEN_CB_MEM,
+    PLUGIN_GEN_CB_HADDR,
+    PLUGIN_GEN_ENABLE_MEM_HELPER,
+    PLUGIN_GEN_DISABLE_MEM_HELPER,
+    PLUGIN_GEN_N_CBS,
+};
+
+void HELPER(plugin_vcpu_udata_cb)(uint32_t cpu_index, void *udata)
+{ }
+
+void HELPER(plugin_vcpu_mem_cb)(unsigned int vcpu_index,
+                                qemu_plugin_meminfo_t info, uint64_t vaddr,
+                                void *userdata)
+{ }
+
+void HELPER(plugin_vcpu_mem_haddr_cb)(unsigned int vcpu_index,
+                                      qemu_plugin_meminfo_t info,
+                                      uint64_t vaddr, void *haddr,
+                                      void *userdata)
+{ }
+
+static void do_gen_mem_cb(TCGv vaddr, uint8_t info, bool is_haddr)
+{
+    TCGv_i32 cpu_index = tcg_temp_new_i32();
+    TCGv_i32 meminfo = tcg_const_i32(info);
+    TCGv_i64 vaddr64 = tcg_temp_new_i64();
+    TCGv_ptr udata = tcg_const_ptr(NULL);
+    TCGv_ptr haddr;
+
+    tcg_gen_ld_i32(cpu_index, cpu_env,
+                   -ENV_OFFSET + offsetof(CPUState, cpu_index));
+    tcg_gen_extu_tl_i64(vaddr64, vaddr);
+
+    if (is_haddr) {
+        /*
+         * Can't use CONFIG_SOFTMMU_GATE here because CPUArchState.hostaddr
+         * only exists in CONFIG_SOFTMMU.
+         */
+#ifdef CONFIG_SOFTMMU
+        haddr = tcg_temp_new_ptr();
+        tcg_gen_ld_ptr(haddr, cpu_env, offsetof(CPUArchState, hostaddr));
+#else
+        haddr = tcg_const_ptr(NULL);
+#endif
+        gen_helper_plugin_vcpu_mem_haddr_cb(cpu_index, meminfo, vaddr64,
+                                            haddr, udata);
+        tcg_temp_free_ptr(haddr);
+    } else {
+        gen_helper_plugin_vcpu_mem_cb(cpu_index, meminfo, vaddr64, udata);
+    }
+
+    tcg_temp_free_ptr(udata);
+    tcg_temp_free_i64(vaddr64);
+    tcg_temp_free_i32(meminfo);
+    tcg_temp_free_i32(cpu_index);
+}
+
+static void gen_empty_udata_cb(void)
+{
+    TCGv_i32 cpu_index = tcg_temp_new_i32();
+    TCGv_ptr udata = tcg_const_ptr(NULL); /* will be overwritten later */
+
+    tcg_gen_ld_i32(cpu_index, cpu_env,
+                   -ENV_OFFSET + offsetof(CPUState, cpu_index));
+    gen_helper_plugin_vcpu_udata_cb(cpu_index, udata);
+
+    tcg_temp_free_ptr(udata);
+    tcg_temp_free_i32(cpu_index);
+}
+
+/*
+ * For now we only support addi_i64.
+ * When we support more ops, we can generate one empty inline cb for each.
+ */
+static void gen_empty_inline_cb(void)
+{
+    TCGv_i64 val = tcg_temp_new_i64();
+    TCGv_ptr ptr = tcg_const_ptr(NULL); /* overwritten later */
+
+    tcg_gen_ld_i64(val, ptr, 0);
+    /* pass an immediate != 0 so that it doesn't get optimized away */
+    tcg_gen_addi_i64(val, val, 0xdeadface);
+    tcg_gen_st_i64(val, ptr, 0);
+    tcg_temp_free_ptr(ptr);
+    tcg_temp_free_i64(val);
+}
+
+static void gen_empty_mem_cb(TCGv addr, uint8_t info)
+{
+    do_gen_mem_cb(addr, info, false);
+}
+
+static void gen_empty_haddr_cb(TCGv addr, uint8_t info)
+{
+    do_gen_mem_cb(addr, info, true);
+}
+
+/*
+ * Share the same function for enable/disable. When enabling, the NULL
+ * pointer will be overwritten later.
+ */
+static void gen_empty_mem_helper(void)
+{
+    TCGv_ptr ptr;
+
+    ptr = tcg_const_ptr(NULL);
+    tcg_gen_st_ptr(ptr, cpu_env, -ENV_OFFSET + offsetof(CPUState,
+                                                        plugin_mem_cbs));
+    tcg_temp_free_ptr(ptr);
+}
+
+static inline
+void gen_plugin_cb_start(enum plugin_gen_from from,
+                         enum plugin_gen_cb type, unsigned wr)
+{
+    TCGOp *op;
+
+    tcg_gen_plugin_cb_start(from, type, wr);
+    op = tcg_last_op();
+    QSIMPLEQ_INSERT_TAIL(&tcg_ctx->plugin_ops, op, plugin_link);
+}
+
+static void gen_wrapped(enum plugin_gen_from from,
+                        enum plugin_gen_cb type, void (*func)(void))
+{
+    gen_plugin_cb_start(from, type, 0);
+    func();
+    tcg_gen_plugin_cb_end();
+}
+
+static inline void plugin_gen_empty_callback(enum plugin_gen_from from)
+{
+    switch (from) {
+    case PLUGIN_GEN_AFTER_INSN:
+        gen_wrapped(from, PLUGIN_GEN_DISABLE_MEM_HELPER,
+                    gen_empty_mem_helper);
+        break;
+    case PLUGIN_GEN_FROM_INSN:
+        /*
+         * Note: plugin_gen_inject() relies on ENABLE_MEM_HELPER being
+         * the first callback of an instruction
+         */
+        gen_wrapped(from, PLUGIN_GEN_ENABLE_MEM_HELPER,
+                    gen_empty_mem_helper);
+        /* fall through */
+    case PLUGIN_GEN_FROM_TB:
+        gen_wrapped(from, PLUGIN_GEN_CB_UDATA, gen_empty_udata_cb);
+        gen_wrapped(from, PLUGIN_GEN_CB_INLINE, gen_empty_inline_cb);
+        break;
+    default:
+        g_assert_not_reached();
+    }
+}
+
+union mem_gen_fn {
+    void (*mem_fn)(TCGv, uint8_t);
+    void (*inline_fn)(void);
+};
+
+static void gen_mem_wrapped(enum plugin_gen_cb type,
+                            const union mem_gen_fn *f, TCGv addr,
+                            uint8_t info, bool is_mem)
+{
+    int wr = !!(info & TRACE_MEM_ST);
+
+    gen_plugin_cb_start(PLUGIN_GEN_FROM_MEM, type, wr);
+    if (is_mem) {
+        f->mem_fn(addr, info);
+    } else {
+        f->inline_fn();
+    }
+    tcg_gen_plugin_cb_end();
+}
+
+void plugin_gen_empty_mem_callback(TCGv addr, uint8_t info)
+{
+    union mem_gen_fn fn;
+
+    fn.mem_fn = gen_empty_mem_cb;
+    gen_mem_wrapped(PLUGIN_GEN_CB_MEM, &fn, addr, info, true);
+
+    fn.mem_fn = gen_empty_haddr_cb;
+    gen_mem_wrapped(PLUGIN_GEN_CB_HADDR, &fn, addr, info, true);
+
+    fn.inline_fn = gen_empty_inline_cb;
+    gen_mem_wrapped(PLUGIN_GEN_CB_INLINE, &fn, 0, info, false);
+}
+
+static TCGOp *find_op(TCGOp *op, TCGOpcode opc)
+{
+    while (op) {
+        if (op->opc == opc) {
+            return op;
+        }
+        op = QTAILQ_NEXT(op, link);
+    }
+    return NULL;
+}
+
+static TCGOp *rm_ops_range(TCGOp *begin, TCGOp *end)
+{
+    TCGOp *ret = QTAILQ_NEXT(end, link);
+
+    QTAILQ_REMOVE_SEVERAL(&tcg_ctx->ops, begin, end, link);
+    return ret;
+}
+
+/* remove all ops until (and including) plugin_cb_end */
+static TCGOp *rm_ops(TCGOp *op)
+{
+    TCGOp *end_op = find_op(op, INDEX_op_plugin_cb_end);
+
+    tcg_debug_assert(end_op);
+    return rm_ops_range(op, end_op);
+}
+
+static TCGOp *copy_op_nocheck(TCGOp **begin_op, TCGOp *op)
+{
+    *begin_op = QTAILQ_NEXT(*begin_op, link);
+    tcg_debug_assert(*begin_op);
+    op = tcg_op_insert_after(tcg_ctx, op, (*begin_op)->opc);
+    memcpy(op->args, (*begin_op)->args, sizeof(op->args));
+    return op;
+}
+
+static TCGOp *copy_op(TCGOp **begin_op, TCGOp *op, TCGOpcode opc)
+{
+    op = copy_op_nocheck(begin_op, op);
+    tcg_debug_assert((*begin_op)->opc == opc);
+    return op;
+}
+
+static TCGOp *copy_extu_i32_i64(TCGOp **begin_op, TCGOp *op)
+{
+    if (TCG_TARGET_REG_BITS == 32) {
+        /* mov_i32 */
+        op = copy_op(begin_op, op, INDEX_op_mov_i32);
+        /* movi_i32 */
+        op = copy_op(begin_op, op, INDEX_op_movi_i32);
+    } else {
+        /* extu_i32_i64 */
+        op = copy_op(begin_op, op, INDEX_op_extu_i32_i64);
+    }
+    return op;
+}
+
+static TCGOp *copy_mov_i64(TCGOp **begin_op, TCGOp *op)
+{
+    if (TCG_TARGET_REG_BITS == 32) {
+        /* 2x mov_i32 */
+        op = copy_op(begin_op, op, INDEX_op_mov_i32);
+        op = copy_op(begin_op, op, INDEX_op_mov_i32);
+    } else {
+        /* mov_i64 */
+        op = copy_op(begin_op, op, INDEX_op_mov_i64);
+    }
+    return op;
+}
+
+static TCGOp *copy_movi_i64(TCGOp **begin_op, TCGOp *op, uint64_t v)
+{
+    if (TCG_TARGET_REG_BITS == 32) {
+        /* 2x movi_i32 */
+        op = copy_op(begin_op, op, INDEX_op_movi_i32);
+        op->args[1] = v;
+
+        op = copy_op(begin_op, op, INDEX_op_movi_i32);
+        op->args[1] = v >> 32;
+    } else {
+        /* movi_i64 */
+        op = copy_op(begin_op, op, INDEX_op_movi_i64);
+        op->args[1] = v;
+    }
+    return op;
+}
+
+static TCGOp *copy_const_ptr(TCGOp **begin_op, TCGOp *op, void *ptr)
+{
+    if (UINTPTR_MAX == UINT32_MAX) {
+        /* movi_i32 */
+        op = copy_op(begin_op, op, INDEX_op_movi_i32);
+        op->args[1] = (uintptr_t)ptr;
+    } else {
+        /* movi_i64 */
+        op = copy_movi_i64(begin_op, op, (uint64_t)(uintptr_t)ptr);
+    }
+    return op;
+}
+
+static TCGOp *copy_const_i64(TCGOp **begin_op, TCGOp *op, uint64_t v)
+{
+    return copy_movi_i64(begin_op, op, v);
+}
+
+static TCGOp *copy_extu_tl_i64(TCGOp **begin_op, TCGOp *op)
+{
+    if (TARGET_LONG_BITS == 32) {
+        /* extu_i32_i64 */
+        op = copy_extu_i32_i64(begin_op, op);
+    } else {
+        /* mov_i64 */
+        op = copy_mov_i64(begin_op, op);
+    }
+    return op;
+}
+
+static TCGOp *copy_ld_i64(TCGOp **begin_op, TCGOp *op)
+{
+    if (TCG_TARGET_REG_BITS == 32) {
+        /* 2x ld_i32 */
+        op = copy_op(begin_op, op, INDEX_op_ld_i32);
+        op = copy_op(begin_op, op, INDEX_op_ld_i32);
+    } else {
+        /* ld_i64 */
+        op = copy_op(begin_op, op, INDEX_op_ld_i64);
+    }
+    return op;
+}
+
+static TCGOp *copy_ld_ptr(TCGOp **begin_op, TCGOp *op)
+{
+    if (UINTPTR_MAX == UINT32_MAX) {
+        /* ld_i32 */
+        op = copy_op(begin_op, op, INDEX_op_ld_i32);
+    } else {
+        /* ld_i64 */
+        op = copy_ld_i64(begin_op, op);
+    }
+    return op;
+}
+
+static TCGOp *skip_ld_i64(TCGOp *begin_op)
+{
+    TCGOp *op;
+
+    if (TCG_TARGET_REG_BITS == 32) {
+        /* 2x ld_i32 */
+        op = QTAILQ_NEXT(begin_op, link);
+        tcg_debug_assert(op->opc == INDEX_op_ld_i32);
+        op = QTAILQ_NEXT(op, link);
+        tcg_debug_assert(op->opc == INDEX_op_ld_i32);
+    } else {
+        /* ld_i64 */
+        op = QTAILQ_NEXT(begin_op, link);
+        tcg_debug_assert(op->opc == INDEX_op_ld_i64);
+    }
+    return op;
+}
+
+static TCGOp *skip_ld_ptr(TCGOp *begin_op)
+{
+    TCGOp *op;
+
+    if (UINTPTR_MAX == UINT32_MAX) {
+        /* ld_i32 */
+        op = QTAILQ_NEXT(begin_op, link);
+        tcg_debug_assert(op->opc == INDEX_op_ld_i32);
+    } else {
+        /* ld_i64 */
+        op = skip_ld_i64(begin_op);
+    }
+    return op;
+}
+
+static TCGOp *copy_st_i64(TCGOp **begin_op, TCGOp *op)
+{
+    if (TCG_TARGET_REG_BITS == 32) {
+        /* 2x st_i32 */
+        op = copy_op(begin_op, op, INDEX_op_st_i32);
+        op = copy_op(begin_op, op, INDEX_op_st_i32);
+    } else {
+        /* st_i64 */
+        op = copy_op(begin_op, op, INDEX_op_st_i64);
+    }
+    return op;
+}
+
+static TCGOp *copy_add_i64(TCGOp **begin_op, TCGOp *op)
+{
+    if (TCG_TARGET_REG_BITS == 32) {
+        /* all 32-bit backends must implement add2_i32 */
+        g_assert(TCG_TARGET_HAS_add2_i32);
+        op = copy_op(begin_op, op, INDEX_op_add2_i32);
+    } else {
+        op = copy_op(begin_op, op, INDEX_op_add_i64);
+    }
+    return op;
+}
+
+static TCGOp *copy_st_ptr(TCGOp **begin_op, TCGOp *op)
+{
+    if (UINTPTR_MAX == UINT32_MAX) {
+        /* st_i32 */
+        op = copy_op(begin_op, op, INDEX_op_st_i32);
+    } else {
+        /* st_i64 */
+        op = copy_st_i64(begin_op, op);
+    }
+    return op;
+}
+
+static TCGOp *copy_call(TCGOp **begin_op, TCGOp *op, void *empty_func,
+                        void *func, unsigned tcg_flags, int *cb_idx)
+{
+    /* copy all ops until the call */
+    do {
+        op = copy_op_nocheck(begin_op, op);
+    } while (op->opc != INDEX_op_call);
+
+    /* fill in the op call */
+    op->param1 = (*begin_op)->param1;
+    op->param2 = (*begin_op)->param2;
+    tcg_debug_assert(op->life == 0);
+    if (*cb_idx == -1) {
+        int i;
+
+        /*
+         * Instead of working out the position of the callback in args[], just
+         * look for @empty_func, since it should be a unique pointer.
+         */
+        for (i = 0; i < MAX_OPC_PARAM_ARGS; i++) {
+            if ((uintptr_t)(*begin_op)->args[i] == (uintptr_t)empty_func) {
+                *cb_idx = i;
+                break;
+            }
+        }
+        tcg_debug_assert(i < MAX_OPC_PARAM_ARGS);
+    }
+    op->args[*cb_idx] = (uintptr_t)func;
+    op->args[*cb_idx + 1] = tcg_flags;
+
+    return op;
+}
+
+static TCGOp *append_udata_cb(const struct qemu_plugin_dyn_cb *cb,
+                              TCGOp *begin_op, TCGOp *op, int *cb_idx)
+{
+    /* const_ptr */
+    op = copy_const_ptr(&begin_op, op, cb->userp);
+
+    /* copy the ld_i32, but note that we only have to copy it once */
+    begin_op = QTAILQ_NEXT(begin_op, link);
+    tcg_debug_assert(begin_op && begin_op->opc == INDEX_op_ld_i32);
+    if (*cb_idx == -1) {
+        op = tcg_op_insert_after(tcg_ctx, op, INDEX_op_ld_i32);
+        memcpy(op->args, begin_op->args, sizeof(op->args));
+    }
+
+    /* call */
+    op = copy_call(&begin_op, op, HELPER(plugin_vcpu_udata_cb),
+                   cb->f.vcpu_udata, cb->tcg_flags, cb_idx);
+
+    return op;
+}
+
+static TCGOp *append_inline_cb(const struct qemu_plugin_dyn_cb *cb,
+                               TCGOp *begin_op, TCGOp *op,
+                               int *unused)
+{
+    /* const_ptr */
+    op = copy_const_ptr(&begin_op, op, cb->userp);
+
+    /* ld_i64 */
+    op = copy_ld_i64(&begin_op, op);
+
+    /* const_i64 */
+    op = copy_const_i64(&begin_op, op, cb->inline_insn.imm);
+
+    /* add_i64 */
+    op = copy_add_i64(&begin_op, op);
+
+    /* st_i64 */
+    op = copy_st_i64(&begin_op, op);
+
+    return op;
+}
+
+static void set_memop_haddr_ldst_i32(TCGOp *op)
+{
+    int idx;
+
+    if (TARGET_LONG_BITS == 32) {
+        idx = 2;
+    } else {
+        idx = TCG_TARGET_REG_BITS == 32 ? 3 : 2;
+    }
+    op->args[idx] |= make_memop_idx(MO_HADDR, 0);
+}
+
+static void set_memop_haddr_ldst_i64(TCGOp *op)
+{
+    int idx;
+
+    if (TARGET_LONG_BITS == 32) {
+        idx = TCG_TARGET_REG_BITS == 32 ? 3 : 2;
+    } else {
+        idx = TCG_TARGET_REG_BITS == 32 ? 4 : 2;
+    }
+    op->args[idx] |= make_memop_idx(MO_HADDR, 0);
+}
+
+static void find_prev_and_set_memop_haddr(TCGOp *op)
+{
+    while ((op = QTAILQ_PREV(op, TCGOpHead, link))) {
+        switch (op->opc) {
+        case INDEX_op_qemu_ld_i32:
+        case INDEX_op_qemu_st_i32:
+            set_memop_haddr_ldst_i32(op);
+            return;
+        case INDEX_op_qemu_ld_i64:
+        case INDEX_op_qemu_st_i64:
+            set_memop_haddr_ldst_i64(op);
+            return;
+        default:
+            break;
+        }
+    }
+    g_assert_not_reached();
+}
+
+static TCGOp *append_mem_cb(const struct qemu_plugin_dyn_cb *cb,
+                            TCGOp *begin_op, TCGOp *op, int *cb_idx)
+{
+    enum plugin_gen_cb type = begin_op->args[1];
+
+    tcg_debug_assert(type == PLUGIN_GEN_CB_MEM || type == PLUGIN_GEN_CB_HADDR);
+
+    /* const_i32 == movi_i32 ("info", so it remains as is) */
+    op = copy_op(&begin_op, op, INDEX_op_movi_i32);
+
+    /* const_ptr */
+    op = copy_const_ptr(&begin_op, op, cb->userp);
+
+    /* copy the ld_i32, but note that we only have to copy it once */
+    begin_op = QTAILQ_NEXT(begin_op, link);
+    tcg_debug_assert(begin_op && begin_op->opc == INDEX_op_ld_i32);
+    if (*cb_idx == -1) {
+        op = tcg_op_insert_after(tcg_ctx, op, INDEX_op_ld_i32);
+        memcpy(op->args, begin_op->args, sizeof(op->args));
+    }
+
+    /* extu_tl_i64 */
+    op = copy_extu_tl_i64(&begin_op, op);
+
+    if (type == PLUGIN_GEN_CB_MEM) {
+        /* call */
+        op = copy_call(&begin_op, op, HELPER(plugin_vcpu_mem_cb),
+                       cb->f.vcpu_udata, cb->tcg_flags, cb_idx);
+    } else {
+        if (CONFIG_SOFTMMU_GATE) {
+            /* ld_ptr; note that we only have to copy it once */
+            if (*cb_idx == -1) {
+                op = copy_ld_ptr(&begin_op, op);
+                find_prev_and_set_memop_haddr(begin_op);
+            } else {
+                begin_op = skip_ld_ptr(begin_op);
+            }
+        } else {
+            /* const_ptr; set it to NULL */
+            op = copy_const_ptr(&begin_op, op, NULL);
+        }
+        /* call */
+        op = copy_call(&begin_op, op, HELPER(plugin_vcpu_mem_haddr_cb),
+                       cb->f.vcpu_mem_haddr, cb->tcg_flags, cb_idx);
+    }
+
+    return op;
+}
+
+typedef TCGOp *(*inject_fn)(const struct qemu_plugin_dyn_cb *cb,
+                            TCGOp *begin_op, TCGOp *op, int *intp);
+typedef bool (*op_ok_fn)(const TCGOp *op, const struct qemu_plugin_dyn_cb *cb);
+
+static bool op_ok(const TCGOp *op, const struct qemu_plugin_dyn_cb *cb)
+{
+    return true;
+}
+
+static bool op_rw(const TCGOp *op, const struct qemu_plugin_dyn_cb *cb)
+{
+    int w;
+
+    w = op->args[2];
+    return !!(cb->rw & (w + 1));
+}
+
+static inline
+void inject_cb_type(const struct qemu_plugin_dyn_cb_arr *cbs, TCGOp *begin_op,
+                    inject_fn inject, op_ok_fn ok)
+{
+    TCGOp *end_op;
+    TCGOp *op;
+    int cb_idx = -1;
+    int i;
+
+    if (cbs->n == 0) {
+        rm_ops(begin_op);
+        return;
+    }
+
+    end_op = find_op(begin_op, INDEX_op_plugin_cb_end);
+    tcg_debug_assert(end_op);
+
+    op = end_op;
+    for (i = 0; i < cbs->n; i++) {
+        struct qemu_plugin_dyn_cb *cb = &cbs->data[i];
+
+        if (!ok(begin_op, cb)) {
+            continue;
+        }
+        op = inject(cb, begin_op, op, &cb_idx);
+    }
+    rm_ops_range(begin_op, end_op);
+}
+
+static void
+inject_udata_cb(const struct qemu_plugin_dyn_cb_arr *cbs, TCGOp *begin_op)
+{
+    inject_cb_type(cbs, begin_op, append_udata_cb, op_ok);
+}
+
+static void
+inject_inline_cb(const struct qemu_plugin_dyn_cb_arr *cbs, TCGOp *begin_op,
+                 op_ok_fn ok)
+{
+    inject_cb_type(cbs, begin_op, append_inline_cb, ok);
+}
+
+static void
+inject_mem_cb(const struct qemu_plugin_dyn_cb_arr *cbs, TCGOp *begin_op)
+{
+    inject_cb_type(cbs, begin_op, append_mem_cb, op_rw);
+}
+
+/* we could change the ops in place, but we can reuse more code by copying */
+static void inject_mem_helper(TCGOp *begin_op,
+                              struct qemu_plugin_dyn_cb_arr *arr)
+{
+    TCGOp *orig_op = begin_op;
+    TCGOp *end_op;
+    TCGOp *op;
+
+    end_op = find_op(begin_op, INDEX_op_plugin_cb_end);
+    tcg_debug_assert(end_op);
+
+    /* const ptr */
+    op = copy_const_ptr(&begin_op, end_op, arr);
+
+    /* st_ptr */
+    op = copy_st_ptr(&begin_op, op);
+
+    rm_ops_range(orig_op, end_op);
+}
+
+/*
+ * Tracking memory accesses performed from helpers requires extra work.
+ * If an instruction is emulated with helpers, we do two things:
+ * (1) copy the CB descriptors, and keep track of it so that they can be
+ * freed later on, and (2) point CPUState.plugin_mem_cbs to the descriptors, so
+ * that we can read them at run-time (i.e. when the helper executes).
+ * This run-time access is performed from qemu_plugin_vcpu_mem_cb.
+ *
+ * Note that plugin_gen_disable_mem_helpers undoes (2). Since it
+ * is possible that the code we generate after the instruction is
+ * dead, we also add checks before generating tb_exit etc.
+ */
+static void inject_mem_enable_helper(struct qemu_plugin_insn *plugin_insn,
+                                     TCGOp *begin_op)
+{
+    const struct qemu_plugin_dyn_cb_arr *cbs[3];
+    struct qemu_plugin_dyn_cb_arr *arr;
+    size_t total, n_cbs, i;
+
+    cbs[0] = &plugin_insn->cbs[PLUGIN_CB_MEM][PLUGIN_CB_REGULAR];
+    cbs[1] = &plugin_insn->cbs[PLUGIN_CB_MEM][PLUGIN_CB_INLINE];
+    cbs[2] = &plugin_insn->cbs[PLUGIN_CB_HADDR][PLUGIN_CB_REGULAR];
+
+    n_cbs = 0;
+    for (i = 0; i < ARRAY_SIZE(cbs); i++) {
+        n_cbs += cbs[i]->n;
+    }
+
+    plugin_insn->mem_helper = plugin_insn->calls_helpers && n_cbs;
+    if (likely(!plugin_insn->mem_helper)) {
+        rm_ops(begin_op);
+        return;
+    }
+
+    arr = g_new(struct qemu_plugin_dyn_cb_arr, 1);
+    arr->capacity = n_cbs;
+    arr->n = n_cbs;
+    arr->data = g_new(struct qemu_plugin_dyn_cb, arr->n);
+
+    total = 0;
+    for (i = 0; i < ARRAY_SIZE(cbs); i++) {
+        const struct qemu_plugin_dyn_cb_arr *from = cbs[i];
+
+        memcpy(arr->data + total, from->data, sizeof(*arr->data) * from->n);
+        total += from->n;
+    }
+
+    qemu_plugin_add_dyn_cb_arr(arr);
+    inject_mem_helper(begin_op, arr);
+}
+
+static void inject_mem_disable_helper(struct qemu_plugin_insn *plugin_insn,
+                                      TCGOp *begin_op)
+{
+    if (likely(!plugin_insn->mem_helper)) {
+        rm_ops(begin_op);
+        return;
+    }
+    inject_mem_helper(begin_op, NULL);
+}
+
+/* called before finishing a TB with exit_tb, goto_tb or goto_ptr */
+void plugin_gen_disable_mem_helpers(void)
+{
+    TCGv_ptr ptr;
+
+    if (likely(tcg_ctx->plugin_insn == NULL ||
+               !tcg_ctx->plugin_insn->mem_helper)) {
+        return;
+    }
+    ptr = tcg_const_ptr(NULL);
+    tcg_gen_st_ptr(ptr, cpu_env, -ENV_OFFSET + offsetof(CPUState,
+                                                        plugin_mem_cbs));
+    tcg_temp_free_ptr(ptr);
+    tcg_ctx->plugin_insn->mem_helper = false;
+}
+
+static void plugin_gen_tb_udata(const struct qemu_plugin_tb *ptb,
+                                TCGOp *begin_op)
+{
+    inject_udata_cb(&ptb->cbs[PLUGIN_CB_REGULAR], begin_op);
+}
+
+static void plugin_gen_tb_inline(const struct qemu_plugin_tb *ptb,
+                                 TCGOp *begin_op)
+{
+    inject_inline_cb(&ptb->cbs[PLUGIN_CB_INLINE], begin_op, op_ok);
+}
+
+static void plugin_gen_insn_udata(const struct qemu_plugin_tb *ptb,
+                                  TCGOp *begin_op, int insn_idx)
+{
+    const struct qemu_plugin_dyn_cb_arr *cbs;
+
+    cbs = &ptb->insns[insn_idx].cbs[PLUGIN_CB_INSN][PLUGIN_CB_REGULAR];
+    inject_udata_cb(cbs, begin_op);
+}
+
+static void plugin_gen_insn_inline(const struct qemu_plugin_tb *ptb,
+                                   TCGOp *begin_op, int insn_idx)
+{
+    const struct qemu_plugin_dyn_cb_arr *cbs;
+
+    cbs = &ptb->insns[insn_idx].cbs[PLUGIN_CB_INSN][PLUGIN_CB_INLINE];
+    inject_inline_cb(cbs, begin_op, op_ok);
+}
+
+static void plugin_gen_mem_regular(const struct qemu_plugin_tb *ptb,
+                                   TCGOp *begin_op, int insn_idx)
+{
+    const struct qemu_plugin_dyn_cb_arr *cbs;
+
+    cbs = &ptb->insns[insn_idx].cbs[PLUGIN_CB_MEM][PLUGIN_CB_REGULAR];
+    inject_mem_cb(cbs, begin_op);
+}
+
+static void plugin_gen_mem_inline(const struct qemu_plugin_tb *ptb,
+                                  TCGOp *begin_op, int insn_idx)
+{
+    const struct qemu_plugin_dyn_cb_arr *cbs;
+
+    cbs = &ptb->insns[insn_idx].cbs[PLUGIN_CB_MEM][PLUGIN_CB_INLINE];
+    inject_inline_cb(cbs, begin_op, op_rw);
+}
+
+static void plugin_gen_haddr_regular(const struct qemu_plugin_tb *ptb,
+                                     TCGOp *begin_op, int insn_idx)
+{
+    const struct qemu_plugin_dyn_cb_arr *cbs;
+
+    cbs = &ptb->insns[insn_idx].cbs[PLUGIN_CB_HADDR][PLUGIN_CB_REGULAR];
+    inject_mem_cb(cbs, begin_op);
+}
+
+static void plugin_gen_enable_mem_helper(const struct qemu_plugin_tb *ptb,
+                                         TCGOp *begin_op, int insn_idx)
+{
+    inject_mem_enable_helper(&ptb->insns[insn_idx], begin_op);
+}
+
+static void plugin_gen_disable_mem_helper(const struct qemu_plugin_tb *ptb,
+                                          TCGOp *begin_op, int insn_idx)
+{
+    inject_mem_disable_helper(&ptb->insns[insn_idx], begin_op);
+}
+
+static void plugin_inject_cb(const struct qemu_plugin_tb *ptb, TCGOp *begin_op,
+                             int insn_idx)
+{
+    enum plugin_gen_from from = begin_op->args[0];
+    enum plugin_gen_cb type = begin_op->args[1];
+
+    switch (from) {
+    case PLUGIN_GEN_FROM_TB:
+        switch (type) {
+        case PLUGIN_GEN_CB_UDATA:
+            plugin_gen_tb_udata(ptb, begin_op);
+            return;
+        case PLUGIN_GEN_CB_INLINE:
+            plugin_gen_tb_inline(ptb, begin_op);
+            return;
+        default:
+            g_assert_not_reached();
+        }
+    case PLUGIN_GEN_FROM_INSN:
+        switch (type) {
+        case PLUGIN_GEN_CB_UDATA:
+            plugin_gen_insn_udata(ptb, begin_op, insn_idx);
+            return;
+        case PLUGIN_GEN_CB_INLINE:
+            plugin_gen_insn_inline(ptb, begin_op, insn_idx);
+            return;
+        case PLUGIN_GEN_ENABLE_MEM_HELPER:
+            plugin_gen_enable_mem_helper(ptb, begin_op, insn_idx);
+            return;
+        default:
+            g_assert_not_reached();
+        }
+    case PLUGIN_GEN_FROM_MEM:
+        switch (type) {
+        case PLUGIN_GEN_CB_MEM:
+            plugin_gen_mem_regular(ptb, begin_op, insn_idx);
+            return;
+        case PLUGIN_GEN_CB_INLINE:
+            plugin_gen_mem_inline(ptb, begin_op, insn_idx);
+            return;
+        case PLUGIN_GEN_CB_HADDR:
+            plugin_gen_haddr_regular(ptb, begin_op, insn_idx);
+            return;
+        default:
+            g_assert_not_reached();
+        }
+    case PLUGIN_GEN_AFTER_INSN:
+        switch (type) {
+        case PLUGIN_GEN_DISABLE_MEM_HELPER:
+            plugin_gen_disable_mem_helper(ptb, begin_op, insn_idx);
+            return;
+        default:
+            g_assert_not_reached();
+        }
+    default:
+        g_assert_not_reached();
+    }
+}
+
+/* #define DEBUG_PLUGIN_GEN_OPS */
+static void pr_ops(void)
+{
+#ifdef DEBUG_PLUGIN_GEN_OPS
+    TCGOp *op;
+    int i = 0;
+
+    QTAILQ_FOREACH(op, &tcg_ctx->ops, link) {
+        const char *name = "";
+        const char *type = "";
+
+        if (op->opc == INDEX_op_plugin_cb_start) {
+            switch (op->args[0]) {
+            case PLUGIN_GEN_FROM_TB:
+                name = "tb";
+                break;
+            case PLUGIN_GEN_FROM_INSN:
+                name = "insn";
+                break;
+            case PLUGIN_GEN_FROM_MEM:
+                name = "mem";
+                break;
+            case PLUGIN_GEN_AFTER_INSN:
+                name = "after insn";
+                break;
+            default:
+                break;
+            }
+            switch (op->args[1]) {
+            case PLUGIN_GEN_CB_UDATA:
+                type = "udata";
+                break;
+            case PLUGIN_GEN_CB_INLINE:
+                type = "inline";
+                break;
+            case PLUGIN_GEN_CB_MEM:
+                type = "mem";
+                break;
+            case PLUGIN_GEN_CB_HADDR:
+                type = "haddr";
+                break;
+            case PLUGIN_GEN_ENABLE_MEM_HELPER:
+                type = "enable mem helper";
+                break;
+            case PLUGIN_GEN_DISABLE_MEM_HELPER:
+                type = "disable mem helper";
+                break;
+            default:
+                break;
+            }
+        }
+        printf("op[%2i]: %s %s %s\n", i, tcg_op_defs[op->opc].name, name, type);
+        i++;
+    }
+#endif
+}
+
+static void plugin_gen_inject(const struct qemu_plugin_tb *plugin_tb)
+{
+    TCGOp *op;
+    int insn_idx;
+
+    pr_ops();
+    insn_idx = -1;
+    QSIMPLEQ_FOREACH(op, &tcg_ctx->plugin_ops, plugin_link) {
+        enum plugin_gen_from from = op->args[0];
+        enum plugin_gen_cb type = op->args[1];
+
+        tcg_debug_assert(op->opc == INDEX_op_plugin_cb_start);
+        /* ENABLE_MEM_HELPER is the first callback of an instruction */
+        if (from == PLUGIN_GEN_FROM_INSN &&
+            type == PLUGIN_GEN_ENABLE_MEM_HELPER) {
+            insn_idx++;
+        }
+        plugin_inject_cb(plugin_tb, op, insn_idx);
+    }
+    pr_ops();
+}
+
+bool plugin_gen_tb_start(CPUState *cpu, const TranslationBlock *tb)
+{
+    struct qemu_plugin_tb *ptb = tcg_ctx->plugin_tb;
+    bool ret = false;
+
+    if (test_bit(QEMU_PLUGIN_EV_VCPU_TB_TRANS, cpu->plugin_mask)) {
+        ret = true;
+
+        QSIMPLEQ_INIT(&tcg_ctx->plugin_ops);
+        ptb->vaddr = tb->pc;
+        ptb->vaddr2 = -1;
+        get_page_addr_code_hostp(cpu->env_ptr, tb->pc, &ptb->haddr1);
+        ptb->haddr2 = NULL;
+
+        plugin_gen_empty_callback(PLUGIN_GEN_FROM_TB);
+    }
+    return ret;
+}
+
+void plugin_gen_insn_start(CPUState *cpu, const DisasContextBase *db)
+{
+    struct qemu_plugin_tb *ptb = tcg_ctx->plugin_tb;
+    struct qemu_plugin_insn *pinsn;
+
+    pinsn = qemu_plugin_tb_insn_get(ptb);
+    tcg_ctx->plugin_insn = pinsn;
+    pinsn->vaddr = db->pc_next;
+    plugin_gen_empty_callback(PLUGIN_GEN_FROM_INSN);
+
+    /*
+     * Detect page crossing to get the new host address.
+     * Note that we skip this when haddr1 == NULL, e.g. when we're
+     * fetching instructions from a region not backed by RAM.
+     */
+    if (likely(ptb->haddr1 != NULL && ptb->vaddr2 == -1) &&
+        unlikely((db->pc_next & TARGET_PAGE_MASK) !=
+                 (db->pc_first & TARGET_PAGE_MASK))) {
+        get_page_addr_code_hostp(cpu->env_ptr, db->pc_next,
+                                 &ptb->haddr2);
+        ptb->vaddr2 = db->pc_next;
+    }
+    if (likely(ptb->vaddr2 == -1)) {
+        pinsn->haddr = ptb->haddr1 + pinsn->vaddr - ptb->vaddr;
+    } else {
+        pinsn->haddr = ptb->haddr2 + pinsn->vaddr - ptb->vaddr2;
+    }
+}
+
+void plugin_gen_insn_end(void)
+{
+    plugin_gen_empty_callback(PLUGIN_GEN_AFTER_INSN);
+}
+
+void plugin_gen_tb_end(CPUState *cpu)
+{
+    struct qemu_plugin_tb *ptb = tcg_ctx->plugin_tb;
+    int i;
+
+    /* collect instrumentation requests */
+    qemu_plugin_tb_trans_cb(cpu, ptb);
+
+    /* inject the instrumentation at the appropriate places */
+    plugin_gen_inject(ptb);
+
+    /* clean up */
+    for (i = 0; i < PLUGIN_N_CB_SUBTYPES; i++) {
+        ptb->cbs[i].n = 0;
+    }
+    ptb->n = 0;
+    tcg_ctx->plugin_insn = NULL;
+}
diff --git a/tcg/tcg.c b/tcg/tcg.c
index e87c662a18..9d72f35133 100644
--- a/tcg/tcg.c
+++ b/tcg/tcg.c
@@ -749,6 +749,12 @@ void tcg_register_thread(void)
     g_assert(n < max_cpus);
     atomic_set(&tcg_ctxs[n], s);
 
+#ifdef CONFIG_PLUGIN
+    if (n > 0) {
+        s->plugin_tb = g_new0(struct qemu_plugin_tb, 1);
+    }
+#endif
+
     tcg_ctx = s;
     qemu_mutex_lock(&region.lock);
     err = tcg_region_initial_alloc__locked(tcg_ctx);
@@ -945,6 +951,10 @@ void tcg_context_init(TCGContext *s)
         indirect_reg_alloc_order[i] = tcg_target_reg_alloc_order[i];
     }
 
+#ifdef CONFIG_PLUGIN
+    s->plugin_tb = g_new0(struct qemu_plugin_tb, 1);
+#endif
+
     tcg_ctx = s;
     /*
      * In user-mode we simply share the init context among threads, since we
@@ -1626,6 +1636,13 @@ void tcg_gen_callN(void *func, TCGTemp *ret, int nargs, TCGTemp **args)
     flags = info->flags;
     sizemask = info->sizemask;
 
+#ifdef CONFIG_PLUGIN
+    /* detect non-plugin helpers */
+    if (tcg_ctx->plugin_insn && unlikely(strncmp(info->name, "plugin_", 7))) {
+        tcg_ctx->plugin_insn->calls_helpers = true;
+    }
+#endif
+
 #if defined(__sparc__) && !defined(__arch64__) \
     && !defined(CONFIG_TCG_INTERPRETER)
     /* We have 64-bit values in one register, but need to pass as two
diff --git a/accel/tcg/Makefile.objs b/accel/tcg/Makefile.objs
index d381a02f34..a92f2c454b 100644
--- a/accel/tcg/Makefile.objs
+++ b/accel/tcg/Makefile.objs
@@ -6,3 +6,4 @@ obj-y += translator.o
 
 obj-$(CONFIG_USER_ONLY) += user-exec.o
 obj-$(call lnot,$(CONFIG_SOFTMMU)) += user-exec-stub.o
+obj-$(CONFIG_PLUGIN) += plugin-gen.o
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [Qemu-devel] [RFC v2 11/38] tcg: add tcg_gen_st_ptr
  2018-12-09 19:37 [Qemu-devel] [RFC v2 00/38] Plugin support Emilio G. Cota
                   ` (9 preceding siblings ...)
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 10/38] plugin-gen: add module for TCG-related code Emilio G. Cota
@ 2018-12-09 19:37 ` Emilio G. Cota
  2019-05-20 13:36   ` Alex Bennée
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 12/38] tcg: add MO_HADDR to TCGMemOp Emilio G. Cota
                   ` (27 subsequent siblings)
  38 siblings, 1 reply; 63+ messages in thread
From: Emilio G. Cota @ 2018-12-09 19:37 UTC (permalink / raw)
  To: qemu-devel; +Cc: Alex Bennée, Richard Henderson, Pavel Dovgalyuk

Will gain a user soon.

Signed-off-by: Emilio G. Cota <cota@braap.org>
---
 tcg/tcg-op.h | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/tcg/tcg-op.h b/tcg/tcg-op.h
index e2948b10a2..d3c79a6cb2 100644
--- a/tcg/tcg-op.h
+++ b/tcg/tcg-op.h
@@ -1219,6 +1219,11 @@ static inline void tcg_gen_ld_ptr(TCGv_ptr r, TCGv_ptr a, intptr_t o)
     glue(tcg_gen_ld_,PTR)((NAT)r, a, o);
 }
 
+static inline void tcg_gen_st_ptr(TCGv_ptr r, TCGv_ptr a, intptr_t o)
+{
+    glue(tcg_gen_st_, PTR)((NAT)r, a, o);
+}
+
 static inline void tcg_gen_discard_ptr(TCGv_ptr a)
 {
     glue(tcg_gen_discard_,PTR)((NAT)a);
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [Qemu-devel] [RFC v2 12/38] tcg: add MO_HADDR to TCGMemOp
  2018-12-09 19:37 [Qemu-devel] [RFC v2 00/38] Plugin support Emilio G. Cota
                   ` (10 preceding siblings ...)
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 11/38] tcg: add tcg_gen_st_ptr Emilio G. Cota
@ 2018-12-09 19:37 ` Emilio G. Cota
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 13/38] atomic_template: fix indentation in GEN_ATOMIC_HELPER Emilio G. Cota
                   ` (26 subsequent siblings)
  38 siblings, 0 replies; 63+ messages in thread
From: Emilio G. Cota @ 2018-12-09 19:37 UTC (permalink / raw)
  To: qemu-devel; +Cc: Alex Bennée, Richard Henderson, Pavel Dovgalyuk

We will use this from plugins to mark mem accesses so that
we can later obtain their host address.

Signed-off-by: Emilio G. Cota <cota@braap.org>
---
 tcg/tcg.h | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/tcg/tcg.h b/tcg/tcg.h
index 6fd525023b..a376f83ab6 100644
--- a/tcg/tcg.h
+++ b/tcg/tcg.h
@@ -359,6 +359,13 @@ typedef enum TCGMemOp {
     MO_ALIGN_32 = 5 << MO_ASHIFT,
     MO_ALIGN_64 = 6 << MO_ASHIFT,
 
+    /*
+     * SoftMMU-only: if set, the TCG backend puts the corresponding host address
+     * in CPUArchState.hostaddr.
+     */
+    MO_HSHIFT = MO_ASHIFT + 3,
+    MO_HADDR = 1 << MO_HSHIFT,
+
     /* Combinations of the above, for ease of use.  */
     MO_UB    = MO_8,
     MO_UW    = MO_16,
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [Qemu-devel] [RFC v2 13/38] atomic_template: fix indentation in GEN_ATOMIC_HELPER
  2018-12-09 19:37 [Qemu-devel] [RFC v2 00/38] Plugin support Emilio G. Cota
                   ` (11 preceding siblings ...)
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 12/38] tcg: add MO_HADDR to TCGMemOp Emilio G. Cota
@ 2018-12-09 19:37 ` Emilio G. Cota
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 14/38] atomic_template: add inline trace/plugin helpers Emilio G. Cota
                   ` (25 subsequent siblings)
  38 siblings, 0 replies; 63+ messages in thread
From: Emilio G. Cota @ 2018-12-09 19:37 UTC (permalink / raw)
  To: qemu-devel; +Cc: Alex Bennée, Richard Henderson, Pavel Dovgalyuk

Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
Signed-off-by: Emilio G. Cota <cota@braap.org>
---
 accel/tcg/atomic_template.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/accel/tcg/atomic_template.h b/accel/tcg/atomic_template.h
index efde12fdb2..8d177fefef 100644
--- a/accel/tcg/atomic_template.h
+++ b/accel/tcg/atomic_template.h
@@ -284,7 +284,7 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr,
 
 #define GEN_ATOMIC_HELPER(X)                                        \
 ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr,       \
-                 ABI_TYPE val EXTRA_ARGS)                           \
+                        ABI_TYPE val EXTRA_ARGS)                    \
 {                                                                   \
     ATOMIC_MMU_DECLS;                                               \
     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;                           \
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [Qemu-devel] [RFC v2 14/38] atomic_template: add inline trace/plugin helpers
  2018-12-09 19:37 [Qemu-devel] [RFC v2 00/38] Plugin support Emilio G. Cota
                   ` (12 preceding siblings ...)
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 13/38] atomic_template: fix indentation in GEN_ATOMIC_HELPER Emilio G. Cota
@ 2018-12-09 19:37 ` Emilio G. Cota
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 15/38] tcg: let plugins instrument memory accesses Emilio G. Cota
                   ` (24 subsequent siblings)
  38 siblings, 0 replies; 63+ messages in thread
From: Emilio G. Cota @ 2018-12-09 19:37 UTC (permalink / raw)
  To: qemu-devel; +Cc: Alex Bennée, Richard Henderson, Pavel Dovgalyuk

In preparation for plugin support.

Signed-off-by: Emilio G. Cota <cota@braap.org>
---
 accel/tcg/atomic_template.h | 110 ++++++++++++++++++++++++------------
 1 file changed, 75 insertions(+), 35 deletions(-)

diff --git a/accel/tcg/atomic_template.h b/accel/tcg/atomic_template.h
index 8d177fefef..2f7d5ee02a 100644
--- a/accel/tcg/atomic_template.h
+++ b/accel/tcg/atomic_template.h
@@ -59,25 +59,44 @@
 # define ABI_TYPE  uint32_t
 #endif
 
-#define ATOMIC_TRACE_RMW do {                                           \
-        uint8_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, false); \
-                                                                        \
-        trace_guest_mem_before_exec(ENV_GET_CPU(env), addr, info);      \
-        trace_guest_mem_before_exec(ENV_GET_CPU(env), addr,             \
-                                    info | TRACE_MEM_ST);               \
-    } while (0)
-
-#define ATOMIC_TRACE_LD do {                                            \
-        uint8_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, false); \
-                                                                        \
-        trace_guest_mem_before_exec(ENV_GET_CPU(env), addr, info);      \
-    } while (0)
-
-# define ATOMIC_TRACE_ST do {                                           \
-        uint8_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, true); \
-                                                                        \
-        trace_guest_mem_before_exec(ENV_GET_CPU(env), addr, info);      \
-    } while (0)
+#ifndef ATOMIC_TEMPLATE_COMMON
+#define ATOMIC_TEMPLATE_COMMON
+static inline
+void atomic_trace_rmw_pre(CPUArchState *env, target_ulong addr, uint8_t info)
+{
+    CPUState *cpu = ENV_GET_CPU(env);
+
+    trace_guest_mem_before_exec(cpu, addr, info);
+    trace_guest_mem_before_exec(cpu, addr, info | TRACE_MEM_ST);
+}
+
+static inline void atomic_trace_rmw_post(CPUArchState *env, target_ulong addr,
+                                         void *haddr, uint8_t info)
+{
+}
+
+static inline
+void atomic_trace_ld_pre(CPUArchState *env, target_ulong addr, uint8_t info)
+{
+    trace_guest_mem_before_exec(ENV_GET_CPU(env), addr, info);
+}
+
+static inline void atomic_trace_ld_post(CPUArchState *env, target_ulong addr,
+                                        void *haddr, uint8_t info)
+{
+}
+
+static inline
+void atomic_trace_st_pre(CPUArchState *env, target_ulong addr, uint8_t info)
+{
+    trace_guest_mem_before_exec(ENV_GET_CPU(env), addr, info);
+}
+
+static inline void atomic_trace_st_post(CPUArchState *env, target_ulong addr,
+                                        void *haddr, uint8_t info)
+{
+}
+#endif /* ATOMIC_TEMPLATE_COMMON */
 
 /* Define host-endian atomic operations.  Note that END is used within
    the ATOMIC_NAME macro, and redefined below.  */
@@ -98,14 +117,16 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr,
     ATOMIC_MMU_DECLS;
     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
     DATA_TYPE ret;
+    uint8_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, false);
 
-    ATOMIC_TRACE_RMW;
+    atomic_trace_rmw_pre(env, addr, info);
 #if DATA_SIZE == 16
     ret = atomic16_cmpxchg(haddr, cmpv, newv);
 #else
     ret = atomic_cmpxchg__nocheck(haddr, cmpv, newv);
 #endif
     ATOMIC_MMU_CLEANUP;
+    atomic_trace_rmw_post(env, addr, haddr, info);
     return ret;
 }
 
@@ -115,10 +136,12 @@ ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS)
 {
     ATOMIC_MMU_DECLS;
     DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP;
+    uint8_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, false);
 
-    ATOMIC_TRACE_LD;
+    atomic_trace_ld_pre(env, addr, info);
     val = atomic16_read(haddr);
     ATOMIC_MMU_CLEANUP;
+    atomic_trace_ld_post(env, addr, haddr, info);
     return val;
 }
 
@@ -127,10 +150,12 @@ void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr,
 {
     ATOMIC_MMU_DECLS;
     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
+    uint8_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, true);
 
-    ATOMIC_TRACE_ST;
+    atomic_trace_st_pre(env, addr, info);
     atomic16_set(haddr, val);
     ATOMIC_MMU_CLEANUP;
+    atomic_trace_st_post(env, addr, haddr, info);
 }
 #endif
 #else
@@ -140,10 +165,12 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr,
     ATOMIC_MMU_DECLS;
     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
     DATA_TYPE ret;
+    uint8_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, false);
 
-    ATOMIC_TRACE_RMW;
+    atomic_trace_rmw_pre(env, addr, info);
     ret = atomic_xchg__nocheck(haddr, val);
     ATOMIC_MMU_CLEANUP;
+    atomic_trace_rmw_post(env, addr, haddr, info);
     return ret;
 }
 
@@ -154,10 +181,12 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr,       \
     ATOMIC_MMU_DECLS;                                               \
     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;                           \
     DATA_TYPE ret;                                                  \
+    uint8_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, false); \
                                                                     \
-    ATOMIC_TRACE_RMW;                                               \
+    atomic_trace_rmw_pre(env, addr, info);                          \
     ret = atomic_##X(haddr, val);                                   \
     ATOMIC_MMU_CLEANUP;                                             \
+    atomic_trace_rmw_post(env, addr, haddr, info);                  \
     return ret;                                                     \
 }
 
@@ -186,8 +215,9 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr,       \
     ATOMIC_MMU_DECLS;                                               \
     XDATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;                          \
     XDATA_TYPE cmp, old, new, val = xval;                           \
+    uint8_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, false); \
                                                                     \
-    ATOMIC_TRACE_RMW;                                               \
+    atomic_trace_rmw_pre(env, addr, info);                          \
     smp_mb();                                                       \
     cmp = atomic_read__nocheck(haddr);                              \
     do {                                                            \
@@ -195,6 +225,7 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr,       \
         cmp = atomic_cmpxchg__nocheck(haddr, old, new);             \
     } while (cmp != old);                                           \
     ATOMIC_MMU_CLEANUP;                                             \
+    atomic_trace_rmw_post(env, addr, haddr, info);                  \
     return RET;                                                     \
 }
 
@@ -232,14 +263,16 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr,
     ATOMIC_MMU_DECLS;
     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
     DATA_TYPE ret;
+    uint8_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, false);
 
-    ATOMIC_TRACE_RMW;
+    atomic_trace_rmw_pre(env, addr, info);
 #if DATA_SIZE == 16
     ret = atomic16_cmpxchg(haddr, BSWAP(cmpv), BSWAP(newv));
 #else
     ret = atomic_cmpxchg__nocheck(haddr, BSWAP(cmpv), BSWAP(newv));
 #endif
     ATOMIC_MMU_CLEANUP;
+    atomic_trace_rmw_post(env, addr, haddr, info);
     return BSWAP(ret);
 }
 
@@ -249,10 +282,12 @@ ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS)
 {
     ATOMIC_MMU_DECLS;
     DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP;
+    uint8_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, false);
 
-    ATOMIC_TRACE_LD;
+    atomic_trace_ld_pre(env, addr, info);
     val = atomic16_read(haddr);
     ATOMIC_MMU_CLEANUP;
+    atomic_trace_ld_post(env, addr, haddr, info);
     return BSWAP(val);
 }
 
@@ -261,11 +296,14 @@ void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr,
 {
     ATOMIC_MMU_DECLS;
     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
+    uint8_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, true);
 
-    ATOMIC_TRACE_ST;
+    val = BSWAP(val);
+    atomic_trace_st_pre(env, addr, info);
     val = BSWAP(val);
     atomic16_set(haddr, val);
     ATOMIC_MMU_CLEANUP;
+    atomic_trace_st_post(env, addr, haddr, info);
 }
 #endif
 #else
@@ -275,10 +313,12 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr,
     ATOMIC_MMU_DECLS;
     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
     ABI_TYPE ret;
+    uint8_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, false);
 
-    ATOMIC_TRACE_RMW;
+    atomic_trace_rmw_pre(env, addr, info);
     ret = atomic_xchg__nocheck(haddr, BSWAP(val));
     ATOMIC_MMU_CLEANUP;
+    atomic_trace_rmw_post(env, addr, haddr, info);
     return BSWAP(ret);
 }
 
@@ -289,10 +329,12 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr,       \
     ATOMIC_MMU_DECLS;                                               \
     DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;                           \
     DATA_TYPE ret;                                                  \
+    uint8_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, false); \
                                                                     \
-    ATOMIC_TRACE_RMW;                                               \
+    atomic_trace_rmw_pre(env, addr, info);                          \
     ret = atomic_##X(haddr, BSWAP(val));                            \
     ATOMIC_MMU_CLEANUP;                                             \
+    atomic_trace_rmw_post(env, addr, haddr, info);                  \
     return BSWAP(ret);                                              \
 }
 
@@ -319,8 +361,9 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr,       \
     ATOMIC_MMU_DECLS;                                               \
     XDATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;                          \
     XDATA_TYPE ldo, ldn, old, new, val = xval;                      \
+    uint8_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, false); \
                                                                     \
-    ATOMIC_TRACE_RMW;                                               \
+    atomic_trace_rmw_pre(env, addr, info);                          \
     smp_mb();                                                       \
     ldn = atomic_read__nocheck(haddr);                              \
     do {                                                            \
@@ -328,6 +371,7 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr,       \
         ldn = atomic_cmpxchg__nocheck(haddr, ldo, BSWAP(new));      \
     } while (ldo != ldn);                                           \
     ATOMIC_MMU_CLEANUP;                                             \
+    atomic_trace_rmw_post(env, addr, haddr, info);                  \
     return RET;                                                     \
 }
 
@@ -355,10 +399,6 @@ GEN_ATOMIC_HELPER_FN(add_fetch, ADD, DATA_TYPE, new)
 #undef MEND
 #endif /* DATA_SIZE > 1 */
 
-#undef ATOMIC_TRACE_ST
-#undef ATOMIC_TRACE_LD
-#undef ATOMIC_TRACE_RMW
-
 #undef BSWAP
 #undef ABI_TYPE
 #undef DATA_TYPE
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [Qemu-devel] [RFC v2 15/38] tcg: let plugins instrument memory accesses
  2018-12-09 19:37 [Qemu-devel] [RFC v2 00/38] Plugin support Emilio G. Cota
                   ` (13 preceding siblings ...)
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 14/38] atomic_template: add inline trace/plugin helpers Emilio G. Cota
@ 2018-12-09 19:37 ` Emilio G. Cota
  2019-01-24 14:39   ` Alex Bennée
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 16/38] translate-all: notify plugin code of tb_flush Emilio G. Cota
                   ` (23 subsequent siblings)
  38 siblings, 1 reply; 63+ messages in thread
From: Emilio G. Cota @ 2018-12-09 19:37 UTC (permalink / raw)
  To: qemu-devel; +Cc: Alex Bennée, Richard Henderson, Pavel Dovgalyuk

XXX: store hostaddr from non-i386 TCG backends
XXX: what hostaddr to return for I/O accesses?
XXX: what hostaddr to return for cross-page accesses?

Here the trickiest feature is passing the host address to
memory callbacks that request it. Perhaps it would be more
appropriate to pass a "physical" address to plugins, but since
in QEMU host addr ~= guest physical, I'm going with that for
simplicity.

To keep the implementation simple we piggy-back on the TLB fast path,
and thus can only provide the host address _after_ memory accesses
have occurred. For the slow path, it's a bit tedious because there
are many places to update, but it's fairly simple.

However, note that cross-page accesses are tricky, since the
access might be to non-contiguous host addresses. So I'm punting
on that and just passing NULL.

Signed-off-by: Emilio G. Cota <cota@braap.org>
---
 accel/tcg/atomic_template.h               |  5 +++
 accel/tcg/softmmu_template.h              | 43 +++++++++++++++++-----
 include/exec/cpu-defs.h                   |  9 +++++
 include/exec/cpu_ldst.h                   |  9 +++++
 include/exec/cpu_ldst_template.h          | 43 ++++++++++++++--------
 include/exec/cpu_ldst_useronly_template.h | 42 +++++++++++++++-------
 tcg/tcg.h                                 |  1 +
 accel/tcg/cpu-exec.c                      |  2 ++
 accel/tcg/cputlb.c                        |  9 +++++
 tcg/i386/tcg-target.inc.c                 |  7 ++++
 tcg/tcg-op.c                              | 44 ++++++++++++++++++-----
 11 files changed, 169 insertions(+), 45 deletions(-)

diff --git a/accel/tcg/atomic_template.h b/accel/tcg/atomic_template.h
index 2f7d5ee02a..5619c4b4b9 100644
--- a/accel/tcg/atomic_template.h
+++ b/accel/tcg/atomic_template.h
@@ -18,6 +18,7 @@
  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
  */
 
+#include "qemu/plugin.h"
 #include "trace/mem.h"
 
 #if DATA_SIZE == 16
@@ -73,6 +74,8 @@ void atomic_trace_rmw_pre(CPUArchState *env, target_ulong addr, uint8_t info)
 static inline void atomic_trace_rmw_post(CPUArchState *env, target_ulong addr,
                                          void *haddr, uint8_t info)
 {
+    qemu_plugin_vcpu_mem_cb(ENV_GET_CPU(env), addr, haddr, info);
+    qemu_plugin_vcpu_mem_cb(ENV_GET_CPU(env), addr, haddr, info | TRACE_MEM_ST);
 }
 
 static inline
@@ -84,6 +87,7 @@ void atomic_trace_ld_pre(CPUArchState *env, target_ulong addr, uint8_t info)
 static inline void atomic_trace_ld_post(CPUArchState *env, target_ulong addr,
                                         void *haddr, uint8_t info)
 {
+    qemu_plugin_vcpu_mem_cb(ENV_GET_CPU(env), addr, haddr, info);
 }
 
 static inline
@@ -95,6 +99,7 @@ void atomic_trace_st_pre(CPUArchState *env, target_ulong addr, uint8_t info)
 static inline void atomic_trace_st_post(CPUArchState *env, target_ulong addr,
                                         void *haddr, uint8_t info)
 {
+    qemu_plugin_vcpu_mem_cb(ENV_GET_CPU(env), addr, haddr, info);
 }
 #endif /* ATOMIC_TEMPLATE_COMMON */
 
diff --git a/accel/tcg/softmmu_template.h b/accel/tcg/softmmu_template.h
index b0adea045e..79109e25a1 100644
--- a/accel/tcg/softmmu_template.h
+++ b/accel/tcg/softmmu_template.h
@@ -45,7 +45,6 @@
 #error unsupported data size
 #endif
 
-
 /* For the benefit of TCG generated code, we want to avoid the complication
    of ABI-specific return type promotion and always return a value extended
    to the register size of the host.  This is tcg_target_long, except in the
@@ -99,10 +98,15 @@ static inline DATA_TYPE glue(io_read, SUFFIX)(CPUArchState *env,
                                               size_t mmu_idx, size_t index,
                                               target_ulong addr,
                                               uintptr_t retaddr,
+                                              TCGMemOp mo,
                                               bool recheck,
                                               MMUAccessType access_type)
 {
     CPUIOTLBEntry *iotlbentry = &env->iotlb[mmu_idx][index];
+
+    /* XXX Any sensible choice other than NULL? */
+    set_hostaddr(env, mo, NULL);
+
     return io_readx(env, iotlbentry, mmu_idx, addr, retaddr, recheck,
                     access_type, DATA_SIZE);
 }
@@ -115,7 +119,8 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr,
     uintptr_t index = tlb_index(env, mmu_idx, addr);
     CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr);
     target_ulong tlb_addr = entry->ADDR_READ;
-    unsigned a_bits = get_alignment_bits(get_memop(oi));
+    TCGMemOp mo = get_memop(oi);
+    unsigned a_bits = get_alignment_bits(mo);
     uintptr_t haddr;
     DATA_TYPE res;
 
@@ -141,7 +146,7 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr,
 
         /* ??? Note that the io helpers always read data in the target
            byte ordering.  We should push the LE/BE request down into io.  */
-        res = glue(io_read, SUFFIX)(env, mmu_idx, index, addr, retaddr,
+        res = glue(io_read, SUFFIX)(env, mmu_idx, index, addr, retaddr, mo,
                                     tlb_addr & TLB_RECHECK,
                                     READ_ACCESS_TYPE);
         res = TGT_LE(res);
@@ -162,12 +167,19 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr,
         res2 = helper_le_ld_name(env, addr2, oi, retaddr);
         shift = (addr & (DATA_SIZE - 1)) * 8;
 
+        /*
+         * XXX cross-page accesses would have to be split into separate accesses
+         * for the host address to make sense. For now, just return NULL.
+         */
+        set_hostaddr(env, mo, NULL);
+
         /* Little-endian combine.  */
         res = (res1 >> shift) | (res2 << ((DATA_SIZE * 8) - shift));
         return res;
     }
 
     haddr = addr + entry->addend;
+    set_hostaddr(env, mo, (void *)haddr);
 #if DATA_SIZE == 1
     res = glue(glue(ld, LSUFFIX), _p)((uint8_t *)haddr);
 #else
@@ -184,7 +196,8 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr,
     uintptr_t index = tlb_index(env, mmu_idx, addr);
     CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr);
     target_ulong tlb_addr = entry->ADDR_READ;
-    unsigned a_bits = get_alignment_bits(get_memop(oi));
+    TCGMemOp mo = get_memop(oi);
+    unsigned a_bits = get_alignment_bits(mo);
     uintptr_t haddr;
     DATA_TYPE res;
 
@@ -210,7 +223,7 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr,
 
         /* ??? Note that the io helpers always read data in the target
            byte ordering.  We should push the LE/BE request down into io.  */
-        res = glue(io_read, SUFFIX)(env, mmu_idx, index, addr, retaddr,
+        res = glue(io_read, SUFFIX)(env, mmu_idx, index, addr, retaddr, mo,
                                     tlb_addr & TLB_RECHECK,
                                     READ_ACCESS_TYPE);
         res = TGT_BE(res);
@@ -231,12 +244,15 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr,
         res2 = helper_be_ld_name(env, addr2, oi, retaddr);
         shift = (addr & (DATA_SIZE - 1)) * 8;
 
+        set_hostaddr(env, mo, NULL);
+
         /* Big-endian combine.  */
         res = (res1 << shift) | (res2 >> ((DATA_SIZE * 8) - shift));
         return res;
     }
 
     haddr = addr + entry->addend;
+    set_hostaddr(env, mo, (void *)haddr);
     res = glue(glue(ld, LSUFFIX), _be_p)((uint8_t *)haddr);
     return res;
 }
@@ -267,9 +283,12 @@ static inline void glue(io_write, SUFFIX)(CPUArchState *env,
                                           DATA_TYPE val,
                                           target_ulong addr,
                                           uintptr_t retaddr,
+                                          TCGMemOp mo,
                                           bool recheck)
 {
     CPUIOTLBEntry *iotlbentry = &env->iotlb[mmu_idx][index];
+
+    set_hostaddr(env, mo, NULL);
     return io_writex(env, iotlbentry, mmu_idx, val, addr, retaddr,
                      recheck, DATA_SIZE);
 }
@@ -281,7 +300,8 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
     uintptr_t index = tlb_index(env, mmu_idx, addr);
     CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr);
     target_ulong tlb_addr = tlb_addr_write(entry);
-    unsigned a_bits = get_alignment_bits(get_memop(oi));
+    TCGMemOp mo = get_memop(oi);
+    unsigned a_bits = get_alignment_bits(mo);
     uintptr_t haddr;
 
     if (addr & ((1 << a_bits) - 1)) {
@@ -308,7 +328,7 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
            byte ordering.  We should push the LE/BE request down into io.  */
         val = TGT_LE(val);
         glue(io_write, SUFFIX)(env, mmu_idx, index, val, addr,
-                               retaddr, tlb_addr & TLB_RECHECK);
+                               retaddr, mo, tlb_addr & TLB_RECHECK);
         return;
     }
 
@@ -340,10 +360,12 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
             glue(helper_ret_stb, MMUSUFFIX)(env, addr + i, val8,
                                             oi, retaddr);
         }
+        set_hostaddr(env, mo, NULL);
         return;
     }
 
     haddr = addr + entry->addend;
+    set_hostaddr(env, mo, (void *)haddr);
 #if DATA_SIZE == 1
     glue(glue(st, SUFFIX), _p)((uint8_t *)haddr, val);
 #else
@@ -359,7 +381,8 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
     uintptr_t index = tlb_index(env, mmu_idx, addr);
     CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr);
     target_ulong tlb_addr = tlb_addr_write(entry);
-    unsigned a_bits = get_alignment_bits(get_memop(oi));
+    TCGMemOp mo = get_memop(oi);
+    unsigned a_bits = get_alignment_bits(mo);
     uintptr_t haddr;
 
     if (addr & ((1 << a_bits) - 1)) {
@@ -385,7 +408,7 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
         /* ??? Note that the io helpers always read data in the target
            byte ordering.  We should push the LE/BE request down into io.  */
         val = TGT_BE(val);
-        glue(io_write, SUFFIX)(env, mmu_idx, index, val, addr, retaddr,
+        glue(io_write, SUFFIX)(env, mmu_idx, index, val, addr, retaddr, mo,
                                tlb_addr & TLB_RECHECK);
         return;
     }
@@ -418,10 +441,12 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
             glue(helper_ret_stb, MMUSUFFIX)(env, addr + i, val8,
                                             oi, retaddr);
         }
+        set_hostaddr(env, mo, NULL);
         return;
     }
 
     haddr = addr + entry->addend;
+    set_hostaddr(env, mo, (void *)haddr);
     glue(glue(st, SUFFIX), _be_p)((uint8_t *)haddr, val);
 }
 #endif /* DATA_SIZE > 1 */
diff --git a/include/exec/cpu-defs.h b/include/exec/cpu-defs.h
index 40cd5d4774..f46bc0917d 100644
--- a/include/exec/cpu-defs.h
+++ b/include/exec/cpu-defs.h
@@ -178,6 +178,14 @@ typedef struct CPUTLBDesc {
     CPUIOTLBEntry iotlb[NB_MMU_MODES][CPU_TLB_SIZE];
 #endif /* TCG_TARGET_IMPLEMENTS_DYN_TLB */
 
+#ifdef CONFIG_PLUGIN
+# define CPU_PLUGIN_HOSTADDR                            \
+    /* stores the host address of a guest access */     \
+    void *hostaddr;
+#else
+# define CPU_PLUGIN_HOSTADDR
+#endif
+
 #define CPU_COMMON_TLB                                                  \
     /* The meaning of the MMU modes is defined in the target code. */   \
     /* tlb_lock serializes updates to tlb_table and tlb_v_table */      \
@@ -186,6 +194,7 @@ typedef struct CPUTLBDesc {
     CPUTLBEntry tlb_v_table[NB_MMU_MODES][CPU_VTLB_SIZE];               \
     CPU_IOTLB                                                           \
     CPUIOTLBEntry iotlb_v[NB_MMU_MODES][CPU_VTLB_SIZE];                 \
+    CPU_PLUGIN_HOSTADDR                                                 \
     size_t tlb_flush_count;                                             \
     target_ulong tlb_flush_addr;                                        \
     target_ulong tlb_flush_mask;                                        \
diff --git a/include/exec/cpu_ldst.h b/include/exec/cpu_ldst.h
index 83b2907d86..32dd8dd603 100644
--- a/include/exec/cpu_ldst.h
+++ b/include/exec/cpu_ldst.h
@@ -85,6 +85,15 @@ typedef target_ulong abi_ptr;
 #define TARGET_ABI_FMT_ptr TARGET_ABI_FMT_lx
 #endif
 
+static inline void *read_hostaddr(CPUArchState *env)
+{
+#if defined(CONFIG_SOFTMMU) && defined(CONFIG_PLUGIN)
+    return env->hostaddr;
+#else
+    return NULL;
+#endif
+}
+
 #if defined(CONFIG_USER_ONLY)
 
 extern __thread uintptr_t helper_retaddr;
diff --git a/include/exec/cpu_ldst_template.h b/include/exec/cpu_ldst_template.h
index 0f061d47ef..3493cb13bf 100644
--- a/include/exec/cpu_ldst_template.h
+++ b/include/exec/cpu_ldst_template.h
@@ -28,6 +28,7 @@
 #include "trace-root.h"
 #endif
 
+#include "qemu/plugin.h"
 #include "trace/mem.h"
 
 #if DATA_SIZE == 8
@@ -86,11 +87,11 @@ glue(glue(glue(cpu_ld, USUFFIX), MEMSUFFIX), _ra)(CPUArchState *env,
     target_ulong addr;
     int mmu_idx;
     TCGMemOpIdx oi;
-
+    uintptr_t hostaddr;
 #if !defined(SOFTMMU_CODE_ACCESS)
-    trace_guest_mem_before_exec(
-        ENV_GET_CPU(env), ptr,
-        trace_mem_build_info(SHIFT, false, MO_TE, false));
+    uint8_t meminfo = trace_mem_build_info(SHIFT, false, MO_TE, false);
+
+    trace_guest_mem_before_exec(ENV_GET_CPU(env), ptr, meminfo);
 #endif
 
     addr = ptr;
@@ -101,10 +102,14 @@ glue(glue(glue(cpu_ld, USUFFIX), MEMSUFFIX), _ra)(CPUArchState *env,
         oi = make_memop_idx(SHIFT, mmu_idx);
         res = glue(glue(helper_ret_ld, URETSUFFIX), MMUSUFFIX)(env, addr,
                                                             oi, retaddr);
+        hostaddr = (uintptr_t)read_hostaddr(env);
     } else {
-        uintptr_t hostaddr = addr + entry->addend;
+        hostaddr = addr + entry->addend;
         res = glue(glue(ld, USUFFIX), _p)((uint8_t *)hostaddr);
     }
+#ifndef SOFTMMU_CODE_ACCESS
+    qemu_plugin_vcpu_mem_cb(ENV_GET_CPU(env), ptr, (void *)hostaddr, meminfo);
+#endif
     return res;
 }
 
@@ -125,11 +130,11 @@ glue(glue(glue(cpu_lds, SUFFIX), MEMSUFFIX), _ra)(CPUArchState *env,
     target_ulong addr;
     int mmu_idx;
     TCGMemOpIdx oi;
-
+    uintptr_t hostaddr;
 #if !defined(SOFTMMU_CODE_ACCESS)
-    trace_guest_mem_before_exec(
-        ENV_GET_CPU(env), ptr,
-        trace_mem_build_info(SHIFT, true, MO_TE, false));
+    uint8_t meminfo = trace_mem_build_info(SHIFT, false, MO_TE, false);
+
+    trace_guest_mem_before_exec(ENV_GET_CPU(env), ptr, meminfo);
 #endif
 
     addr = ptr;
@@ -140,10 +145,14 @@ glue(glue(glue(cpu_lds, SUFFIX), MEMSUFFIX), _ra)(CPUArchState *env,
         oi = make_memop_idx(SHIFT, mmu_idx);
         res = (DATA_STYPE)glue(glue(helper_ret_ld, SRETSUFFIX),
                                MMUSUFFIX)(env, addr, oi, retaddr);
+        hostaddr = (uintptr_t)read_hostaddr(env);
     } else {
-        uintptr_t hostaddr = addr + entry->addend;
+        hostaddr = addr + entry->addend;
         res = glue(glue(lds, SUFFIX), _p)((uint8_t *)hostaddr);
     }
+#ifndef SOFTMMU_CODE_ACCESS
+    qemu_plugin_vcpu_mem_cb(ENV_GET_CPU(env), ptr, (void *)hostaddr, meminfo);
+#endif
     return res;
 }
 
@@ -167,11 +176,11 @@ glue(glue(glue(cpu_st, SUFFIX), MEMSUFFIX), _ra)(CPUArchState *env,
     target_ulong addr;
     int mmu_idx;
     TCGMemOpIdx oi;
-
+    uintptr_t hostaddr;
 #if !defined(SOFTMMU_CODE_ACCESS)
-    trace_guest_mem_before_exec(
-        ENV_GET_CPU(env), ptr,
-        trace_mem_build_info(SHIFT, false, MO_TE, true));
+    uint8_t meminfo = trace_mem_build_info(SHIFT, false, MO_TE, true);
+
+    trace_guest_mem_before_exec(ENV_GET_CPU(env), ptr, meminfo);
 #endif
 
     addr = ptr;
@@ -182,10 +191,14 @@ glue(glue(glue(cpu_st, SUFFIX), MEMSUFFIX), _ra)(CPUArchState *env,
         oi = make_memop_idx(SHIFT, mmu_idx);
         glue(glue(helper_ret_st, SUFFIX), MMUSUFFIX)(env, addr, v, oi,
                                                      retaddr);
+        hostaddr = (uintptr_t)read_hostaddr(env);
     } else {
-        uintptr_t hostaddr = addr + entry->addend;
+        hostaddr = addr + entry->addend;
         glue(glue(st, SUFFIX), _p)((uint8_t *)hostaddr, v);
     }
+#ifndef SOFTMMU_CODE_ACCESS
+    qemu_plugin_vcpu_mem_cb(ENV_GET_CPU(env), ptr, (void *)hostaddr, meminfo);
+#endif
 }
 
 static inline void
diff --git a/include/exec/cpu_ldst_useronly_template.h b/include/exec/cpu_ldst_useronly_template.h
index 0fd6019af0..e752e9c00e 100644
--- a/include/exec/cpu_ldst_useronly_template.h
+++ b/include/exec/cpu_ldst_useronly_template.h
@@ -64,12 +64,19 @@
 static inline RES_TYPE
 glue(glue(cpu_ld, USUFFIX), MEMSUFFIX)(CPUArchState *env, abi_ptr ptr)
 {
+    RES_TYPE ret;
+#if !defined(CODE_ACCESS)
+    uint8_t meminfo = trace_mem_build_info(SHIFT, false, MO_TE, false);
+
+    trace_guest_mem_before_exec(ENV_GET_CPU(env), ptr, meminfo);
+#endif
+
+    ret = glue(glue(ld, USUFFIX), _p)(g2h(ptr));
+
 #if !defined(CODE_ACCESS)
-    trace_guest_mem_before_exec(
-        ENV_GET_CPU(env), ptr,
-        trace_mem_build_info(SHIFT, false, MO_TE, false));
+    qemu_plugin_vcpu_mem_cb(ENV_GET_CPU(env), ptr, NULL, meminfo);
 #endif
-    return glue(glue(ld, USUFFIX), _p)(g2h(ptr));
+    return ret;
 }
 
 static inline RES_TYPE
@@ -88,12 +95,19 @@ glue(glue(glue(cpu_ld, USUFFIX), MEMSUFFIX), _ra)(CPUArchState *env,
 static inline int
 glue(glue(cpu_lds, SUFFIX), MEMSUFFIX)(CPUArchState *env, abi_ptr ptr)
 {
+    int ret;
 #if !defined(CODE_ACCESS)
-    trace_guest_mem_before_exec(
-        ENV_GET_CPU(env), ptr,
-        trace_mem_build_info(SHIFT, true, MO_TE, false));
+    uint8_t meminfo = trace_mem_build_info(SHIFT, true, MO_TE, false);
+
+    trace_guest_mem_before_exec(ENV_GET_CPU(env), ptr, meminfo);
 #endif
-    return glue(glue(lds, SUFFIX), _p)(g2h(ptr));
+
+    ret = glue(glue(lds, SUFFIX), _p)(g2h(ptr));
+
+#if !defined(CODE_ACCESS)
+    qemu_plugin_vcpu_mem_cb(ENV_GET_CPU(env), ptr, NULL, meminfo);
+#endif
+    return ret;
 }
 
 static inline int
@@ -109,17 +123,21 @@ glue(glue(glue(cpu_lds, SUFFIX), MEMSUFFIX), _ra)(CPUArchState *env,
 }
 #endif
 
-#ifndef CODE_ACCESS
+#if !defined(CODE_ACCESS)
 static inline void
 glue(glue(cpu_st, SUFFIX), MEMSUFFIX)(CPUArchState *env, abi_ptr ptr,
                                       RES_TYPE v)
 {
 #if !defined(CODE_ACCESS)
-    trace_guest_mem_before_exec(
-        ENV_GET_CPU(env), ptr,
-        trace_mem_build_info(SHIFT, false, MO_TE, true));
+    uint8_t meminfo = trace_mem_build_info(SHIFT, false, MO_TE, true);
+    trace_guest_mem_before_exec(ENV_GET_CPU(env), ptr, meminfo);
 #endif
+
     glue(glue(st, SUFFIX), _p)(g2h(ptr), v);
+
+#if !defined(CODE_ACCESS)
+    qemu_plugin_vcpu_mem_cb(ENV_GET_CPU(env), ptr, NULL, meminfo);
+#endif
 }
 
 static inline void
diff --git a/tcg/tcg.h b/tcg/tcg.h
index a376f83ab6..8938dcf52e 100644
--- a/tcg/tcg.h
+++ b/tcg/tcg.h
@@ -29,6 +29,7 @@
 #include "cpu.h"
 #include "exec/tb-context.h"
 #include "qemu/bitops.h"
+#include "qemu/plugin.h"
 #include "qemu/queue.h"
 #include "tcg-mo.h"
 #include "tcg-target.h"
diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c
index d590f1f6c0..4c8265a908 100644
--- a/accel/tcg/cpu-exec.c
+++ b/accel/tcg/cpu-exec.c
@@ -267,6 +267,7 @@ void cpu_exec_step_atomic(CPUState *cpu)
         tcg_debug_assert(!have_mmap_lock());
 #endif
         assert_no_pages_locked();
+        qemu_plugin_disable_mem_helpers(cpu);
     }
 
     if (in_exclusive_region) {
@@ -716,6 +717,7 @@ int cpu_exec(CPUState *cpu)
         if (qemu_mutex_iothread_locked()) {
             qemu_mutex_unlock_iothread();
         }
+        qemu_plugin_disable_mem_helpers(cpu);
     }
 
     /* if an exception is pending, we execute it here */
diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c
index 5c61908084..3bdf98d2c3 100644
--- a/accel/tcg/cputlb.c
+++ b/accel/tcg/cputlb.c
@@ -1208,6 +1208,15 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr,
     cpu_loop_exit_atomic(ENV_GET_CPU(env), retaddr);
 }
 
+static inline void set_hostaddr(CPUArchState *env, TCGMemOp mo, void *haddr)
+{
+#ifdef CONFIG_PLUGIN
+    if (mo & MO_HADDR) {
+        env->hostaddr = haddr;
+    }
+#endif
+}
+
 #ifdef TARGET_WORDS_BIGENDIAN
 # define TGT_BE(X)  (X)
 # define TGT_LE(X)  BSWAP(X)
diff --git a/tcg/i386/tcg-target.inc.c b/tcg/i386/tcg-target.inc.c
index 5cbb07deab..eb3725f2e3 100644
--- a/tcg/i386/tcg-target.inc.c
+++ b/tcg/i386/tcg-target.inc.c
@@ -1685,6 +1685,13 @@ static inline void tcg_out_tlb_load(TCGContext *s, TCGReg addrlo, TCGReg addrhi,
     /* add addend(r0), r1 */
     tcg_out_modrm_offset(s, OPC_ADD_GvEv + hrexw, r1, r0,
                          offsetof(CPUTLBEntry, addend));
+
+#ifdef CONFIG_PLUGIN
+    if (opc & MO_HADDR) {
+        tcg_out_st(s, TCG_TYPE_PTR, r1, TCG_AREG0,
+                   offsetof(CPUArchState, hostaddr));
+    }
+#endif
 }
 
 /*
diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c
index 7a8015c5a9..b30f0d4440 100644
--- a/tcg/tcg-op.c
+++ b/tcg/tcg-op.c
@@ -31,6 +31,7 @@
 #include "tcg-mo.h"
 #include "trace-tcg.h"
 #include "trace/mem.h"
+#include "exec/plugin-gen.h"
 
 /* Reduce the number of ifdefs below.  This assumes that all uses of
    TCGV_HIGH and TCGV_LOW are properly protected by a conditional that
@@ -2595,6 +2596,7 @@ void tcg_gen_exit_tb(TranslationBlock *tb, unsigned idx)
         tcg_debug_assert(idx == TB_EXIT_REQUESTED);
     }
 
+    plugin_gen_disable_mem_helpers();
     tcg_gen_op1i(INDEX_op_exit_tb, val);
 }
 
@@ -2607,6 +2609,7 @@ void tcg_gen_goto_tb(unsigned idx)
     tcg_debug_assert((tcg_ctx->goto_tb_issue_mask & (1 << idx)) == 0);
     tcg_ctx->goto_tb_issue_mask |= 1 << idx;
 #endif
+    plugin_gen_disable_mem_helpers();
     /* When not chaining, we simply fall through to the "fallback" exit.  */
     if (!qemu_loglevel_mask(CPU_LOG_TB_NOCHAIN)) {
         tcg_gen_op1i(INDEX_op_goto_tb, idx);
@@ -2616,7 +2619,10 @@ void tcg_gen_goto_tb(unsigned idx)
 void tcg_gen_lookup_and_goto_ptr(void)
 {
     if (TCG_TARGET_HAS_goto_ptr && !qemu_loglevel_mask(CPU_LOG_TB_NOCHAIN)) {
-        TCGv_ptr ptr = tcg_temp_new_ptr();
+        TCGv_ptr ptr;
+
+        plugin_gen_disable_mem_helpers();
+        ptr = tcg_temp_new_ptr();
         gen_helper_lookup_tb_ptr(ptr, cpu_env);
         tcg_gen_op1i(INDEX_op_goto_ptr, tcgv_ptr_arg(ptr));
         tcg_temp_free_ptr(ptr);
@@ -2699,26 +2705,42 @@ static void tcg_gen_req_mo(TCGBar type)
     }
 }
 
+static inline void plugin_gen_mem_callbacks(TCGv vaddr, uint8_t info)
+{
+#ifdef CONFIG_PLUGIN
+    if (tcg_ctx->plugin_insn == NULL) {
+        return;
+    }
+    plugin_gen_empty_mem_callback(vaddr, info);
+#endif
+}
+
 void tcg_gen_qemu_ld_i32(TCGv_i32 val, TCGv addr, TCGArg idx, TCGMemOp memop)
 {
+    uint8_t info = trace_mem_get_info(memop, 0);
+
     tcg_gen_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD);
     memop = tcg_canonicalize_memop(memop, 0, 0);
-    trace_guest_mem_before_tcg(tcg_ctx->cpu, cpu_env,
-                               addr, trace_mem_get_info(memop, 0));
+    trace_guest_mem_before_tcg(tcg_ctx->cpu, cpu_env, addr, info);
     gen_ldst_i32(INDEX_op_qemu_ld_i32, val, addr, memop, idx);
+    plugin_gen_mem_callbacks(addr, info);
 }
 
 void tcg_gen_qemu_st_i32(TCGv_i32 val, TCGv addr, TCGArg idx, TCGMemOp memop)
 {
+    uint8_t info = trace_mem_get_info(memop, 1);
+
     tcg_gen_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST);
     memop = tcg_canonicalize_memop(memop, 0, 1);
-    trace_guest_mem_before_tcg(tcg_ctx->cpu, cpu_env,
-                               addr, trace_mem_get_info(memop, 1));
+    trace_guest_mem_before_tcg(tcg_ctx->cpu, cpu_env, addr, info);
     gen_ldst_i32(INDEX_op_qemu_st_i32, val, addr, memop, idx);
+    plugin_gen_mem_callbacks(addr, info);
 }
 
 void tcg_gen_qemu_ld_i64(TCGv_i64 val, TCGv addr, TCGArg idx, TCGMemOp memop)
 {
+    uint8_t info;
+
     tcg_gen_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD);
     if (TCG_TARGET_REG_BITS == 32 && (memop & MO_SIZE) < MO_64) {
         tcg_gen_qemu_ld_i32(TCGV_LOW(val), addr, idx, memop);
@@ -2731,13 +2753,16 @@ void tcg_gen_qemu_ld_i64(TCGv_i64 val, TCGv addr, TCGArg idx, TCGMemOp memop)
     }
 
     memop = tcg_canonicalize_memop(memop, 1, 0);
-    trace_guest_mem_before_tcg(tcg_ctx->cpu, cpu_env,
-                               addr, trace_mem_get_info(memop, 0));
+    info = trace_mem_get_info(memop, 0);
+    trace_guest_mem_before_tcg(tcg_ctx->cpu, cpu_env, addr, info);
     gen_ldst_i64(INDEX_op_qemu_ld_i64, val, addr, memop, idx);
+    plugin_gen_mem_callbacks(addr, info);
 }
 
 void tcg_gen_qemu_st_i64(TCGv_i64 val, TCGv addr, TCGArg idx, TCGMemOp memop)
 {
+    uint8_t info;
+
     tcg_gen_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST);
     if (TCG_TARGET_REG_BITS == 32 && (memop & MO_SIZE) < MO_64) {
         tcg_gen_qemu_st_i32(TCGV_LOW(val), addr, idx, memop);
@@ -2745,9 +2770,10 @@ void tcg_gen_qemu_st_i64(TCGv_i64 val, TCGv addr, TCGArg idx, TCGMemOp memop)
     }
 
     memop = tcg_canonicalize_memop(memop, 1, 1);
-    trace_guest_mem_before_tcg(tcg_ctx->cpu, cpu_env,
-                               addr, trace_mem_get_info(memop, 1));
+    info = trace_mem_get_info(memop, 1);
+    trace_guest_mem_before_tcg(tcg_ctx->cpu, cpu_env, addr, info);
     gen_ldst_i64(INDEX_op_qemu_st_i64, val, addr, memop, idx);
+    plugin_gen_mem_callbacks(addr, info);
 }
 
 static void tcg_gen_ext_i32(TCGv_i32 ret, TCGv_i32 val, TCGMemOp opc)
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [Qemu-devel] [RFC v2 16/38] translate-all: notify plugin code of tb_flush
  2018-12-09 19:37 [Qemu-devel] [RFC v2 00/38] Plugin support Emilio G. Cota
                   ` (14 preceding siblings ...)
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 15/38] tcg: let plugins instrument memory accesses Emilio G. Cota
@ 2018-12-09 19:37 ` Emilio G. Cota
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 17/38] *-user: notify plugin of exit Emilio G. Cota
                   ` (22 subsequent siblings)
  38 siblings, 0 replies; 63+ messages in thread
From: Emilio G. Cota @ 2018-12-09 19:37 UTC (permalink / raw)
  To: qemu-devel; +Cc: Alex Bennée, Richard Henderson, Pavel Dovgalyuk

Plugins might allocate per-TB data that then they get passed each
time a TB is executed (via the *userdata pointer).

Notify plugin code every time a code cache flush occurs, so
that plugins can then reclaim the memory of the per-TB data.

Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
Signed-off-by: Emilio G. Cota <cota@braap.org>
---
 accel/tcg/translate-all.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c
index 62d5e13185..aaa8193ceb 100644
--- a/accel/tcg/translate-all.c
+++ b/accel/tcg/translate-all.c
@@ -1232,6 +1232,8 @@ static gboolean tb_host_size_iter(gpointer key, gpointer value, gpointer data)
 /* flush all the translation blocks */
 static void do_tb_flush(CPUState *cpu, run_on_cpu_data tb_flush_count)
 {
+    bool did_flush = false;
+
     mmap_lock();
     /* If it is already been done on request of another CPU,
      * just retry.
@@ -1239,6 +1241,7 @@ static void do_tb_flush(CPUState *cpu, run_on_cpu_data tb_flush_count)
     if (tb_ctx.tb_flush_count != tb_flush_count.host_int) {
         goto done;
     }
+    did_flush = true;
 
     if (DEBUG_TB_FLUSH_GATE) {
         size_t nb_tbs = tcg_nb_tbs();
@@ -1263,6 +1266,9 @@ static void do_tb_flush(CPUState *cpu, run_on_cpu_data tb_flush_count)
 
 done:
     mmap_unlock();
+    if (did_flush) {
+        qemu_plugin_flush_cb();
+    }
 }
 
 void tb_flush(CPUState *cpu)
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [Qemu-devel] [RFC v2 17/38] *-user: notify plugin of exit
  2018-12-09 19:37 [Qemu-devel] [RFC v2 00/38] Plugin support Emilio G. Cota
                   ` (15 preceding siblings ...)
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 16/38] translate-all: notify plugin code of tb_flush Emilio G. Cota
@ 2018-12-09 19:37 ` Emilio G. Cota
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 18/38] *-user: plugin syscalls Emilio G. Cota
                   ` (21 subsequent siblings)
  38 siblings, 0 replies; 63+ messages in thread
From: Emilio G. Cota @ 2018-12-09 19:37 UTC (permalink / raw)
  To: qemu-devel; +Cc: Alex Bennée, Richard Henderson, Pavel Dovgalyuk

Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
Signed-off-by: Emilio G. Cota <cota@braap.org>
---
 bsd-user/syscall.c | 3 +++
 linux-user/exit.c  | 1 +
 2 files changed, 4 insertions(+)

diff --git a/bsd-user/syscall.c b/bsd-user/syscall.c
index 66492aaf5d..b7818af450 100644
--- a/bsd-user/syscall.c
+++ b/bsd-user/syscall.c
@@ -332,6 +332,7 @@ abi_long do_freebsd_syscall(void *cpu_env, int num, abi_long arg1,
         _mcleanup();
 #endif
         gdb_exit(cpu_env, arg1);
+        qemu_plugin_atexit_cb();
         /* XXX: should free thread stack and CPU env */
         _exit(arg1);
         ret = 0; /* avoid warning */
@@ -430,6 +431,7 @@ abi_long do_netbsd_syscall(void *cpu_env, int num, abi_long arg1,
         _mcleanup();
 #endif
         gdb_exit(cpu_env, arg1);
+        qemu_plugin_atexit_cb();
         /* XXX: should free thread stack and CPU env */
         _exit(arg1);
         ret = 0; /* avoid warning */
@@ -505,6 +507,7 @@ abi_long do_openbsd_syscall(void *cpu_env, int num, abi_long arg1,
         _mcleanup();
 #endif
         gdb_exit(cpu_env, arg1);
+        qemu_plugin_atexit_cb();
         /* XXX: should free thread stack and CPU env */
         _exit(arg1);
         ret = 0; /* avoid warning */
diff --git a/linux-user/exit.c b/linux-user/exit.c
index 14e94e28fa..768856483a 100644
--- a/linux-user/exit.c
+++ b/linux-user/exit.c
@@ -32,4 +32,5 @@ void preexit_cleanup(CPUArchState *env, int code)
         __gcov_dump();
 #endif
         gdb_exit(env, code);
+        qemu_plugin_atexit_cb();
 }
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [Qemu-devel] [RFC v2 18/38] *-user: plugin syscalls
  2018-12-09 19:37 [Qemu-devel] [RFC v2 00/38] Plugin support Emilio G. Cota
                   ` (16 preceding siblings ...)
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 17/38] *-user: notify plugin of exit Emilio G. Cota
@ 2018-12-09 19:37 ` Emilio G. Cota
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 19/38] cpu: hook plugin vcpu events Emilio G. Cota
                   ` (20 subsequent siblings)
  38 siblings, 0 replies; 63+ messages in thread
From: Emilio G. Cota @ 2018-12-09 19:37 UTC (permalink / raw)
  To: qemu-devel; +Cc: Alex Bennée, Richard Henderson, Pavel Dovgalyuk

Signed-off-by: Emilio G. Cota <cota@braap.org>
---
 bsd-user/syscall.c   | 9 +++++++++
 linux-user/syscall.c | 3 +++
 2 files changed, 12 insertions(+)

diff --git a/bsd-user/syscall.c b/bsd-user/syscall.c
index b7818af450..4993f81b2b 100644
--- a/bsd-user/syscall.c
+++ b/bsd-user/syscall.c
@@ -323,6 +323,8 @@ abi_long do_freebsd_syscall(void *cpu_env, int num, abi_long arg1,
     gemu_log("freebsd syscall %d\n", num);
 #endif
     trace_guest_user_syscall(cpu, num, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8);
+    qemu_plugin_vcpu_syscall(cpu, num, arg1, arg2, arg3, arg4, arg5, arg6, arg7,
+                             arg8);
     if(do_strace)
         print_freebsd_syscall(num, arg1, arg2, arg3, arg4, arg5, arg6);
 
@@ -404,6 +406,7 @@ abi_long do_freebsd_syscall(void *cpu_env, int num, abi_long arg1,
     if (do_strace)
         print_freebsd_syscall_ret(num, ret);
     trace_guest_user_syscall_ret(cpu, num, ret);
+    qemu_plugin_vcpu_syscall_ret(cpu, num, ret);
     return ret;
  efault:
     ret = -TARGET_EFAULT;
@@ -422,6 +425,8 @@ abi_long do_netbsd_syscall(void *cpu_env, int num, abi_long arg1,
     gemu_log("netbsd syscall %d\n", num);
 #endif
     trace_guest_user_syscall(cpu, num, arg1, arg2, arg3, arg4, arg5, arg6, 0, 0);
+    qemu_plugin_vcpu_syscall(cpu, num, arg1, arg2, arg3, arg4, arg5, arg6, 0,
+                             0);
     if(do_strace)
         print_netbsd_syscall(num, arg1, arg2, arg3, arg4, arg5, arg6);
 
@@ -480,6 +485,7 @@ abi_long do_netbsd_syscall(void *cpu_env, int num, abi_long arg1,
     if (do_strace)
         print_netbsd_syscall_ret(num, ret);
     trace_guest_user_syscall_ret(cpu, num, ret);
+    qemu_plugin_vcpu_syscall_ret(cpu, num, ret);
     return ret;
  efault:
     ret = -TARGET_EFAULT;
@@ -498,6 +504,8 @@ abi_long do_openbsd_syscall(void *cpu_env, int num, abi_long arg1,
     gemu_log("openbsd syscall %d\n", num);
 #endif
     trace_guest_user_syscall(cpu, num, arg1, arg2, arg3, arg4, arg5, arg6, 0, 0);
+    qemu_plugin_vcpu_syscall(cpu, num, arg1, arg2, arg3, arg4, arg5, arg6, 0,
+                             0);
     if(do_strace)
         print_openbsd_syscall(num, arg1, arg2, arg3, arg4, arg5, arg6);
 
@@ -556,6 +564,7 @@ abi_long do_openbsd_syscall(void *cpu_env, int num, abi_long arg1,
     if (do_strace)
         print_openbsd_syscall_ret(num, ret);
     trace_guest_user_syscall_ret(cpu, num, ret);
+    qemu_plugin_vcpu_syscall_ret(cpu, num, ret);
     return ret;
  efault:
     ret = -TARGET_EFAULT;
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index 15b03e17b9..9f6457768c 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -11422,6 +11422,8 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
 
     trace_guest_user_syscall(cpu, num, arg1, arg2, arg3, arg4,
                              arg5, arg6, arg7, arg8);
+    qemu_plugin_vcpu_syscall(cpu, num, arg1, arg2, arg3, arg4, arg5, arg6, arg7,
+                             arg8);
 
     if (unlikely(do_strace)) {
         print_syscall(num, arg1, arg2, arg3, arg4, arg5, arg6);
@@ -11434,5 +11436,6 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
     }
 
     trace_guest_user_syscall_ret(cpu, num, ret);
+    qemu_plugin_vcpu_syscall_ret(cpu, num, ret);
     return ret;
 }
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [Qemu-devel] [RFC v2 19/38] cpu: hook plugin vcpu events
  2018-12-09 19:37 [Qemu-devel] [RFC v2 00/38] Plugin support Emilio G. Cota
                   ` (17 preceding siblings ...)
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 18/38] *-user: plugin syscalls Emilio G. Cota
@ 2018-12-09 19:37 ` Emilio G. Cota
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 20/38] plugin-gen: add plugin_insn_append Emilio G. Cota
                   ` (19 subsequent siblings)
  38 siblings, 0 replies; 63+ messages in thread
From: Emilio G. Cota @ 2018-12-09 19:37 UTC (permalink / raw)
  To: qemu-devel; +Cc: Alex Bennée, Richard Henderson, Pavel Dovgalyuk

Signed-off-by: Emilio G. Cota <cota@braap.org>
---
 cpus.c    | 10 ++++++++++
 exec.c    |  2 ++
 qom/cpu.c |  2 ++
 3 files changed, 14 insertions(+)

diff --git a/cpus.c b/cpus.c
index c9acef73e4..e3844c69c8 100644
--- a/cpus.c
+++ b/cpus.c
@@ -43,6 +43,7 @@
 #include "exec/exec-all.h"
 
 #include "qemu/thread.h"
+#include "qemu/plugin.h"
 #include "sysemu/cpus.h"
 #include "sysemu/qtest.h"
 #include "qemu/main-loop.h"
@@ -1322,12 +1323,21 @@ static void qemu_tcg_rr_wait_io_event(CPUState *cpu)
 
 static void qemu_wait_io_event(CPUState *cpu)
 {
+    bool slept = false;
+
     g_assert(cpu_mutex_locked(cpu));
     g_assert(!qemu_mutex_iothread_locked());
 
     while (cpu_thread_is_idle(cpu)) {
+        if (!slept) {
+            slept = true;
+            qemu_plugin_vcpu_idle_cb(cpu);
+        }
         qemu_cond_wait(&cpu->halt_cond, &cpu->lock);
     }
+    if (slept) {
+        qemu_plugin_vcpu_resume_cb(cpu);
+    }
 
 #ifdef _WIN32
     /* Eat dummy APC queued by qemu_cpu_kick_thread.  */
diff --git a/exec.c b/exec.c
index 04d505500b..bdafb0e71a 100644
--- a/exec.c
+++ b/exec.c
@@ -967,6 +967,8 @@ void cpu_exec_realizefn(CPUState *cpu, Error **errp)
     }
     tlb_init(cpu);
 
+    qemu_plugin_vcpu_init_hook(cpu);
+
 #ifndef CONFIG_USER_ONLY
     if (qdev_get_vmsd(DEVICE(cpu)) == NULL) {
         vmstate_register(NULL, cpu->cpu_index, &vmstate_cpu_common, cpu);
diff --git a/qom/cpu.c b/qom/cpu.c
index b33d182c4c..6233a98a84 100644
--- a/qom/cpu.c
+++ b/qom/cpu.c
@@ -32,6 +32,7 @@
 #include "hw/boards.h"
 #include "hw/qdev-properties.h"
 #include "trace-root.h"
+#include "qemu/plugin.h"
 
 CPUInterruptHandler cpu_interrupt_handler;
 
@@ -352,6 +353,7 @@ static void cpu_common_unrealizefn(DeviceState *dev, Error **errp)
     CPUState *cpu = CPU(dev);
     /* NOTE: latest generic point before the cpu is fully unrealized */
     trace_fini_vcpu(cpu);
+    qemu_plugin_vcpu_exit_hook(cpu);
     cpu_exec_unrealizefn(cpu);
 }
 
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [Qemu-devel] [RFC v2 20/38] plugin-gen: add plugin_insn_append
  2018-12-09 19:37 [Qemu-devel] [RFC v2 00/38] Plugin support Emilio G. Cota
                   ` (18 preceding siblings ...)
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 19/38] cpu: hook plugin vcpu events Emilio G. Cota
@ 2018-12-09 19:37 ` Emilio G. Cota
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 21/38] translator: add translator_ld{ub, sw, uw, l, q} Emilio G. Cota
                   ` (18 subsequent siblings)
  38 siblings, 0 replies; 63+ messages in thread
From: Emilio G. Cota @ 2018-12-09 19:37 UTC (permalink / raw)
  To: qemu-devel; +Cc: Alex Bennée, Richard Henderson, Pavel Dovgalyuk

By adding it to plugin-gen's header file, we can export is as
an inline, since tcg.h is included in the header (we need tcg_ctx).

Signed-off-by: Emilio G. Cota <cota@braap.org>
---
 include/exec/plugin-gen.h | 27 ++++++++++++++++++---------
 accel/tcg/plugin-gen.c    | 10 +++++++++-
 2 files changed, 27 insertions(+), 10 deletions(-)

diff --git a/include/exec/plugin-gen.h b/include/exec/plugin-gen.h
index 449ea16034..b09c16b720 100644
--- a/include/exec/plugin-gen.h
+++ b/include/exec/plugin-gen.h
@@ -15,15 +15,6 @@
 #include "qemu/plugin.h"
 #include "tcg/tcg.h"
 
-/* used by plugin_callback_start and plugin_callback_end TCG ops */
-enum plugin_gen_from {
-    PLUGIN_GEN_FROM_TB,
-    PLUGIN_GEN_FROM_INSN,
-    PLUGIN_GEN_FROM_MEM,
-    PLUGIN_GEN_AFTER_INSN,
-    PLUGIN_GEN_N_FROMS,
-};
-
 struct DisasContextBase;
 
 #ifdef CONFIG_PLUGIN
@@ -36,6 +27,21 @@ void plugin_gen_insn_end(void);
 void plugin_gen_disable_mem_helpers(void);
 void plugin_gen_empty_mem_callback(TCGv addr, uint8_t info);
 
+static inline void plugin_insn_append(const void *from, size_t size)
+{
+    struct qemu_plugin_insn *insn = tcg_ctx->plugin_insn;
+
+    if (insn == NULL) {
+        return;
+    }
+    if (unlikely(insn->size + size > insn->capacity)) {
+        insn->data = g_realloc(insn->data, insn->size + size);
+        insn->capacity = insn->size + size;
+    }
+    memcpy(insn->data + insn->size, from, size);
+    insn->size += size;
+}
+
 #else /* !CONFIG_PLUGIN */
 
 static inline
@@ -60,6 +66,9 @@ static inline void plugin_gen_disable_mem_helpers(void)
 static inline void plugin_gen_empty_mem_callback(TCGv addr, uint8_t info)
 { }
 
+static inline void plugin_insn_append(const void *from, size_t size)
+{ }
+
 #endif /* CONFIG_PLUGIN */
 
 #endif /* QEMU_PLUGIN_GEN_H */
diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c
index 06ec23e9f5..e6dd79e4d8 100644
--- a/accel/tcg/plugin-gen.c
+++ b/accel/tcg/plugin-gen.c
@@ -60,9 +60,17 @@
 /*
  * plugin_cb_start TCG op args[]:
  * 0: enum plugin_gen_from
- * 1: enum plugin_gen_cb (defined below)
+ * 1: enum plugin_gen_cb
  * 2: set to 1 if it's a mem callback and it's a write, 0 otherwise.
  */
+enum plugin_gen_from {
+    PLUGIN_GEN_FROM_TB,
+    PLUGIN_GEN_FROM_INSN,
+    PLUGIN_GEN_FROM_MEM,
+    PLUGIN_GEN_AFTER_INSN,
+    PLUGIN_GEN_N_FROMS,
+};
+
 enum plugin_gen_cb {
     PLUGIN_GEN_CB_UDATA,
     PLUGIN_GEN_CB_INLINE,
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [Qemu-devel] [RFC v2 21/38] translator: add translator_ld{ub, sw, uw, l, q}
  2018-12-09 19:37 [Qemu-devel] [RFC v2 00/38] Plugin support Emilio G. Cota
                   ` (19 preceding siblings ...)
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 20/38] plugin-gen: add plugin_insn_append Emilio G. Cota
@ 2018-12-09 19:37 ` Emilio G. Cota
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 22/38] target/arm: call qemu_plugin_insn_append Emilio G. Cota
                   ` (17 subsequent siblings)
  38 siblings, 0 replies; 63+ messages in thread
From: Emilio G. Cota @ 2018-12-09 19:37 UTC (permalink / raw)
  To: qemu-devel; +Cc: Alex Bennée, Richard Henderson, Pavel Dovgalyuk

Suggested-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Emilio G. Cota <cota@braap.org>
---
 include/exec/translator.h | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)

diff --git a/include/exec/translator.h b/include/exec/translator.h
index 71e7b2c347..39f6f514a7 100644
--- a/include/exec/translator.h
+++ b/include/exec/translator.h
@@ -19,7 +19,10 @@
  */
 
 
+#include "qemu/bswap.h"
 #include "exec/exec-all.h"
+#include "exec/cpu_ldst.h"
+#include "exec/plugin-gen.h"
 #include "tcg/tcg.h"
 
 
@@ -141,4 +144,29 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
 
 void translator_loop_temp_check(DisasContextBase *db);
 
+#define GEN_TRANSLATOR_LD(fullname, name, type, swap_fn)                \
+    static inline type                                                  \
+    fullname ## _swap(CPUArchState *env, abi_ptr pc, bool do_swap)      \
+    {                                                                   \
+        type ret = cpu_ ## name ## _code(env, pc);                      \
+                                                                        \
+        if (do_swap) {                                                  \
+            ret = swap_fn(ret);                                         \
+        }                                                               \
+        plugin_insn_append(&ret, sizeof(ret));                          \
+        return ret;                                                     \
+    }                                                                   \
+                                                                        \
+    static inline type fullname(CPUArchState *env, abi_ptr pc)          \
+    {                                                                   \
+        return fullname ## _swap(env, pc, false);                       \
+    }
+
+GEN_TRANSLATOR_LD(translator_ldub, ldub, uint8_t, /* no swap needed */)
+GEN_TRANSLATOR_LD(translator_ldsw, ldsw, int16_t, bswap16)
+GEN_TRANSLATOR_LD(translator_lduw, lduw, uint16_t, bswap16)
+GEN_TRANSLATOR_LD(translator_ldl, ldl, uint32_t, bswap32)
+GEN_TRANSLATOR_LD(translator_ldq, ldq, uint64_t, bswap64)
+#undef GEN_TRANSLATOR_LD
+
 #endif  /* EXEC__TRANSLATOR_H */
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [Qemu-devel] [RFC v2 22/38] target/arm: call qemu_plugin_insn_append
  2018-12-09 19:37 [Qemu-devel] [RFC v2 00/38] Plugin support Emilio G. Cota
                   ` (20 preceding siblings ...)
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 21/38] translator: add translator_ld{ub, sw, uw, l, q} Emilio G. Cota
@ 2018-12-09 19:37 ` Emilio G. Cota
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 23/38] target/ppc: fetch code with translator_ld Emilio G. Cota
                   ` (16 subsequent siblings)
  38 siblings, 0 replies; 63+ messages in thread
From: Emilio G. Cota @ 2018-12-09 19:37 UTC (permalink / raw)
  To: qemu-devel; +Cc: Alex Bennée, Richard Henderson, Pavel Dovgalyuk

I considered using translator_ld* from arm_ldl_code
et al. However, note that there's a helper that also calls
arm_ldl_code, so we'd have to change that caller.

In thumb's case I'm also calling plugin_insn_append directly,
since we can't assume that all instructions are 16 bits long.

Signed-off-by: Emilio G. Cota <cota@braap.org>
---
 target/arm/translate-a64.c | 2 ++
 target/arm/translate.c     | 8 +++++++-
 2 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c
index 88195ab949..db95161c16 100644
--- a/target/arm/translate-a64.c
+++ b/target/arm/translate-a64.c
@@ -38,6 +38,7 @@
 #include "trace-tcg.h"
 #include "translate-a64.h"
 #include "qemu/atomic128.h"
+#include "qemu/plugin.h"
 
 static TCGv_i64 cpu_X[32];
 static TCGv_i64 cpu_pc;
@@ -13321,6 +13322,7 @@ static void disas_a64_insn(CPUARMState *env, DisasContext *s)
     uint32_t insn;
 
     insn = arm_ldl_code(env, s->pc, s->sctlr_b);
+    plugin_insn_append(&insn, sizeof(insn));
     s->insn = insn;
     s->pc += 4;
 
diff --git a/target/arm/translate.c b/target/arm/translate.c
index 7c4675ffd8..d5171f54f6 100644
--- a/target/arm/translate.c
+++ b/target/arm/translate.c
@@ -13234,6 +13234,7 @@ static void arm_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu)
     }
 
     insn = arm_ldl_code(env, dc->pc, dc->sctlr_b);
+    plugin_insn_append(&insn, sizeof(insn));
     dc->insn = insn;
     dc->pc += 4;
     disas_arm_insn(dc, insn);
@@ -13304,11 +13305,16 @@ static void thumb_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu)
     insn = arm_lduw_code(env, dc->pc, dc->sctlr_b);
     is_16bit = thumb_insn_is_16bit(dc, insn);
     dc->pc += 2;
-    if (!is_16bit) {
+    if (is_16bit) {
+        uint16_t insn16 = insn;
+
+        plugin_insn_append(&insn16, sizeof(insn16));
+    } else {
         uint32_t insn2 = arm_lduw_code(env, dc->pc, dc->sctlr_b);
 
         insn = insn << 16 | insn2;
         dc->pc += 2;
+        plugin_insn_append(&insn, sizeof(insn));
     }
     dc->insn = insn;
 
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [Qemu-devel] [RFC v2 23/38] target/ppc: fetch code with translator_ld
  2018-12-09 19:37 [Qemu-devel] [RFC v2 00/38] Plugin support Emilio G. Cota
                   ` (21 preceding siblings ...)
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 22/38] target/arm: call qemu_plugin_insn_append Emilio G. Cota
@ 2018-12-09 19:37 ` Emilio G. Cota
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 24/38] target/sh4: fetch code with translator_ld (WIP) Emilio G. Cota
                   ` (15 subsequent siblings)
  38 siblings, 0 replies; 63+ messages in thread
From: Emilio G. Cota @ 2018-12-09 19:37 UTC (permalink / raw)
  To: qemu-devel; +Cc: Alex Bennée, Richard Henderson, Pavel Dovgalyuk

Signed-off-by: Emilio G. Cota <cota@braap.org>
---
 target/ppc/translate.c | 8 +++-----
 1 file changed, 3 insertions(+), 5 deletions(-)

diff --git a/target/ppc/translate.c b/target/ppc/translate.c
index 2d31b5f7a1..7a7c8a9a88 100644
--- a/target/ppc/translate.c
+++ b/target/ppc/translate.c
@@ -7555,11 +7555,9 @@ static void ppc_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs)
     LOG_DISAS("nip=" TARGET_FMT_lx " super=%d ir=%d\n",
               ctx->base.pc_next, ctx->mem_idx, (int)msr_ir);
 
-    if (unlikely(need_byteswap(ctx))) {
-        ctx->opcode = bswap32(cpu_ldl_code(env, ctx->base.pc_next));
-    } else {
-        ctx->opcode = cpu_ldl_code(env, ctx->base.pc_next);
-    }
+    ctx->opcode = translator_ldl_swap(env, ctx->base.pc_next,
+                                      need_byteswap(ctx));
+
     LOG_DISAS("translate opcode %08x (%02x %02x %02x %02x) (%s)\n",
               ctx->opcode, opc1(ctx->opcode), opc2(ctx->opcode),
               opc3(ctx->opcode), opc4(ctx->opcode),
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [Qemu-devel] [RFC v2 24/38] target/sh4: fetch code with translator_ld (WIP)
  2018-12-09 19:37 [Qemu-devel] [RFC v2 00/38] Plugin support Emilio G. Cota
                   ` (22 preceding siblings ...)
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 23/38] target/ppc: fetch code with translator_ld Emilio G. Cota
@ 2018-12-09 19:37 ` Emilio G. Cota
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 25/38] target/i386: fetch code with translator_ld Emilio G. Cota
                   ` (14 subsequent siblings)
  38 siblings, 0 replies; 63+ messages in thread
From: Emilio G. Cota @ 2018-12-09 19:37 UTC (permalink / raw)
  To: qemu-devel; +Cc: Alex Bennée, Richard Henderson, Pavel Dovgalyuk

XXX: cleanly get the gUSA instructions

Signed-off-by: Emilio G. Cota <cota@braap.org>
---
 target/sh4/translate.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/target/sh4/translate.c b/target/sh4/translate.c
index ab254b0e8d..1704ce8dae 100644
--- a/target/sh4/translate.c
+++ b/target/sh4/translate.c
@@ -2331,7 +2331,7 @@ static void sh4_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs)
     }
 #endif
 
-    ctx->opcode = cpu_lduw_code(env, ctx->base.pc_next);
+    ctx->opcode = translator_lduw(env, ctx->base.pc_next);
     decode_opc(ctx);
     ctx->base.pc_next += 2;
 }
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [Qemu-devel] [RFC v2 25/38] target/i386: fetch code with translator_ld
  2018-12-09 19:37 [Qemu-devel] [RFC v2 00/38] Plugin support Emilio G. Cota
                   ` (23 preceding siblings ...)
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 24/38] target/sh4: fetch code with translator_ld (WIP) Emilio G. Cota
@ 2018-12-09 19:37 ` Emilio G. Cota
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 26/38] target/hppa: " Emilio G. Cota
                   ` (13 subsequent siblings)
  38 siblings, 0 replies; 63+ messages in thread
From: Emilio G. Cota @ 2018-12-09 19:37 UTC (permalink / raw)
  To: qemu-devel; +Cc: Alex Bennée, Richard Henderson, Pavel Dovgalyuk

Signed-off-by: Emilio G. Cota <cota@braap.org>
---
 target/i386/translate.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/target/i386/translate.c b/target/i386/translate.c
index 83c1ebe491..6ea784da54 100644
--- a/target/i386/translate.c
+++ b/target/i386/translate.c
@@ -1900,28 +1900,28 @@ static uint64_t advance_pc(CPUX86State *env, DisasContext *s, int num_bytes)
 
 static inline uint8_t x86_ldub_code(CPUX86State *env, DisasContext *s)
 {
-    return cpu_ldub_code(env, advance_pc(env, s, 1));
+    return translator_ldub(env, advance_pc(env, s, 1));
 }
 
 static inline int16_t x86_ldsw_code(CPUX86State *env, DisasContext *s)
 {
-    return cpu_ldsw_code(env, advance_pc(env, s, 2));
+    return translator_ldsw(env, advance_pc(env, s, 2));
 }
 
 static inline uint16_t x86_lduw_code(CPUX86State *env, DisasContext *s)
 {
-    return cpu_lduw_code(env, advance_pc(env, s, 2));
+    return translator_lduw(env, advance_pc(env, s, 2));
 }
 
 static inline uint32_t x86_ldl_code(CPUX86State *env, DisasContext *s)
 {
-    return cpu_ldl_code(env, advance_pc(env, s, 4));
+    return translator_ldl(env, advance_pc(env, s, 4));
 }
 
 #ifdef TARGET_X86_64
 static inline uint64_t x86_ldq_code(CPUX86State *env, DisasContext *s)
 {
-    return cpu_ldq_code(env, advance_pc(env, s, 8));
+    return translator_ldq(env, advance_pc(env, s, 8));
 }
 #endif
 
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [Qemu-devel] [RFC v2 26/38] target/hppa: fetch code with translator_ld
  2018-12-09 19:37 [Qemu-devel] [RFC v2 00/38] Plugin support Emilio G. Cota
                   ` (24 preceding siblings ...)
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 25/38] target/i386: fetch code with translator_ld Emilio G. Cota
@ 2018-12-09 19:37 ` Emilio G. Cota
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 27/38] target/m68k: " Emilio G. Cota
                   ` (12 subsequent siblings)
  38 siblings, 0 replies; 63+ messages in thread
From: Emilio G. Cota @ 2018-12-09 19:37 UTC (permalink / raw)
  To: qemu-devel; +Cc: Alex Bennée, Richard Henderson, Pavel Dovgalyuk

Signed-off-by: Emilio G. Cota <cota@braap.org>
---
 target/hppa/translate.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/target/hppa/translate.c b/target/hppa/translate.c
index df9179e70f..806dbda51f 100644
--- a/target/hppa/translate.c
+++ b/target/hppa/translate.c
@@ -4754,7 +4754,7 @@ static void hppa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs)
     {
         /* Always fetch the insn, even if nullified, so that we check
            the page permissions for execute.  */
-        uint32_t insn = cpu_ldl_code(env, ctx->base.pc_next);
+        uint32_t insn = translator_ldl(env, ctx->base.pc_next);
 
         /* Set up the IA queue for the next insn.
            This will be overwritten by a branch.  */
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [Qemu-devel] [RFC v2 27/38] target/m68k: fetch code with translator_ld
  2018-12-09 19:37 [Qemu-devel] [RFC v2 00/38] Plugin support Emilio G. Cota
                   ` (25 preceding siblings ...)
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 26/38] target/hppa: " Emilio G. Cota
@ 2018-12-09 19:37 ` Emilio G. Cota
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 28/38] target/alpha: " Emilio G. Cota
                   ` (11 subsequent siblings)
  38 siblings, 0 replies; 63+ messages in thread
From: Emilio G. Cota @ 2018-12-09 19:37 UTC (permalink / raw)
  To: qemu-devel; +Cc: Alex Bennée, Richard Henderson, Pavel Dovgalyuk

Signed-off-by: Emilio G. Cota <cota@braap.org>
---
 target/m68k/translate.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/target/m68k/translate.c b/target/m68k/translate.c
index d55e707cf6..71263f8b37 100644
--- a/target/m68k/translate.c
+++ b/target/m68k/translate.c
@@ -373,7 +373,7 @@ static TCGv gen_ldst(DisasContext *s, int opsize, TCGv addr, TCGv val,
 static inline uint16_t read_im16(CPUM68KState *env, DisasContext *s)
 {
     uint16_t im;
-    im = cpu_lduw_code(env, s->pc);
+    im = translator_lduw(env, s->pc);
     s->pc += 2;
     return im;
 }
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [Qemu-devel] [RFC v2 28/38] target/alpha: fetch code with translator_ld
  2018-12-09 19:37 [Qemu-devel] [RFC v2 00/38] Plugin support Emilio G. Cota
                   ` (26 preceding siblings ...)
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 27/38] target/m68k: " Emilio G. Cota
@ 2018-12-09 19:37 ` Emilio G. Cota
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 29/38] target/riscv: " Emilio G. Cota
                   ` (10 subsequent siblings)
  38 siblings, 0 replies; 63+ messages in thread
From: Emilio G. Cota @ 2018-12-09 19:37 UTC (permalink / raw)
  To: qemu-devel; +Cc: Alex Bennée, Richard Henderson, Pavel Dovgalyuk

Signed-off-by: Emilio G. Cota <cota@braap.org>
---
 target/alpha/translate.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/target/alpha/translate.c b/target/alpha/translate.c
index 25cd95931d..f8d194994a 100644
--- a/target/alpha/translate.c
+++ b/target/alpha/translate.c
@@ -2987,7 +2987,7 @@ static void alpha_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu)
 {
     DisasContext *ctx = container_of(dcbase, DisasContext, base);
     CPUAlphaState *env = cpu->env_ptr;
-    uint32_t insn = cpu_ldl_code(env, ctx->base.pc_next);
+    uint32_t insn = translator_ldl(env, ctx->base.pc_next);
 
     ctx->base.pc_next += 4;
     ctx->base.is_jmp = translate_one(ctx, insn);
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [Qemu-devel] [RFC v2 29/38] target/riscv: fetch code with translator_ld
  2018-12-09 19:37 [Qemu-devel] [RFC v2 00/38] Plugin support Emilio G. Cota
                   ` (27 preceding siblings ...)
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 28/38] target/alpha: " Emilio G. Cota
@ 2018-12-09 19:37 ` Emilio G. Cota
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 30/38] target/sparc: " Emilio G. Cota
                   ` (9 subsequent siblings)
  38 siblings, 0 replies; 63+ messages in thread
From: Emilio G. Cota @ 2018-12-09 19:37 UTC (permalink / raw)
  To: qemu-devel; +Cc: Alex Bennée, Richard Henderson, Pavel Dovgalyuk

Signed-off-by: Emilio G. Cota <cota@braap.org>
---
 target/riscv/translate.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/target/riscv/translate.c b/target/riscv/translate.c
index 18d7b6d147..fa96f45a69 100644
--- a/target/riscv/translate.c
+++ b/target/riscv/translate.c
@@ -1848,7 +1848,7 @@ static void riscv_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu)
     DisasContext *ctx = container_of(dcbase, DisasContext, base);
     CPURISCVState *env = cpu->env_ptr;
 
-    ctx->opcode = cpu_ldl_code(env, ctx->base.pc_next);
+    ctx->opcode = translator_ldl(env, ctx->base.pc_next);
     decode_opc(env, ctx);
     ctx->base.pc_next = ctx->pc_succ_insn;
 
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [Qemu-devel] [RFC v2 30/38] target/sparc: fetch code with translator_ld
  2018-12-09 19:37 [Qemu-devel] [RFC v2 00/38] Plugin support Emilio G. Cota
                   ` (28 preceding siblings ...)
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 29/38] target/riscv: " Emilio G. Cota
@ 2018-12-09 19:37 ` Emilio G. Cota
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 31/38] target/xtensa: " Emilio G. Cota
                   ` (8 subsequent siblings)
  38 siblings, 0 replies; 63+ messages in thread
From: Emilio G. Cota @ 2018-12-09 19:37 UTC (permalink / raw)
  To: qemu-devel; +Cc: Alex Bennée, Richard Henderson, Pavel Dovgalyuk

Signed-off-by: Emilio G. Cota <cota@braap.org>
---
 target/sparc/translate.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/target/sparc/translate.c b/target/sparc/translate.c
index 74315cdf09..2c754b6163 100644
--- a/target/sparc/translate.c
+++ b/target/sparc/translate.c
@@ -5900,7 +5900,7 @@ static void sparc_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs)
     CPUSPARCState *env = cs->env_ptr;
     unsigned int insn;
 
-    insn = cpu_ldl_code(env, dc->pc);
+    insn = translator_ldl(env, dc->pc);
     dc->base.pc_next += 4;
     disas_sparc_insn(dc, insn);
 
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [Qemu-devel] [RFC v2 31/38] target/xtensa: fetch code with translator_ld
  2018-12-09 19:37 [Qemu-devel] [RFC v2 00/38] Plugin support Emilio G. Cota
                   ` (29 preceding siblings ...)
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 30/38] target/sparc: " Emilio G. Cota
@ 2018-12-09 19:37 ` Emilio G. Cota
  2019-02-25 14:54   ` Alex Bennée
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 32/38] target/openrisc: " Emilio G. Cota
                   ` (7 subsequent siblings)
  38 siblings, 1 reply; 63+ messages in thread
From: Emilio G. Cota @ 2018-12-09 19:37 UTC (permalink / raw)
  To: qemu-devel; +Cc: Alex Bennée, Richard Henderson, Pavel Dovgalyuk

Signed-off-by: Emilio G. Cota <cota@braap.org>
---
 target/xtensa/translate.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/target/xtensa/translate.c b/target/xtensa/translate.c
index 46e1338448..c140742562 100644
--- a/target/xtensa/translate.c
+++ b/target/xtensa/translate.c
@@ -882,7 +882,7 @@ static inline unsigned xtensa_op0_insn_len(DisasContext *dc, uint8_t op0)
 static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc)
 {
     xtensa_isa isa = dc->config->isa;
-    unsigned char b[MAX_INSN_LENGTH] = {cpu_ldub_code(env, dc->pc)};
+    unsigned char b[MAX_INSN_LENGTH] = {translator_ldub(env, dc->pc)};
     unsigned len = xtensa_op0_insn_len(dc, b[0]);
     xtensa_format fmt;
     int slot, slots;
@@ -914,7 +914,7 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc)
                       dc->pc);
     }
     for (i = 1; i < len; ++i) {
-        b[i] = cpu_ldub_code(env, dc->pc + i);
+        b[i] = translator_ldub(env, dc->pc + i);
     }
     xtensa_insnbuf_from_chars(isa, dc->insnbuf, b, len);
     fmt = xtensa_format_decode(isa, dc->insnbuf);
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [Qemu-devel] [RFC v2 32/38] target/openrisc: fetch code with translator_ld
  2018-12-09 19:37 [Qemu-devel] [RFC v2 00/38] Plugin support Emilio G. Cota
                   ` (30 preceding siblings ...)
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 31/38] target/xtensa: " Emilio G. Cota
@ 2018-12-09 19:37 ` Emilio G. Cota
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 33/38] translator: inject instrumentation from plugins Emilio G. Cota
                   ` (6 subsequent siblings)
  38 siblings, 0 replies; 63+ messages in thread
From: Emilio G. Cota @ 2018-12-09 19:37 UTC (permalink / raw)
  To: qemu-devel; +Cc: Alex Bennée, Richard Henderson, Pavel Dovgalyuk

Signed-off-by: Emilio G. Cota <cota@braap.org>
---
 target/openrisc/translate.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/target/openrisc/translate.c b/target/openrisc/translate.c
index a271cd3903..6b5efc0155 100644
--- a/target/openrisc/translate.c
+++ b/target/openrisc/translate.c
@@ -1305,7 +1305,7 @@ static void openrisc_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs)
 {
     DisasContext *dc = container_of(dcbase, DisasContext, base);
     OpenRISCCPU *cpu = OPENRISC_CPU(cs);
-    uint32_t insn = cpu_ldl_code(&cpu->env, dc->base.pc_next);
+    uint32_t insn = translator_ldl(&cpu->env, dc->base.pc_next);
 
     if (!decode(dc, insn)) {
         gen_illegal_exception(dc);
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [Qemu-devel] [RFC v2 33/38] translator: inject instrumentation from plugins
  2018-12-09 19:37 [Qemu-devel] [RFC v2 00/38] Plugin support Emilio G. Cota
                   ` (31 preceding siblings ...)
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 32/38] target/openrisc: " Emilio G. Cota
@ 2018-12-09 19:37 ` Emilio G. Cota
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 34/38] plugin: add API symbols to qemu-plugins.symbols Emilio G. Cota
                   ` (5 subsequent siblings)
  38 siblings, 0 replies; 63+ messages in thread
From: Emilio G. Cota @ 2018-12-09 19:37 UTC (permalink / raw)
  To: qemu-devel; +Cc: Alex Bennée, Richard Henderson, Pavel Dovgalyuk

Signed-off-by: Emilio G. Cota <cota@braap.org>
---
 accel/tcg/translator.c | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/accel/tcg/translator.c b/accel/tcg/translator.c
index afd0a49ea6..68174a2986 100644
--- a/accel/tcg/translator.c
+++ b/accel/tcg/translator.c
@@ -17,6 +17,7 @@
 #include "exec/gen-icount.h"
 #include "exec/log.h"
 #include "exec/translator.h"
+#include "exec/plugin-gen.h"
 
 /* Pairs with tcg_clear_temp_count.
    To be called by #TranslatorOps.{translate_insn,tb_stop} if
@@ -35,6 +36,7 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
                      CPUState *cpu, TranslationBlock *tb)
 {
     int bp_insn = 0;
+    bool plugin_enabled;
 
     /* Initialize DisasContext */
     db->tb = tb;
@@ -67,11 +69,17 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
     ops->tb_start(db, cpu);
     tcg_debug_assert(db->is_jmp == DISAS_NEXT);  /* no early exit */
 
+    plugin_enabled = plugin_gen_tb_start(cpu, tb);
+
     while (true) {
         db->num_insns++;
         ops->insn_start(db, cpu);
         tcg_debug_assert(db->is_jmp == DISAS_NEXT);  /* no early exit */
 
+        if (plugin_enabled) {
+            plugin_gen_insn_start(cpu, db);
+        }
+
         /* Pass breakpoint hits to target for further processing */
         if (!db->singlestep_enabled
             && unlikely(!QTAILQ_EMPTY(&cpu->breakpoints))) {
@@ -107,6 +115,10 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
             ops->translate_insn(db, cpu);
         }
 
+        if (plugin_enabled) {
+            plugin_gen_insn_end();
+        }
+
         /* Stop translation if translate_insn so indicated.  */
         if (db->is_jmp != DISAS_NEXT) {
             break;
@@ -124,6 +136,10 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
     ops->tb_stop(db, cpu);
     gen_tb_end(db->tb, db->num_insns - bp_insn);
 
+    if (plugin_enabled) {
+        plugin_gen_tb_end(cpu);
+    }
+
     /* The disas_log hook may use these values rather than recompute.  */
     db->tb->size = db->pc_next - db->pc_first;
     db->tb->icount = db->num_insns;
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [Qemu-devel] [RFC v2 34/38] plugin: add API symbols to qemu-plugins.symbols
  2018-12-09 19:37 [Qemu-devel] [RFC v2 00/38] Plugin support Emilio G. Cota
                   ` (32 preceding siblings ...)
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 33/38] translator: inject instrumentation from plugins Emilio G. Cota
@ 2018-12-09 19:37 ` Emilio G. Cota
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 35/38] configure: add --enable-plugins Emilio G. Cota
                   ` (4 subsequent siblings)
  38 siblings, 0 replies; 63+ messages in thread
From: Emilio G. Cota @ 2018-12-09 19:37 UTC (permalink / raw)
  To: qemu-devel; +Cc: Alex Bennée, Richard Henderson, Pavel Dovgalyuk

Signed-off-by: Emilio G. Cota <cota@braap.org>
---
 qemu-plugins.symbols | 34 ++++++++++++++++++++++++++++++++++
 1 file changed, 34 insertions(+)
 create mode 100644 qemu-plugins.symbols

diff --git a/qemu-plugins.symbols b/qemu-plugins.symbols
new file mode 100644
index 0000000000..2a5b18862a
--- /dev/null
+++ b/qemu-plugins.symbols
@@ -0,0 +1,34 @@
+{
+  qemu_plugin_uninstall;
+  qemu_plugin_register_vcpu_init_cb;
+  qemu_plugin_register_vcpu_exit_cb;
+  qemu_plugin_register_vcpu_idle_cb;
+  qemu_plugin_register_vcpu_resume_cb;
+  qemu_plugin_register_vcpu_insn_exec_cb;
+  qemu_plugin_register_vcpu_insn_exec_inline;
+  qemu_plugin_register_vcpu_mem_cb;
+  qemu_plugin_register_vcpu_mem_haddr_cb;
+  qemu_plugin_register_vcpu_mem_inline;
+  qemu_plugin_ram_addr_from_host;
+  qemu_plugin_register_vcpu_tb_trans_cb;
+  qemu_plugin_register_vcpu_tb_exec_cb;
+  qemu_plugin_register_vcpu_tb_exec_inline;
+  qemu_plugin_register_flush_cb;
+  qemu_plugin_register_vcpu_syscall_cb;
+  qemu_plugin_register_vcpu_syscall_ret_cb;
+  qemu_plugin_register_atexit_cb;
+  qemu_plugin_tb_n_insns;
+  qemu_plugin_tb_get_insn;
+  qemu_plugin_tb_vaddr;
+  qemu_plugin_insn_data;
+  qemu_plugin_insn_size;
+  qemu_plugin_insn_vaddr;
+  qemu_plugin_insn_haddr;
+  qemu_plugin_mem_size_shift;
+  qemu_plugin_mem_is_sign_extended;
+  qemu_plugin_mem_is_big_endian;
+  qemu_plugin_mem_is_store;
+  qemu_plugin_vcpu_for_each;
+  qemu_plugin_n_vcpus;
+  qemu_plugin_n_max_vcpus;
+};
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [Qemu-devel] [RFC v2 35/38] configure: add --enable-plugins
  2018-12-09 19:37 [Qemu-devel] [RFC v2 00/38] Plugin support Emilio G. Cota
                   ` (33 preceding siblings ...)
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 34/38] plugin: add API symbols to qemu-plugins.symbols Emilio G. Cota
@ 2018-12-09 19:37 ` Emilio G. Cota
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 36/38] vl: support -plugin option Emilio G. Cota
                   ` (3 subsequent siblings)
  38 siblings, 0 replies; 63+ messages in thread
From: Emilio G. Cota @ 2018-12-09 19:37 UTC (permalink / raw)
  To: qemu-devel; +Cc: Alex Bennée, Richard Henderson, Pavel Dovgalyuk

Add support for both ld (using --dynamic-list) and MacOSX's ld64
(-exported_symbols_list).

Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
Signed-off-by: Emilio G. Cota <cota@braap.org>
---
 configure       | 82 +++++++++++++++++++++++++++++++++++++++++++++++++
 Makefile        |  1 +
 Makefile.target | 18 ++++++++++-
 .gitignore      |  2 ++
 4 files changed, 102 insertions(+), 1 deletion(-)

diff --git a/configure b/configure
index 1c473ce95b..91f9c08ae2 100755
--- a/configure
+++ b/configure
@@ -30,6 +30,7 @@ TMPO="${TMPDIR1}/${TMPB}.o"
 TMPCXX="${TMPDIR1}/${TMPB}.cxx"
 TMPE="${TMPDIR1}/${TMPB}.exe"
 TMPMO="${TMPDIR1}/${TMPB}.mo"
+TMPTXT="${TMPDIR1}/${TMPB}.txt"
 
 rm -f config.log
 
@@ -474,6 +475,7 @@ libxml2=""
 docker="no"
 debug_mutex="no"
 libpmem=""
+plugins="no"
 
 # cross compilers defaults, can be overridden with --cross-cc-ARCH
 cross_cc_aarch64="aarch64-linux-gnu-gcc"
@@ -1444,6 +1446,10 @@ for opt do
   ;;
   --disable-libpmem) libpmem=no
   ;;
+  --enable-plugins) plugins="yes"
+  ;;
+  --disable-plugins) plugins="no"
+  ;;
   *)
       echo "ERROR: unknown option $opt"
       echo "Try '$0 --help' for more information"
@@ -1634,6 +1640,8 @@ Advanced options (experts only):
                            xen pv domain builder
   --enable-debug-stack-usage
                            track the maximum stack usage of stacks created by qemu_alloc_stack
+  --enable-plugins
+                           enable plugins via shared library loading
 
 Optional features, enabled with --enable-FEATURE and
 disabled with --disable-FEATURE, default is enabled if available:
@@ -5143,6 +5151,58 @@ if compile_prog "" "" ; then
   atomic64=yes
 fi
 
+#########################################
+# See if --dynamic-list is supported by the linker
+
+cat > $TMPTXT <<EOF
+{
+  foo;
+};
+EOF
+
+cat > $TMPC <<EOF
+#include <stdio.h>
+void foo(void);
+
+void foo(void)
+{
+  printf("foo\n");
+}
+
+int main(void)
+{
+  foo();
+  return 0;
+}
+EOF
+
+ld_dynamic_list="no"
+if compile_prog "" "-Wl,--dynamic-list=$TMPTXT" ; then
+  ld_dynamic_list="yes"
+fi
+
+#########################################
+# See if -exported_symbols_list is supported by the linker
+
+cat > $TMPTXT <<EOF
+  _foo
+EOF
+
+ld_exported_symbols_list="no"
+if compile_prog "" "-Wl,-exported_symbols_list,$TMPTXT" ; then
+  ld_exported_symbols_list="yes"
+fi
+
+if  test "$plugins" = "yes" &&
+    test "$ld_dynamic_list" = "no" &&
+    test "$ld_exported_symbols_list" = "no" ; then
+  error_exit \
+      "Plugin support requires specifying a set of symbols that " \
+      "are exported to plugins. Unfortunately your linker doesn't " \
+      "support the flag (--dynamic-list or -exported_symbols_list) used " \
+      "for this purpose."
+fi
+
 ########################################
 # See if 16-byte vector operations are supported.
 # Even without a vector unit the compiler may expand these.
@@ -6027,6 +6087,7 @@ echo "VxHS block device $vxhs"
 echo "capstone          $capstone"
 echo "docker            $docker"
 echo "libpmem support   $libpmem"
+echo "plugin support    $plugins"
 
 if test "$sdl_too_old" = "yes"; then
 echo "-> Your SDL version is too old - please upgrade to have SDL support"
@@ -6779,6 +6840,27 @@ if test "$libpmem" = "yes" ; then
   echo "CONFIG_LIBPMEM=y" >> $config_host_mak
 fi
 
+if test "$plugins" = "yes" ; then
+    echo "CONFIG_PLUGIN=y" >> $config_host_mak
+    LIBS="-ldl $LIBS"
+    # Copy the export object list to the build dir
+    if test "$ld_dynamic_list" = "yes" ; then
+	echo "CONFIG_HAS_LD_DYNAMIC_LIST=yes" >> $config_host_mak
+	ld_symbols=qemu-plugins-ld.symbols
+	cp "$source_path/qemu-plugins.symbols" $ld_symbols
+    elif test "$ld_exported_symbols_list" = "yes" ; then
+	echo "CONFIG_HAS_LD_EXPORTED_SYMBOLS_LIST=yes" >> $config_host_mak
+	ld64_symbols=qemu-plugins-ld64.symbols
+	echo "# Automatically generated by configure - do not modify" > $ld64_symbols
+	grep 'qemu_' "$source_path/qemu-plugins.symbols" | sed 's/;//g' | \
+	    sed -E 's/^[[:space:]]*(.*)/_\1/' >> $ld64_symbols
+    else
+	error_exit \
+	    "If \$plugins=yes, either \$ld_dynamic_list or " \
+	    "\$ld_exported_symbols_list should have been set to 'yes'."
+    fi
+fi
+
 if test "$tcg_interpreter" = "yes"; then
   QEMU_INCLUDES="-iquote \$(SRC_PATH)/tcg/tci $QEMU_INCLUDES"
 elif test "$ARCH" = "sparc64" ; then
diff --git a/Makefile b/Makefile
index 9cb3076d84..6fd15b296f 100644
--- a/Makefile
+++ b/Makefile
@@ -778,6 +778,7 @@ distclean: clean
 	rm -f qemu-doc.fn qemu-doc.fns qemu-doc.info qemu-doc.ky qemu-doc.kys
 	rm -f qemu-doc.log qemu-doc.pdf qemu-doc.pg qemu-doc.toc qemu-doc.tp
 	rm -f qemu-doc.vr qemu-doc.txt
+	rm -f qemu-plugins-ld.symbols qemu-plugins-ld64.symbols
 	rm -f config.log
 	rm -f linux-headers/asm
 	rm -f docs/version.texi
diff --git a/Makefile.target b/Makefile.target
index 75637c285c..7dada1f368 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -107,7 +107,23 @@ obj-y += target/$(TARGET_BASE_ARCH)/
 obj-y += disas.o
 obj-$(call notempty,$(TARGET_XML_FILES)) += gdbstub-xml.o
 
-obj-$(CONFIG_PLUGINS) += plugin.o
+ifdef CONFIG_PLUGIN
+obj-y += plugin.o
+# Abuse -libs suffix to only link with --dynamic-list/-exported_symbols_list
+# when the final binary includes the plugin object.
+#
+# Note that simply setting LDFLAGS is not enough: we build binaries that
+# never link plugin.o, and the linker might fail (at least ld64 does)
+# if the symbols in the list are not in the output binary.
+ ifdef CONFIG_HAS_LD_DYNAMIC_LIST
+ plugin.o-libs := -Wl,--dynamic-list=$(BUILD_DIR)/qemu-plugins-ld.symbols
+ else
+  ifdef CONFIG_HAS_LD_EXPORTED_SYMBOLS_LIST
+  plugin.o-libs := \
+	-Wl,-exported_symbols_list,$(BUILD_DIR)/qemu-plugins-ld64.symbols
+  endif
+ endif
+endif
 
 #########################################################
 # Linux user emulator target
diff --git a/.gitignore b/.gitignore
index 64efdfd929..29f6446508 100644
--- a/.gitignore
+++ b/.gitignore
@@ -213,3 +213,5 @@ trace-dtrace-root.dtrace
 trace-ust-all.h
 trace-ust-all.c
 /target/arm/decode-sve.inc.c
+qemu-plugins-ld.symbols
+qemu-plugins-ld64.symbols
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [Qemu-devel] [RFC v2 36/38] vl: support -plugin option
  2018-12-09 19:37 [Qemu-devel] [RFC v2 00/38] Plugin support Emilio G. Cota
                   ` (34 preceding siblings ...)
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 35/38] configure: add --enable-plugins Emilio G. Cota
@ 2018-12-09 19:37 ` Emilio G. Cota
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 37/38] linux-user: " Emilio G. Cota
                   ` (2 subsequent siblings)
  38 siblings, 0 replies; 63+ messages in thread
From: Emilio G. Cota @ 2018-12-09 19:37 UTC (permalink / raw)
  To: qemu-devel
  Cc: Alex Bennée, Richard Henderson, Pavel Dovgalyuk,
	Lluís Vilanova

From: Lluís Vilanova <vilanova@ac.upc.edu>

Signed-off-by: Lluís Vilanova <vilanova@ac.upc.edu>
[ cota: s/instrument/plugin ]
Signed-off-by: Emilio G. Cota <cota@braap.org>
---
 vl.c            | 11 +++++++++++
 qemu-options.hx | 17 +++++++++++++++++
 2 files changed, 28 insertions(+)

diff --git a/vl.c b/vl.c
index 1fcacc5caa..a1d6b76315 100644
--- a/vl.c
+++ b/vl.c
@@ -111,6 +111,7 @@ int main(int argc, char **argv)
 
 #include "trace-root.h"
 #include "trace/control.h"
+#include "qemu/plugin.h"
 #include "qemu/queue.h"
 #include "sysemu/arch_init.h"
 
@@ -2998,6 +2999,7 @@ int main(int argc, char **argv, char **envp)
     } BlockdevOptions_queue;
     QSIMPLEQ_HEAD(, BlockdevOptions_queue) bdo_queue
         = QSIMPLEQ_HEAD_INITIALIZER(bdo_queue);
+    struct qemu_plugin_list plugin_list = QTAILQ_HEAD_INITIALIZER(plugin_list);
 
     module_call_init(MODULE_INIT_TRACE);
 
@@ -3026,6 +3028,7 @@ int main(int argc, char **argv, char **envp)
     qemu_add_opts(&qemu_global_opts);
     qemu_add_opts(&qemu_mon_opts);
     qemu_add_opts(&qemu_trace_opts);
+    qemu_plugin_add_opts();
     qemu_add_opts(&qemu_option_rom_opts);
     qemu_add_opts(&qemu_machine_opts);
     qemu_add_opts(&qemu_accel_opts);
@@ -3840,6 +3843,9 @@ int main(int argc, char **argv, char **envp)
                 g_free(trace_file);
                 trace_file = trace_opt_parse(optarg);
                 break;
+            case QEMU_OPTION_plugin:
+                qemu_plugin_opt_parse(optarg, &plugin_list);
+                break;
             case QEMU_OPTION_readconfig:
                 {
                     int ret = qemu_read_config_file(optarg);
@@ -4137,6 +4143,11 @@ int main(int argc, char **argv, char **envp)
                                machine_class->default_machine_opts, 0);
     }
 
+    /* process plugin before CPUs are created, but once -smp has been parsed */
+    if (qemu_plugin_load_list(&plugin_list)) {
+        exit(1);
+    }
+
     qemu_opts_foreach(qemu_find_opts("device"),
                       default_driver_check, NULL, NULL);
     qemu_opts_foreach(qemu_find_opts("global"),
diff --git a/qemu-options.hx b/qemu-options.hx
index 08f8516a9a..f3549eb2ec 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -3847,6 +3847,23 @@ HXCOMM HX does not support conditional compilation of text.
 @findex -trace
 @include qemu-option-trace.texi
 ETEXI
+DEF("plugin", HAS_ARG, QEMU_OPTION_plugin,
+    "-plugin [file=]<file>[,arg=<string>]\n"
+    "                load a plugin\n",
+    QEMU_ARCH_ALL)
+STEXI
+@item -plugin file=@var{file}[,arg=@var{string}]
+@findex -plugin
+
+Load a plugin.
+
+@table @option
+@item file=@var{file}
+Load the given plugin from a shared library file.
+@item arg=@var{string}
+Argument string passed to the plugin. (Can be given multiple times.)
+@end table
+ETEXI
 
 HXCOMM Internal use
 DEF("qtest", HAS_ARG, QEMU_OPTION_qtest, "", QEMU_ARCH_ALL)
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [Qemu-devel] [RFC v2 37/38] linux-user: support -plugin option
  2018-12-09 19:37 [Qemu-devel] [RFC v2 00/38] Plugin support Emilio G. Cota
                   ` (35 preceding siblings ...)
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 36/38] vl: support -plugin option Emilio G. Cota
@ 2018-12-09 19:37 ` Emilio G. Cota
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 38/38] tests/plugin: add sample plugins Emilio G. Cota
  2019-05-17 19:11 ` [Qemu-devel] [RFC PATCH] tests/tcg: enable plugin testing Alex Bennée
  38 siblings, 0 replies; 63+ messages in thread
From: Emilio G. Cota @ 2018-12-09 19:37 UTC (permalink / raw)
  To: qemu-devel
  Cc: Alex Bennée, Richard Henderson, Pavel Dovgalyuk,
	Lluís Vilanova

From: Lluís Vilanova <vilanova@ac.upc.edu>

Signed-off-by: Lluís Vilanova <vilanova@ac.upc.edu>
[ cota: s/instrument/plugin ]
Signed-off-by: Emilio G. Cota <cota@braap.org>
---
 linux-user/main.c | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/linux-user/main.c b/linux-user/main.c
index 923cbb753a..482766f0f4 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -28,6 +28,7 @@
 #include "qemu/config-file.h"
 #include "qemu/cutils.h"
 #include "qemu/help_option.h"
+#include "qemu/plugin.h"
 #include "cpu.h"
 #include "exec/exec-all.h"
 #include "tcg.h"
@@ -385,6 +386,15 @@ static void handle_arg_trace(const char *arg)
     trace_file = trace_opt_parse(arg);
 }
 
+static struct qemu_plugin_list plugins = QTAILQ_HEAD_INITIALIZER(plugins);
+
+#ifdef CONFIG_PLUGIN
+static void handle_arg_plugin(const char *arg)
+{
+    qemu_plugin_opt_parse(arg, &plugins);
+}
+#endif
+
 struct qemu_argument {
     const char *argv;
     const char *env;
@@ -436,6 +446,10 @@ static const struct qemu_argument arg_table[] = {
      "",           "Seed for pseudo-random number generator"},
     {"trace",      "QEMU_TRACE",       true,  handle_arg_trace,
      "",           "[[enable=]<pattern>][,events=<file>][,file=<file>]"},
+#ifdef CONFIG_PLUGIN
+    {"plugin",     "QEMU_PLUGIN",      true,  handle_arg_plugin,
+     "",           "[file=]<file>[,arg=<string>]"},
+#endif
     {"version",    "QEMU_VERSION",     false, handle_arg_version,
      "",           "display version information and exit"},
     {NULL, NULL, false, NULL, NULL, NULL}
@@ -627,6 +641,7 @@ int main(int argc, char **argv, char **envp)
     srand(time(NULL));
 
     qemu_add_opts(&qemu_trace_opts);
+    qemu_plugin_add_opts();
 
     optind = parse_args(argc, argv);
 
@@ -634,6 +649,9 @@ int main(int argc, char **argv, char **envp)
         exit(1);
     }
     trace_init_file(trace_file);
+    if (qemu_plugin_load_list(&plugins)) {
+        exit(1);
+    }
 
     /* Zero out regs */
     memset(regs, 0, sizeof(struct target_pt_regs));
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [Qemu-devel] [RFC v2 38/38] tests/plugin: add sample plugins
  2018-12-09 19:37 [Qemu-devel] [RFC v2 00/38] Plugin support Emilio G. Cota
                   ` (36 preceding siblings ...)
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 37/38] linux-user: " Emilio G. Cota
@ 2018-12-09 19:37 ` Emilio G. Cota
  2019-05-17 19:11 ` [Qemu-devel] [RFC PATCH] tests/tcg: enable plugin testing Alex Bennée
  38 siblings, 0 replies; 63+ messages in thread
From: Emilio G. Cota @ 2018-12-09 19:37 UTC (permalink / raw)
  To: qemu-devel; +Cc: Alex Bennée, Richard Henderson, Pavel Dovgalyuk

Pass arguments with -plugin=libfoo.so,arg=bar,arg=baz

Signed-off-by: Emilio G. Cota <cota@braap.org>
---
 configure             |  4 +-
 tests/plugin/bb.c     | 66 ++++++++++++++++++++++++++++++
 tests/plugin/empty.c  | 30 ++++++++++++++
 tests/plugin/insn.c   | 63 +++++++++++++++++++++++++++++
 tests/plugin/mem.c    | 93 +++++++++++++++++++++++++++++++++++++++++++
 tests/plugin/Makefile | 28 +++++++++++++
 6 files changed, 282 insertions(+), 2 deletions(-)
 create mode 100644 tests/plugin/bb.c
 create mode 100644 tests/plugin/empty.c
 create mode 100644 tests/plugin/insn.c
 create mode 100644 tests/plugin/mem.c
 create mode 100644 tests/plugin/Makefile

diff --git a/configure b/configure
index 91f9c08ae2..1440b5e688 100755
--- a/configure
+++ b/configure
@@ -7450,14 +7450,14 @@ fi
 
 # build tree in object directory in case the source is not in the current directory
 DIRS="tests tests/tcg tests/tcg/cris tests/tcg/lm32 tests/libqos tests/qapi-schema tests/tcg/xtensa tests/qemu-iotests tests/vm"
-DIRS="$DIRS tests/fp"
+DIRS="$DIRS tests/fp tests/plugin"
 DIRS="$DIRS docs docs/interop fsdev scsi"
 DIRS="$DIRS pc-bios/optionrom pc-bios/spapr-rtas pc-bios/s390-ccw"
 DIRS="$DIRS roms/seabios roms/vgabios"
 FILES="Makefile tests/tcg/Makefile qdict-test-data.txt"
 FILES="$FILES tests/tcg/cris/Makefile tests/tcg/cris/.gdbinit"
 FILES="$FILES tests/tcg/lm32/Makefile tests/tcg/xtensa/Makefile po/Makefile"
-FILES="$FILES tests/fp/Makefile"
+FILES="$FILES tests/fp/Makefile tests/plugin/Makefile"
 FILES="$FILES pc-bios/optionrom/Makefile pc-bios/keymaps"
 FILES="$FILES pc-bios/spapr-rtas/Makefile"
 FILES="$FILES pc-bios/s390-ccw/Makefile"
diff --git a/tests/plugin/bb.c b/tests/plugin/bb.c
new file mode 100644
index 0000000000..bb868599a9
--- /dev/null
+++ b/tests/plugin/bb.c
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2018, Emilio G. Cota <cota@braap.org>
+ *
+ * License: GNU GPL, version 2 or later.
+ *   See the COPYING file in the top-level directory.
+ */
+#include <inttypes.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdio.h>
+
+#include <qemu-plugin.h>
+
+static uint64_t bb_count;
+static uint64_t insn_count;
+static int stdout_fd;
+static bool do_inline;
+
+static void plugin_exit(qemu_plugin_id_t id, void *p)
+{
+    dprintf(stdout_fd, "bb's: %" PRIu64", insns: %" PRIu64 "\n",
+            bb_count, insn_count);
+}
+
+static void vcpu_tb_exec(unsigned int cpu_index, void *udata)
+{
+    unsigned long n_insns = (unsigned long)udata;
+
+    insn_count += n_insns;
+    bb_count++;
+}
+
+static void vcpu_tb_trans(qemu_plugin_id_t id, unsigned int cpu_index,
+                          struct qemu_plugin_tb *tb)
+{
+    unsigned long n_insns = qemu_plugin_tb_n_insns(tb);
+
+    if (do_inline) {
+        qemu_plugin_register_vcpu_tb_exec_inline(tb, QEMU_PLUGIN_INLINE_ADD_U64,
+                                                 &bb_count, 1);
+        qemu_plugin_register_vcpu_tb_exec_inline(tb, QEMU_PLUGIN_INLINE_ADD_U64,
+                                                 &insn_count, n_insns);
+    } else {
+        qemu_plugin_register_vcpu_tb_exec_cb(tb, vcpu_tb_exec,
+                                             QEMU_PLUGIN_CB_NO_REGS,
+                                             (void *)n_insns);
+    }
+}
+
+QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, int argc,
+                                           char **argv)
+{
+    if (argc && strcmp(argv[0], "inline") == 0) {
+        do_inline = true;
+    }
+
+    /* to be used when in the exit hook */
+    stdout_fd = dup(STDOUT_FILENO);
+    assert(stdout_fd);
+
+    qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans);
+    qemu_plugin_register_atexit_cb(id, plugin_exit, NULL);
+    return 0;
+}
diff --git a/tests/plugin/empty.c b/tests/plugin/empty.c
new file mode 100644
index 0000000000..b2e30bddb2
--- /dev/null
+++ b/tests/plugin/empty.c
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2018, Emilio G. Cota <cota@braap.org>
+ *
+ * License: GNU GPL, version 2 or later.
+ *   See the COPYING file in the top-level directory.
+ */
+#include <inttypes.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdio.h>
+
+#include <qemu-plugin.h>
+
+/*
+ * Empty TB translation callback.
+ * This allows us to measure the overhead of injecting and then
+ * removing empty instrumentation.
+ */
+static void vcpu_tb_trans(qemu_plugin_id_t id, unsigned int cpu_index,
+                          struct qemu_plugin_tb *tb)
+{ }
+
+QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, int argc,
+                                           char **argv)
+{
+    qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans);
+    return 0;
+}
diff --git a/tests/plugin/insn.c b/tests/plugin/insn.c
new file mode 100644
index 0000000000..11afe0e8f1
--- /dev/null
+++ b/tests/plugin/insn.c
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2018, Emilio G. Cota <cota@braap.org>
+ *
+ * License: GNU GPL, version 2 or later.
+ *   See the COPYING file in the top-level directory.
+ */
+#include <inttypes.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdio.h>
+
+#include <qemu-plugin.h>
+
+static int stdout_fd;
+static uint64_t insn_count;
+static bool do_inline;
+
+static void vcpu_insn_exec_before(unsigned int cpu_index, void *udata)
+{
+    insn_count++;
+}
+
+static void vcpu_tb_trans(qemu_plugin_id_t id, unsigned int cpu_index,
+                          struct qemu_plugin_tb *tb)
+{
+    size_t n = qemu_plugin_tb_n_insns(tb);
+    size_t i;
+
+    for (i = 0; i < n; i++) {
+        struct qemu_plugin_insn *insn = qemu_plugin_tb_get_insn(tb, i);
+
+        if (do_inline) {
+            qemu_plugin_register_vcpu_insn_exec_inline(
+                insn, QEMU_PLUGIN_INLINE_ADD_U64, &insn_count, 1);
+        } else {
+            qemu_plugin_register_vcpu_insn_exec_cb(
+                insn, vcpu_insn_exec_before, QEMU_PLUGIN_CB_NO_REGS, NULL);
+        }
+    }
+}
+
+static void plugin_exit(qemu_plugin_id_t id, void *p)
+{
+    dprintf(stdout_fd, "insns: %" PRIu64 "\n", insn_count);
+}
+
+QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, int argc,
+                                           char **argv)
+{
+    if (argc && !strcmp(argv[0], "inline")) {
+        do_inline = true;
+    }
+
+    /* to be used when in the exit hook */
+    stdout_fd = dup(STDOUT_FILENO);
+    assert(stdout_fd);
+
+    qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans);
+    qemu_plugin_register_atexit_cb(id, plugin_exit, NULL);
+    return 0;
+}
diff --git a/tests/plugin/mem.c b/tests/plugin/mem.c
new file mode 100644
index 0000000000..c13c672c91
--- /dev/null
+++ b/tests/plugin/mem.c
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2018, Emilio G. Cota <cota@braap.org>
+ *
+ * License: GNU GPL, version 2 or later.
+ *   See the COPYING file in the top-level directory.
+ */
+#include <inttypes.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdio.h>
+
+#include <qemu-plugin.h>
+
+static uint64_t mem_count;
+static int stdout_fd;
+static bool do_inline;
+static bool do_haddr;
+static enum qemu_plugin_mem_rw rw = QEMU_PLUGIN_MEM_RW;
+
+static void plugin_exit(qemu_plugin_id_t id, void *p)
+{
+    dprintf(stdout_fd, "accesses: %" PRIu64 "\n", mem_count);
+}
+
+static void vcpu_mem(unsigned int cpu_index, qemu_plugin_meminfo_t meminfo,
+                     uint64_t vaddr, void *udata)
+{
+    mem_count++;
+}
+
+static void vcpu_haddr(unsigned int cpu_index, qemu_plugin_meminfo_t meminfo,
+                       uint64_t vaddr, void *haddr, void *udata)
+{
+    mem_count++;
+}
+
+static void vcpu_tb_trans(qemu_plugin_id_t id, unsigned int cpu_index,
+                          struct qemu_plugin_tb *tb)
+{
+    size_t n = qemu_plugin_tb_n_insns(tb);
+    size_t i;
+
+    for (i = 0; i < n; i++) {
+        struct qemu_plugin_insn *insn = qemu_plugin_tb_get_insn(tb, i);
+
+        if (do_inline) {
+            qemu_plugin_register_vcpu_mem_inline(insn, rw,
+                                                 QEMU_PLUGIN_INLINE_ADD_U64,
+                                                 &mem_count, 1);
+        } else if (do_haddr) {
+            qemu_plugin_register_vcpu_mem_haddr_cb(insn, vcpu_haddr,
+                                                   QEMU_PLUGIN_CB_NO_REGS,
+                                                   rw, NULL);
+        } else {
+            qemu_plugin_register_vcpu_mem_cb(insn, vcpu_mem,
+                                             QEMU_PLUGIN_CB_NO_REGS,
+                                             rw, NULL);
+        }
+    }
+}
+
+QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, int argc,
+                                           char **argv)
+{
+    if (argc) {
+        if (argc >= 3) {
+            if (!strcmp(argv[2], "haddr")) {
+                do_haddr = true;
+            }
+        }
+        if (argc >= 2) {
+            const char *str = argv[1];
+
+            if (!strcmp(str, "r")) {
+                rw = QEMU_PLUGIN_MEM_R;
+            } else if (!strcmp(str, "w")) {
+                rw = QEMU_PLUGIN_MEM_W;
+            }
+        }
+        if (!strcmp(argv[0], "inline")) {
+            do_inline = true;
+        }
+    }
+    /* plugin_exit might write to stdout after stdout has been closed */
+    stdout_fd = dup(STDOUT_FILENO);
+    assert(stdout_fd);
+
+    qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans);
+    qemu_plugin_register_atexit_cb(id, plugin_exit, NULL);
+    return 0;
+}
diff --git a/tests/plugin/Makefile b/tests/plugin/Makefile
new file mode 100644
index 0000000000..f9a3546ea3
--- /dev/null
+++ b/tests/plugin/Makefile
@@ -0,0 +1,28 @@
+BUILD_DIR := $(CURDIR)/../..
+
+include $(BUILD_DIR)/config-host.mak
+include $(SRC_PATH)/rules.mak
+
+$(call set-vpath, $(SRC_PATH)/tests/plugin)
+
+NAMES :=
+NAMES += bb
+NAMES += empty
+NAMES += insn
+NAMES += mem
+
+SONAMES := $(addsuffix .so,$(addprefix lib,$(NAMES)))
+
+QEMU_CFLAGS += -fPIC
+QEMU_CFLAGS += -I$(SRC_PATH)/include/qemu
+
+all: $(SONAMES)
+
+lib%.so: %.o
+	$(CC) -shared -Wl,-soname,$@ -o $@ $^ $(LDLIBS)
+
+clean:
+	rm -f *.o *.so *.d
+	rm -Rf .libs
+
+.PHONY: all clean
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 63+ messages in thread

* Re: [Qemu-devel] [RFC v2 06/38] plugin: add core code
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 06/38] plugin: add core code Emilio G. Cota
@ 2018-12-10 11:37   ` Pavel Dovgalyuk
  2018-12-10 17:40     ` Emilio G. Cota
  2019-01-24 15:57   ` Alex Bennée
  1 sibling, 1 reply; 63+ messages in thread
From: Pavel Dovgalyuk @ 2018-12-10 11:37 UTC (permalink / raw)
  To: 'Emilio G. Cota', qemu-devel
  Cc: 'Alex Bennée', 'Richard Henderson'


> From: Emilio G. Cota [mailto:cota@braap.org]
> +/*
> + * A dynamic callback has an insertion point that is determined at run-time.
> + * Usually the insertion point is somewhere in the code cache; think for
> + * instance of a callback to be called upon the execution of a particular TB.
> + */
> +struct qemu_plugin_dyn_cb {
> +    union qemu_plugin_cb_sig f;
> +    void *userp;
> +    unsigned tcg_flags;
> +    enum plugin_dyn_cb_type type;
> +    /* @rw applies to mem callbacks only (both regular and inline) */
> +    enum qemu_plugin_mem_rw rw;
> +    /* fields specific to each dyn_cb type go here */
> +    union {
> +        struct {
> +            bool haddr;
> +        } mem;
> +        struct {
> +            enum qemu_plugin_op op;
> +            uint64_t imm;
> +        } inline_insn;
> +    };
> +};
> +
> +struct qemu_plugin_dyn_cb_arr {
> +    struct qemu_plugin_dyn_cb *data;
> +    size_t n;
> +    size_t capacity;
> +};
> +

Why not list or something dynamic? Is the indexing required?

Can you add the comments for the data structures and functions?
It is very hard to seek through the whole patch to get the details about them.

Pavel Dovgalyuk

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [Qemu-devel] [RFC v2 06/38] plugin: add core code
  2018-12-10 11:37   ` Pavel Dovgalyuk
@ 2018-12-10 17:40     ` Emilio G. Cota
  0 siblings, 0 replies; 63+ messages in thread
From: Emilio G. Cota @ 2018-12-10 17:40 UTC (permalink / raw)
  To: Pavel Dovgalyuk
  Cc: qemu-devel, 'Alex Bennée', 'Richard Henderson'

On Mon, Dec 10, 2018 at 14:37:25 +0300, Pavel Dovgalyuk wrote:
> > From: Emilio G. Cota [mailto:cota@braap.org]
(snip)
> > +struct qemu_plugin_dyn_cb_arr {
> > +    struct qemu_plugin_dyn_cb *data;
> > +    size_t n;
> > +    size_t capacity;
> > +};
> > +
> 
> Why not list or something dynamic?

Performance. Registering of dynamic callbacks can happen
very frequently (e.g. several times per instruction
translated), so we avoid malloc/free churn by keeping
an array of callback requests that we reuse across
translated TB's. The hierarchy is:

struct qemu_plugin_tb {
	insns[n_insns_in_the_tb] {
		dyn_cb_arr[various types];
	}
}

Each array has a "capacity" field so that we only ever expand
the arrays. This ensures that the amortized cost of
adding callbacks is negligible.

> Is the indexing required?

No, this is done just for performance.

> Can you add the comments for the data structures and functions?
> It is very hard to seek through the whole patch to get the details about them.

I had some comments but then the code evolved quickly and the
comments were outdated, which led to confusion. So I removed
most of them.

To understand the code I recommend you to go through one
of the examples and then follow the API calls, first through
plugin.c and then to plugin-gen.c where the instrumentation
is injected (based on the contents of the dyn_cb arrays).

Please ask further questions if anything is unclear.

Thanks,

		Emilio

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [Qemu-devel] [RFC v2 08/38] tcg: drop nargs from tcg_op_insert_{before, after}
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 08/38] tcg: drop nargs from tcg_op_insert_{before, after} Emilio G. Cota
@ 2018-12-13 23:52   ` Richard Henderson
  0 siblings, 0 replies; 63+ messages in thread
From: Richard Henderson @ 2018-12-13 23:52 UTC (permalink / raw)
  To: Emilio G. Cota, qemu-devel; +Cc: Alex Bennée, Pavel Dovgalyuk

On 12/9/18 1:37 PM, Emilio G. Cota wrote:
> It's unused.
> 
> Signed-off-by: Emilio G. Cota <cota@braap.org>
> ---
>  tcg/tcg.h      |  4 ++--
>  tcg/optimize.c |  4 ++--
>  tcg/tcg.c      | 10 ++++------
>  3 files changed, 8 insertions(+), 10 deletions(-)

Cherry-picked this into tcg-next.
The nargs argument is unused since 75e8b9b7aa0b95a761b9add7e2f09248b101a392.


r~

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [Qemu-devel] [RFC v2 05/38] plugin: add user-facing API
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 05/38] plugin: add user-facing API Emilio G. Cota
@ 2018-12-14 15:57   ` Aaron Lindsay
  2018-12-14 16:04     ` Aaron Lindsay
  2018-12-14 17:08     ` Emilio G. Cota
  0 siblings, 2 replies; 63+ messages in thread
From: Aaron Lindsay @ 2018-12-14 15:57 UTC (permalink / raw)
  To: Emilio G. Cota
  Cc: qemu-devel, Richard Henderson, Alex Bennée, Pavel Dovgalyuk

Emilio,

First, thanks for putting this together - I think everyone doing this
sort of thing will benefit if we're able to agree on one upstream plugin
interface.

One thing I'd like to see is support for unregistering callbacks once
they are registered. For instance, you can imagine that a plugin may
have 'modality', where it may care about tracing very detailed
information in one mode, but trace only limited information otherwise -
perhaps only enough to figure out when it needs to switch back to the
other mode.

I added a function to the user-facing plugin API in my own version of
Pavel's plugin patchset to clear all existing plugin instrumentation,
allowing the plugin to drop instrumentation if it was no longer
interested. Of course, you could always add logic to a plugin to ignore
unwanted callbacks, but it could be significantly more efficient to
remove them entirely. In Pavel's patchset, this was essentially just a
call to tb_flush(), though I think it would be a little more involved
for yours.

-Aaron

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [Qemu-devel] [RFC v2 05/38] plugin: add user-facing API
  2018-12-14 15:57   ` Aaron Lindsay
@ 2018-12-14 16:04     ` Aaron Lindsay
  2018-12-14 17:08     ` Emilio G. Cota
  1 sibling, 0 replies; 63+ messages in thread
From: Aaron Lindsay @ 2018-12-14 16:04 UTC (permalink / raw)
  To: Emilio G. Cota
  Cc: qemu-devel, Richard Henderson, Alex Bennée, Pavel Dovgalyuk

On Dec 14 10:57, Aaron Lindsay wrote:
> One thing I'd like to see is support for unregistering callbacks once
> they are registered.

By the way, I'm willing to work on this if we agree it sounds reasonable
and fits in with the rest of your implementation.

-Aaron

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [Qemu-devel] [RFC v2 05/38] plugin: add user-facing API
  2018-12-14 15:57   ` Aaron Lindsay
  2018-12-14 16:04     ` Aaron Lindsay
@ 2018-12-14 17:08     ` Emilio G. Cota
  2018-12-14 17:50       ` Emilio G. Cota
  2018-12-14 17:59       ` Aaron Lindsay
  1 sibling, 2 replies; 63+ messages in thread
From: Emilio G. Cota @ 2018-12-14 17:08 UTC (permalink / raw)
  To: Aaron Lindsay
  Cc: qemu-devel, Richard Henderson, Alex Bennée, Pavel Dovgalyuk

On Fri, Dec 14, 2018 at 15:57:32 +0000, Aaron Lindsay wrote:
> Emilio,
> 
> First, thanks for putting this together - I think everyone doing this
> sort of thing will benefit if we're able to agree on one upstream plugin
> interface.
> 
> One thing I'd like to see is support for unregistering callbacks once
> they are registered. For instance, you can imagine that a plugin may
> have 'modality', where it may care about tracing very detailed
> information in one mode, but trace only limited information otherwise -
> perhaps only enough to figure out when it needs to switch back to the
> other mode.
> 
> I added a function to the user-facing plugin API in my own version of
> Pavel's plugin patchset to clear all existing plugin instrumentation,
> allowing the plugin to drop instrumentation if it was no longer
> interested. Of course, you could always add logic to a plugin to ignore
> unwanted callbacks, but it could be significantly more efficient to
> remove them entirely. In Pavel's patchset, this was essentially just a
> call to tb_flush(), though I think it would be a little more involved
> for yours.

This is a good point.

"Regular" callbacks can be unregistered by just passing NULL as the
callback (see plugin_unregister_cb__locked, which is called from
do_plugin_register_cb). "Direct" callbacks, however (i.e. the ones
that embed a callback directly in the translated code),
would have to be unregistered by flushing the particular TB
(or all TBs, which is probably better from an API viewpoint).

I think the following API call would do what you need:

  typedef int (*qemu_plugin_reset_cb_t)(qemu_plugin_id_t id);
  void qemu_plugin_reset(qemu_plugin_id_t id, qemu_plugin_reset_cb_t cb);

(or maybe qemu_plugin_reinstall?)

The idea is that a plugin can "reset" itself, so that (1) all
its CBs are cleared and (2) the plugin can register new callbacks.
This would all happen in an atomic context (no vCPU running), so
that the plugin would miss no CPU events.

How does this sound?

Thanks,

		Emilio

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [Qemu-devel] [RFC v2 05/38] plugin: add user-facing API
  2018-12-14 17:08     ` Emilio G. Cota
@ 2018-12-14 17:50       ` Emilio G. Cota
  2018-12-14 18:47         ` Aaron Lindsay
  2018-12-14 17:59       ` Aaron Lindsay
  1 sibling, 1 reply; 63+ messages in thread
From: Emilio G. Cota @ 2018-12-14 17:50 UTC (permalink / raw)
  To: Aaron Lindsay
  Cc: Alex Bennée, Richard Henderson, qemu-devel, Pavel Dovgalyuk

On Fri, Dec 14, 2018 at 12:08:22 -0500, Emilio G. Cota wrote:
> On Fri, Dec 14, 2018 at 15:57:32 +0000, Aaron Lindsay wrote:
(snip)
> > I added a function to the user-facing plugin API in my own version of
> > Pavel's plugin patchset to clear all existing plugin instrumentation,
(snip)
> I think the following API call would do what you need:
> 
>   typedef int (*qemu_plugin_reset_cb_t)(qemu_plugin_id_t id);
>   void qemu_plugin_reset(qemu_plugin_id_t id, qemu_plugin_reset_cb_t cb);
> 
> (or maybe qemu_plugin_reinstall?)

An alternative is to track the TBs that a plugin has inserted
instrumentation into, and only flush those. This would require
us to do an additional hash table insert when adding a
direct callback, but it allow us to avoid exporting tb_flush indirectly
to plugins, which could be abused by malicious plugins to perform
a DoS attack.

I'll look into this.

		E.

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [Qemu-devel] [RFC v2 05/38] plugin: add user-facing API
  2018-12-14 17:08     ` Emilio G. Cota
  2018-12-14 17:50       ` Emilio G. Cota
@ 2018-12-14 17:59       ` Aaron Lindsay
  2018-12-14 18:23         ` Emilio G. Cota
  1 sibling, 1 reply; 63+ messages in thread
From: Aaron Lindsay @ 2018-12-14 17:59 UTC (permalink / raw)
  To: Emilio G. Cota
  Cc: qemu-devel, Richard Henderson, Alex Bennée, Pavel Dovgalyuk

On Dec 14 12:08, Emilio G. Cota wrote:
> On Fri, Dec 14, 2018 at 15:57:32 +0000, Aaron Lindsay wrote:
> > Emilio,
> > 
> > First, thanks for putting this together - I think everyone doing this
> > sort of thing will benefit if we're able to agree on one upstream plugin
> > interface.
> > 
> > One thing I'd like to see is support for unregistering callbacks once
> > they are registered. For instance, you can imagine that a plugin may
> > have 'modality', where it may care about tracing very detailed
> > information in one mode, but trace only limited information otherwise -
> > perhaps only enough to figure out when it needs to switch back to the
> > other mode.
> > 
> > I added a function to the user-facing plugin API in my own version of
> > Pavel's plugin patchset to clear all existing plugin instrumentation,
> > allowing the plugin to drop instrumentation if it was no longer
> > interested. Of course, you could always add logic to a plugin to ignore
> > unwanted callbacks, but it could be significantly more efficient to
> > remove them entirely. In Pavel's patchset, this was essentially just a
> > call to tb_flush(), though I think it would be a little more involved
> > for yours.
> 
> This is a good point.
> 
> "Regular" callbacks can be unregistered by just passing NULL as the
> callback (see plugin_unregister_cb__locked, which is called from
> do_plugin_register_cb).

Ah, okay, thanks - I missed this detail when looking through your
patches earlier.

> "Direct" callbacks, however (i.e. the ones
> that embed a callback directly in the translated code),
> would have to be unregistered by flushing the particular TB
> (or all TBs, which is probably better from an API viewpoint).
> 
> I think the following API call would do what you need:
> 
>   typedef int (*qemu_plugin_reset_cb_t)(qemu_plugin_id_t id);
>   void qemu_plugin_reset(qemu_plugin_id_t id, qemu_plugin_reset_cb_t cb);
> 
> (or maybe qemu_plugin_reinstall?)

I think I prefer your initial suggestion of qemu_plugin_reset - to me it
does a better job communicating that existing callbacks will be removed.
But, then again, _reinstall makes it more clear that you are responsible
for re-adding any callbacks you still want...

> The idea is that a plugin can "reset" itself, so that (1) all
> its CBs are cleared and (2) the plugin can register new callbacks.
> This would all happen in an atomic context (no vCPU running), so
> that the plugin would miss no CPU events.

The implication being that there would not be the same possibility of
other callbacks being called between when qemu_plugin_reset and the
qemu_plugin_reset_cb_t callback are called as there is at plugin
un-installation time?

> How does this sound?

I think what you describe is exactly what I'm interested in.

-Aaron

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [Qemu-devel] [RFC v2 05/38] plugin: add user-facing API
  2018-12-14 17:59       ` Aaron Lindsay
@ 2018-12-14 18:23         ` Emilio G. Cota
  0 siblings, 0 replies; 63+ messages in thread
From: Emilio G. Cota @ 2018-12-14 18:23 UTC (permalink / raw)
  To: Aaron Lindsay
  Cc: qemu-devel, Richard Henderson, Alex Bennée, Pavel Dovgalyuk

On Fri, Dec 14, 2018 at 17:59:20 +0000, Aaron Lindsay wrote:
> On Dec 14 12:08, Emilio G. Cota wrote:
(snip)
> > The idea is that a plugin can "reset" itself, so that (1) all
> > its CBs are cleared and (2) the plugin can register new callbacks.
> > This would all happen in an atomic context (no vCPU running), so
> > that the plugin would miss no CPU events.
> 
> The implication being that there would not be the same possibility of
> other callbacks being called between when qemu_plugin_reset and the
> qemu_plugin_reset_cb_t callback are called as there is at plugin
> un-installation time?

The callback is needed for the same reason -- we can only guarantee
that there will be no callbacks once the current RCU read critical section
expires.

> > How does this sound?
> 
> I think what you describe is exactly what I'm interested in.

Nice. I'll work on this for v3.

Thanks,

		Emilio

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [Qemu-devel] [RFC v2 05/38] plugin: add user-facing API
  2018-12-14 17:50       ` Emilio G. Cota
@ 2018-12-14 18:47         ` Aaron Lindsay
  2018-12-14 19:40           ` Emilio G. Cota
  0 siblings, 1 reply; 63+ messages in thread
From: Aaron Lindsay @ 2018-12-14 18:47 UTC (permalink / raw)
  To: Emilio G. Cota
  Cc: Alex Bennée, Richard Henderson, qemu-devel, Pavel Dovgalyuk

On Dec 14 12:50, Emilio G. Cota wrote:
> On Fri, Dec 14, 2018 at 12:08:22 -0500, Emilio G. Cota wrote:
> > On Fri, Dec 14, 2018 at 15:57:32 +0000, Aaron Lindsay wrote:
> (snip)
> > > I added a function to the user-facing plugin API in my own version of
> > > Pavel's plugin patchset to clear all existing plugin instrumentation,
> (snip)
> > I think the following API call would do what you need:
> > 
> >   typedef int (*qemu_plugin_reset_cb_t)(qemu_plugin_id_t id);
> >   void qemu_plugin_reset(qemu_plugin_id_t id, qemu_plugin_reset_cb_t cb);
> > 
> > (or maybe qemu_plugin_reinstall?)
> 
> An alternative is to track the TBs that a plugin has inserted
> instrumentation into, and only flush those. This would require
> us to do an additional hash table insert when adding a
> direct callback, but it allow us to avoid exporting tb_flush indirectly
> to plugins, which could be abused by malicious plugins to perform
> a DoS attack.

I don't think I have a preference. Though now I'm curious - when/why do
you expect users might run plugins that could be malicious in this way?

-Aaron

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [Qemu-devel] [RFC v2 05/38] plugin: add user-facing API
  2018-12-14 18:47         ` Aaron Lindsay
@ 2018-12-14 19:40           ` Emilio G. Cota
  0 siblings, 0 replies; 63+ messages in thread
From: Emilio G. Cota @ 2018-12-14 19:40 UTC (permalink / raw)
  To: Aaron Lindsay
  Cc: Alex Bennée, Richard Henderson, qemu-devel, Pavel Dovgalyuk

On Fri, Dec 14, 2018 at 18:47:42 +0000, Aaron Lindsay wrote:
> On Dec 14 12:50, Emilio G. Cota wrote:
> > On Fri, Dec 14, 2018 at 12:08:22 -0500, Emilio G. Cota wrote:
> > > On Fri, Dec 14, 2018 at 15:57:32 +0000, Aaron Lindsay wrote:
> > (snip)
> > > > I added a function to the user-facing plugin API in my own version of
> > > > Pavel's plugin patchset to clear all existing plugin instrumentation,
> > (snip)
> > > I think the following API call would do what you need:
> > > 
> > >   typedef int (*qemu_plugin_reset_cb_t)(qemu_plugin_id_t id);
> > >   void qemu_plugin_reset(qemu_plugin_id_t id, qemu_plugin_reset_cb_t cb);
> > > 
> > > (or maybe qemu_plugin_reinstall?)
> > 
> > An alternative is to track the TBs that a plugin has inserted
> > instrumentation into, and only flush those. This would require
> > us to do an additional hash table insert when adding a
> > direct callback, but it allow us to avoid exporting tb_flush indirectly
> > to plugins, which could be abused by malicious plugins to perform
> > a DoS attack.
> 
> I don't think I have a preference. Though now I'm curious - when/why do
> you expect users might run plugins that could be malicious in this way?

When this work started, others expressed concern about potentially
malicious plugins, although I think they were thinking of preventing
plugins from gaining access to data structures that could affect
the guest, e.g. CPUState. That's why I'm using a hash table for
the plugin context id, although that's probably overkill (do we
care about malicious plugins messing with the subscriptions of
other plugins? Not sure we should.)

Here I'm thinking more about giving plugin developers too much rope to
hang themselves with, e.g. calling repeatedly plugin_reset() out
of convenience, without realising the performance impact that
it entails. That's why I'd like to explore the alternative.

		Emilio

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [Qemu-devel] [RFC v2 15/38] tcg: let plugins instrument memory accesses
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 15/38] tcg: let plugins instrument memory accesses Emilio G. Cota
@ 2019-01-24 14:39   ` Alex Bennée
  2019-05-16 15:06     ` Alex Bennée
  0 siblings, 1 reply; 63+ messages in thread
From: Alex Bennée @ 2019-01-24 14:39 UTC (permalink / raw)
  To: Emilio G. Cota; +Cc: qemu-devel, Richard Henderson, Pavel Dovgalyuk


Emilio G. Cota <cota@braap.org> writes:

> XXX: store hostaddr from non-i386 TCG backends
> XXX: what hostaddr to return for I/O accesses?
> XXX: what hostaddr to return for cross-page accesses?

Just a heads up this patch now clashes with changes that have been made
to master.

>
> Here the trickiest feature is passing the host address to
> memory callbacks that request it. Perhaps it would be more
> appropriate to pass a "physical" address to plugins, but since
> in QEMU host addr ~= guest physical, I'm going with that for
> simplicity.
>
> To keep the implementation simple we piggy-back on the TLB fast path,
> and thus can only provide the host address _after_ memory accesses
> have occurred. For the slow path, it's a bit tedious because there
> are many places to update, but it's fairly simple.
>
> However, note that cross-page accesses are tricky, since the
> access might be to non-contiguous host addresses. So I'm punting
> on that and just passing NULL.
>
> Signed-off-by: Emilio G. Cota <cota@braap.org>
> ---
>  accel/tcg/atomic_template.h               |  5 +++
>  accel/tcg/softmmu_template.h              | 43 +++++++++++++++++-----
>  include/exec/cpu-defs.h                   |  9 +++++
>  include/exec/cpu_ldst.h                   |  9 +++++
>  include/exec/cpu_ldst_template.h          | 43 ++++++++++++++--------
>  include/exec/cpu_ldst_useronly_template.h | 42 +++++++++++++++-------
>  tcg/tcg.h                                 |  1 +
>  accel/tcg/cpu-exec.c                      |  2 ++
>  accel/tcg/cputlb.c                        |  9 +++++
>  tcg/i386/tcg-target.inc.c                 |  7 ++++
>  tcg/tcg-op.c                              | 44 ++++++++++++++++++-----
>  11 files changed, 169 insertions(+), 45 deletions(-)
>
> diff --git a/accel/tcg/atomic_template.h b/accel/tcg/atomic_template.h
> index 2f7d5ee02a..5619c4b4b9 100644
> --- a/accel/tcg/atomic_template.h
> +++ b/accel/tcg/atomic_template.h
> @@ -18,6 +18,7 @@
>   * License along with this library; if not, see <http://www.gnu.org/licenses/>.
>   */
>
> +#include "qemu/plugin.h"
>  #include "trace/mem.h"
>
>  #if DATA_SIZE == 16
> @@ -73,6 +74,8 @@ void atomic_trace_rmw_pre(CPUArchState *env, target_ulong addr, uint8_t info)
>  static inline void atomic_trace_rmw_post(CPUArchState *env, target_ulong addr,
>                                           void *haddr, uint8_t info)
>  {
> +    qemu_plugin_vcpu_mem_cb(ENV_GET_CPU(env), addr, haddr, info);
> +    qemu_plugin_vcpu_mem_cb(ENV_GET_CPU(env), addr, haddr, info | TRACE_MEM_ST);
>  }
>
>  static inline
> @@ -84,6 +87,7 @@ void atomic_trace_ld_pre(CPUArchState *env, target_ulong addr, uint8_t info)
>  static inline void atomic_trace_ld_post(CPUArchState *env, target_ulong addr,
>                                          void *haddr, uint8_t info)
>  {
> +    qemu_plugin_vcpu_mem_cb(ENV_GET_CPU(env), addr, haddr, info);
>  }
>
>  static inline
> @@ -95,6 +99,7 @@ void atomic_trace_st_pre(CPUArchState *env, target_ulong addr, uint8_t info)
>  static inline void atomic_trace_st_post(CPUArchState *env, target_ulong addr,
>                                          void *haddr, uint8_t info)
>  {
> +    qemu_plugin_vcpu_mem_cb(ENV_GET_CPU(env), addr, haddr, info);
>  }
>  #endif /* ATOMIC_TEMPLATE_COMMON */
>
> diff --git a/accel/tcg/softmmu_template.h b/accel/tcg/softmmu_template.h
> index b0adea045e..79109e25a1 100644
> --- a/accel/tcg/softmmu_template.h
> +++ b/accel/tcg/softmmu_template.h
> @@ -45,7 +45,6 @@
>  #error unsupported data size
>  #endif
>
> -
>  /* For the benefit of TCG generated code, we want to avoid the complication
>     of ABI-specific return type promotion and always return a value extended
>     to the register size of the host.  This is tcg_target_long, except in the
> @@ -99,10 +98,15 @@ static inline DATA_TYPE glue(io_read, SUFFIX)(CPUArchState *env,
>                                                size_t mmu_idx, size_t index,
>                                                target_ulong addr,
>                                                uintptr_t retaddr,
> +                                              TCGMemOp mo,
>                                                bool recheck,
>                                                MMUAccessType access_type)
>  {
>      CPUIOTLBEntry *iotlbentry = &env->iotlb[mmu_idx][index];
> +
> +    /* XXX Any sensible choice other than NULL? */
> +    set_hostaddr(env, mo, NULL);
> +
>      return io_readx(env, iotlbentry, mmu_idx, addr, retaddr, recheck,
>                      access_type, DATA_SIZE);
>  }
> @@ -115,7 +119,8 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr,
>      uintptr_t index = tlb_index(env, mmu_idx, addr);
>      CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr);
>      target_ulong tlb_addr = entry->ADDR_READ;
> -    unsigned a_bits = get_alignment_bits(get_memop(oi));
> +    TCGMemOp mo = get_memop(oi);
> +    unsigned a_bits = get_alignment_bits(mo);
>      uintptr_t haddr;
>      DATA_TYPE res;
>
> @@ -141,7 +146,7 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr,
>
>          /* ??? Note that the io helpers always read data in the target
>             byte ordering.  We should push the LE/BE request down into io.  */
> -        res = glue(io_read, SUFFIX)(env, mmu_idx, index, addr, retaddr,
> +        res = glue(io_read, SUFFIX)(env, mmu_idx, index, addr, retaddr, mo,
>                                      tlb_addr & TLB_RECHECK,
>                                      READ_ACCESS_TYPE);
>          res = TGT_LE(res);
> @@ -162,12 +167,19 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr,
>          res2 = helper_le_ld_name(env, addr2, oi, retaddr);
>          shift = (addr & (DATA_SIZE - 1)) * 8;
>
> +        /*
> +         * XXX cross-page accesses would have to be split into separate accesses
> +         * for the host address to make sense. For now, just return NULL.
> +         */
> +        set_hostaddr(env, mo, NULL);
> +
>          /* Little-endian combine.  */
>          res = (res1 >> shift) | (res2 << ((DATA_SIZE * 8) - shift));
>          return res;
>      }
>
>      haddr = addr + entry->addend;
> +    set_hostaddr(env, mo, (void *)haddr);
>  #if DATA_SIZE == 1
>      res = glue(glue(ld, LSUFFIX), _p)((uint8_t *)haddr);
>  #else
> @@ -184,7 +196,8 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr,
>      uintptr_t index = tlb_index(env, mmu_idx, addr);
>      CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr);
>      target_ulong tlb_addr = entry->ADDR_READ;
> -    unsigned a_bits = get_alignment_bits(get_memop(oi));
> +    TCGMemOp mo = get_memop(oi);
> +    unsigned a_bits = get_alignment_bits(mo);
>      uintptr_t haddr;
>      DATA_TYPE res;
>
> @@ -210,7 +223,7 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr,
>
>          /* ??? Note that the io helpers always read data in the target
>             byte ordering.  We should push the LE/BE request down into io.  */
> -        res = glue(io_read, SUFFIX)(env, mmu_idx, index, addr, retaddr,
> +        res = glue(io_read, SUFFIX)(env, mmu_idx, index, addr, retaddr, mo,
>                                      tlb_addr & TLB_RECHECK,
>                                      READ_ACCESS_TYPE);
>          res = TGT_BE(res);
> @@ -231,12 +244,15 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr,
>          res2 = helper_be_ld_name(env, addr2, oi, retaddr);
>          shift = (addr & (DATA_SIZE - 1)) * 8;
>
> +        set_hostaddr(env, mo, NULL);
> +
>          /* Big-endian combine.  */
>          res = (res1 << shift) | (res2 >> ((DATA_SIZE * 8) - shift));
>          return res;
>      }
>
>      haddr = addr + entry->addend;
> +    set_hostaddr(env, mo, (void *)haddr);
>      res = glue(glue(ld, LSUFFIX), _be_p)((uint8_t *)haddr);
>      return res;
>  }
> @@ -267,9 +283,12 @@ static inline void glue(io_write, SUFFIX)(CPUArchState *env,
>                                            DATA_TYPE val,
>                                            target_ulong addr,
>                                            uintptr_t retaddr,
> +                                          TCGMemOp mo,
>                                            bool recheck)
>  {
>      CPUIOTLBEntry *iotlbentry = &env->iotlb[mmu_idx][index];
> +
> +    set_hostaddr(env, mo, NULL);
>      return io_writex(env, iotlbentry, mmu_idx, val, addr, retaddr,
>                       recheck, DATA_SIZE);
>  }
> @@ -281,7 +300,8 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
>      uintptr_t index = tlb_index(env, mmu_idx, addr);
>      CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr);
>      target_ulong tlb_addr = tlb_addr_write(entry);
> -    unsigned a_bits = get_alignment_bits(get_memop(oi));
> +    TCGMemOp mo = get_memop(oi);
> +    unsigned a_bits = get_alignment_bits(mo);
>      uintptr_t haddr;
>
>      if (addr & ((1 << a_bits) - 1)) {
> @@ -308,7 +328,7 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
>             byte ordering.  We should push the LE/BE request down into io.  */
>          val = TGT_LE(val);
>          glue(io_write, SUFFIX)(env, mmu_idx, index, val, addr,
> -                               retaddr, tlb_addr & TLB_RECHECK);
> +                               retaddr, mo, tlb_addr & TLB_RECHECK);
>          return;
>      }
>
> @@ -340,10 +360,12 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
>              glue(helper_ret_stb, MMUSUFFIX)(env, addr + i, val8,
>                                              oi, retaddr);
>          }
> +        set_hostaddr(env, mo, NULL);
>          return;
>      }
>
>      haddr = addr + entry->addend;
> +    set_hostaddr(env, mo, (void *)haddr);
>  #if DATA_SIZE == 1
>      glue(glue(st, SUFFIX), _p)((uint8_t *)haddr, val);
>  #else
> @@ -359,7 +381,8 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
>      uintptr_t index = tlb_index(env, mmu_idx, addr);
>      CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr);
>      target_ulong tlb_addr = tlb_addr_write(entry);
> -    unsigned a_bits = get_alignment_bits(get_memop(oi));
> +    TCGMemOp mo = get_memop(oi);
> +    unsigned a_bits = get_alignment_bits(mo);
>      uintptr_t haddr;
>
>      if (addr & ((1 << a_bits) - 1)) {
> @@ -385,7 +408,7 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
>          /* ??? Note that the io helpers always read data in the target
>             byte ordering.  We should push the LE/BE request down into io.  */
>          val = TGT_BE(val);
> -        glue(io_write, SUFFIX)(env, mmu_idx, index, val, addr, retaddr,
> +        glue(io_write, SUFFIX)(env, mmu_idx, index, val, addr, retaddr, mo,
>                                 tlb_addr & TLB_RECHECK);
>          return;
>      }
> @@ -418,10 +441,12 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
>              glue(helper_ret_stb, MMUSUFFIX)(env, addr + i, val8,
>                                              oi, retaddr);
>          }
> +        set_hostaddr(env, mo, NULL);
>          return;
>      }
>
>      haddr = addr + entry->addend;
> +    set_hostaddr(env, mo, (void *)haddr);
>      glue(glue(st, SUFFIX), _be_p)((uint8_t *)haddr, val);
>  }
>  #endif /* DATA_SIZE > 1 */
> diff --git a/include/exec/cpu-defs.h b/include/exec/cpu-defs.h
> index 40cd5d4774..f46bc0917d 100644
> --- a/include/exec/cpu-defs.h
> +++ b/include/exec/cpu-defs.h
> @@ -178,6 +178,14 @@ typedef struct CPUTLBDesc {
>      CPUIOTLBEntry iotlb[NB_MMU_MODES][CPU_TLB_SIZE];
>  #endif /* TCG_TARGET_IMPLEMENTS_DYN_TLB */
>
> +#ifdef CONFIG_PLUGIN
> +# define CPU_PLUGIN_HOSTADDR                            \
> +    /* stores the host address of a guest access */     \
> +    void *hostaddr;
> +#else
> +# define CPU_PLUGIN_HOSTADDR
> +#endif
> +
>  #define CPU_COMMON_TLB                                                  \
>      /* The meaning of the MMU modes is defined in the target code. */   \
>      /* tlb_lock serializes updates to tlb_table and tlb_v_table */      \
> @@ -186,6 +194,7 @@ typedef struct CPUTLBDesc {
>      CPUTLBEntry tlb_v_table[NB_MMU_MODES][CPU_VTLB_SIZE];               \
>      CPU_IOTLB                                                           \
>      CPUIOTLBEntry iotlb_v[NB_MMU_MODES][CPU_VTLB_SIZE];                 \
> +    CPU_PLUGIN_HOSTADDR                                                 \
>      size_t tlb_flush_count;                                             \
>      target_ulong tlb_flush_addr;                                        \
>      target_ulong tlb_flush_mask;                                        \
> diff --git a/include/exec/cpu_ldst.h b/include/exec/cpu_ldst.h
> index 83b2907d86..32dd8dd603 100644
> --- a/include/exec/cpu_ldst.h
> +++ b/include/exec/cpu_ldst.h
> @@ -85,6 +85,15 @@ typedef target_ulong abi_ptr;
>  #define TARGET_ABI_FMT_ptr TARGET_ABI_FMT_lx
>  #endif
>
> +static inline void *read_hostaddr(CPUArchState *env)
> +{
> +#if defined(CONFIG_SOFTMMU) && defined(CONFIG_PLUGIN)
> +    return env->hostaddr;
> +#else
> +    return NULL;
> +#endif
> +}
> +
>  #if defined(CONFIG_USER_ONLY)
>
>  extern __thread uintptr_t helper_retaddr;
> diff --git a/include/exec/cpu_ldst_template.h b/include/exec/cpu_ldst_template.h
> index 0f061d47ef..3493cb13bf 100644
> --- a/include/exec/cpu_ldst_template.h
> +++ b/include/exec/cpu_ldst_template.h
> @@ -28,6 +28,7 @@
>  #include "trace-root.h"
>  #endif
>
> +#include "qemu/plugin.h"
>  #include "trace/mem.h"
>
>  #if DATA_SIZE == 8
> @@ -86,11 +87,11 @@ glue(glue(glue(cpu_ld, USUFFIX), MEMSUFFIX), _ra)(CPUArchState *env,
>      target_ulong addr;
>      int mmu_idx;
>      TCGMemOpIdx oi;
> -
> +    uintptr_t hostaddr;
>  #if !defined(SOFTMMU_CODE_ACCESS)
> -    trace_guest_mem_before_exec(
> -        ENV_GET_CPU(env), ptr,
> -        trace_mem_build_info(SHIFT, false, MO_TE, false));
> +    uint8_t meminfo = trace_mem_build_info(SHIFT, false, MO_TE, false);
> +
> +    trace_guest_mem_before_exec(ENV_GET_CPU(env), ptr, meminfo);
>  #endif
>
>      addr = ptr;
> @@ -101,10 +102,14 @@ glue(glue(glue(cpu_ld, USUFFIX), MEMSUFFIX), _ra)(CPUArchState *env,
>          oi = make_memop_idx(SHIFT, mmu_idx);
>          res = glue(glue(helper_ret_ld, URETSUFFIX), MMUSUFFIX)(env, addr,
>                                                              oi, retaddr);
> +        hostaddr = (uintptr_t)read_hostaddr(env);
>      } else {
> -        uintptr_t hostaddr = addr + entry->addend;
> +        hostaddr = addr + entry->addend;
>          res = glue(glue(ld, USUFFIX), _p)((uint8_t *)hostaddr);
>      }
> +#ifndef SOFTMMU_CODE_ACCESS
> +    qemu_plugin_vcpu_mem_cb(ENV_GET_CPU(env), ptr, (void *)hostaddr, meminfo);
> +#endif
>      return res;
>  }
>
> @@ -125,11 +130,11 @@ glue(glue(glue(cpu_lds, SUFFIX), MEMSUFFIX), _ra)(CPUArchState *env,
>      target_ulong addr;
>      int mmu_idx;
>      TCGMemOpIdx oi;
> -
> +    uintptr_t hostaddr;
>  #if !defined(SOFTMMU_CODE_ACCESS)
> -    trace_guest_mem_before_exec(
> -        ENV_GET_CPU(env), ptr,
> -        trace_mem_build_info(SHIFT, true, MO_TE, false));
> +    uint8_t meminfo = trace_mem_build_info(SHIFT, false, MO_TE, false);
> +
> +    trace_guest_mem_before_exec(ENV_GET_CPU(env), ptr, meminfo);
>  #endif
>
>      addr = ptr;
> @@ -140,10 +145,14 @@ glue(glue(glue(cpu_lds, SUFFIX), MEMSUFFIX), _ra)(CPUArchState *env,
>          oi = make_memop_idx(SHIFT, mmu_idx);
>          res = (DATA_STYPE)glue(glue(helper_ret_ld, SRETSUFFIX),
>                                 MMUSUFFIX)(env, addr, oi, retaddr);
> +        hostaddr = (uintptr_t)read_hostaddr(env);
>      } else {
> -        uintptr_t hostaddr = addr + entry->addend;
> +        hostaddr = addr + entry->addend;
>          res = glue(glue(lds, SUFFIX), _p)((uint8_t *)hostaddr);
>      }
> +#ifndef SOFTMMU_CODE_ACCESS
> +    qemu_plugin_vcpu_mem_cb(ENV_GET_CPU(env), ptr, (void *)hostaddr, meminfo);
> +#endif
>      return res;
>  }
>
> @@ -167,11 +176,11 @@ glue(glue(glue(cpu_st, SUFFIX), MEMSUFFIX), _ra)(CPUArchState *env,
>      target_ulong addr;
>      int mmu_idx;
>      TCGMemOpIdx oi;
> -
> +    uintptr_t hostaddr;
>  #if !defined(SOFTMMU_CODE_ACCESS)
> -    trace_guest_mem_before_exec(
> -        ENV_GET_CPU(env), ptr,
> -        trace_mem_build_info(SHIFT, false, MO_TE, true));
> +    uint8_t meminfo = trace_mem_build_info(SHIFT, false, MO_TE, true);
> +
> +    trace_guest_mem_before_exec(ENV_GET_CPU(env), ptr, meminfo);
>  #endif
>
>      addr = ptr;
> @@ -182,10 +191,14 @@ glue(glue(glue(cpu_st, SUFFIX), MEMSUFFIX), _ra)(CPUArchState *env,
>          oi = make_memop_idx(SHIFT, mmu_idx);
>          glue(glue(helper_ret_st, SUFFIX), MMUSUFFIX)(env, addr, v, oi,
>                                                       retaddr);
> +        hostaddr = (uintptr_t)read_hostaddr(env);
>      } else {
> -        uintptr_t hostaddr = addr + entry->addend;
> +        hostaddr = addr + entry->addend;
>          glue(glue(st, SUFFIX), _p)((uint8_t *)hostaddr, v);
>      }
> +#ifndef SOFTMMU_CODE_ACCESS
> +    qemu_plugin_vcpu_mem_cb(ENV_GET_CPU(env), ptr, (void *)hostaddr, meminfo);
> +#endif
>  }
>
>  static inline void
> diff --git a/include/exec/cpu_ldst_useronly_template.h b/include/exec/cpu_ldst_useronly_template.h
> index 0fd6019af0..e752e9c00e 100644
> --- a/include/exec/cpu_ldst_useronly_template.h
> +++ b/include/exec/cpu_ldst_useronly_template.h
> @@ -64,12 +64,19 @@
>  static inline RES_TYPE
>  glue(glue(cpu_ld, USUFFIX), MEMSUFFIX)(CPUArchState *env, abi_ptr ptr)
>  {
> +    RES_TYPE ret;
> +#if !defined(CODE_ACCESS)
> +    uint8_t meminfo = trace_mem_build_info(SHIFT, false, MO_TE, false);
> +
> +    trace_guest_mem_before_exec(ENV_GET_CPU(env), ptr, meminfo);
> +#endif
> +
> +    ret = glue(glue(ld, USUFFIX), _p)(g2h(ptr));
> +
>  #if !defined(CODE_ACCESS)
> -    trace_guest_mem_before_exec(
> -        ENV_GET_CPU(env), ptr,
> -        trace_mem_build_info(SHIFT, false, MO_TE, false));
> +    qemu_plugin_vcpu_mem_cb(ENV_GET_CPU(env), ptr, NULL, meminfo);
>  #endif
> -    return glue(glue(ld, USUFFIX), _p)(g2h(ptr));
> +    return ret;
>  }
>
>  static inline RES_TYPE
> @@ -88,12 +95,19 @@ glue(glue(glue(cpu_ld, USUFFIX), MEMSUFFIX), _ra)(CPUArchState *env,
>  static inline int
>  glue(glue(cpu_lds, SUFFIX), MEMSUFFIX)(CPUArchState *env, abi_ptr ptr)
>  {
> +    int ret;
>  #if !defined(CODE_ACCESS)
> -    trace_guest_mem_before_exec(
> -        ENV_GET_CPU(env), ptr,
> -        trace_mem_build_info(SHIFT, true, MO_TE, false));
> +    uint8_t meminfo = trace_mem_build_info(SHIFT, true, MO_TE, false);
> +
> +    trace_guest_mem_before_exec(ENV_GET_CPU(env), ptr, meminfo);
>  #endif
> -    return glue(glue(lds, SUFFIX), _p)(g2h(ptr));
> +
> +    ret = glue(glue(lds, SUFFIX), _p)(g2h(ptr));
> +
> +#if !defined(CODE_ACCESS)
> +    qemu_plugin_vcpu_mem_cb(ENV_GET_CPU(env), ptr, NULL, meminfo);
> +#endif
> +    return ret;
>  }
>
>  static inline int
> @@ -109,17 +123,21 @@ glue(glue(glue(cpu_lds, SUFFIX), MEMSUFFIX), _ra)(CPUArchState *env,
>  }
>  #endif
>
> -#ifndef CODE_ACCESS
> +#if !defined(CODE_ACCESS)
>  static inline void
>  glue(glue(cpu_st, SUFFIX), MEMSUFFIX)(CPUArchState *env, abi_ptr ptr,
>                                        RES_TYPE v)
>  {
>  #if !defined(CODE_ACCESS)
> -    trace_guest_mem_before_exec(
> -        ENV_GET_CPU(env), ptr,
> -        trace_mem_build_info(SHIFT, false, MO_TE, true));
> +    uint8_t meminfo = trace_mem_build_info(SHIFT, false, MO_TE, true);
> +    trace_guest_mem_before_exec(ENV_GET_CPU(env), ptr, meminfo);
>  #endif
> +
>      glue(glue(st, SUFFIX), _p)(g2h(ptr), v);
> +
> +#if !defined(CODE_ACCESS)
> +    qemu_plugin_vcpu_mem_cb(ENV_GET_CPU(env), ptr, NULL, meminfo);
> +#endif
>  }
>
>  static inline void
> diff --git a/tcg/tcg.h b/tcg/tcg.h
> index a376f83ab6..8938dcf52e 100644
> --- a/tcg/tcg.h
> +++ b/tcg/tcg.h
> @@ -29,6 +29,7 @@
>  #include "cpu.h"
>  #include "exec/tb-context.h"
>  #include "qemu/bitops.h"
> +#include "qemu/plugin.h"
>  #include "qemu/queue.h"
>  #include "tcg-mo.h"
>  #include "tcg-target.h"
> diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c
> index d590f1f6c0..4c8265a908 100644
> --- a/accel/tcg/cpu-exec.c
> +++ b/accel/tcg/cpu-exec.c
> @@ -267,6 +267,7 @@ void cpu_exec_step_atomic(CPUState *cpu)
>          tcg_debug_assert(!have_mmap_lock());
>  #endif
>          assert_no_pages_locked();
> +        qemu_plugin_disable_mem_helpers(cpu);
>      }
>
>      if (in_exclusive_region) {
> @@ -716,6 +717,7 @@ int cpu_exec(CPUState *cpu)
>          if (qemu_mutex_iothread_locked()) {
>              qemu_mutex_unlock_iothread();
>          }
> +        qemu_plugin_disable_mem_helpers(cpu);
>      }
>
>      /* if an exception is pending, we execute it here */
> diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c
> index 5c61908084..3bdf98d2c3 100644
> --- a/accel/tcg/cputlb.c
> +++ b/accel/tcg/cputlb.c
> @@ -1208,6 +1208,15 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr,
>      cpu_loop_exit_atomic(ENV_GET_CPU(env), retaddr);
>  }
>
> +static inline void set_hostaddr(CPUArchState *env, TCGMemOp mo, void *haddr)
> +{
> +#ifdef CONFIG_PLUGIN
> +    if (mo & MO_HADDR) {
> +        env->hostaddr = haddr;
> +    }
> +#endif
> +}
> +
>  #ifdef TARGET_WORDS_BIGENDIAN
>  # define TGT_BE(X)  (X)
>  # define TGT_LE(X)  BSWAP(X)
> diff --git a/tcg/i386/tcg-target.inc.c b/tcg/i386/tcg-target.inc.c
> index 5cbb07deab..eb3725f2e3 100644
> --- a/tcg/i386/tcg-target.inc.c
> +++ b/tcg/i386/tcg-target.inc.c
> @@ -1685,6 +1685,13 @@ static inline void tcg_out_tlb_load(TCGContext *s, TCGReg addrlo, TCGReg addrhi,
>      /* add addend(r0), r1 */
>      tcg_out_modrm_offset(s, OPC_ADD_GvEv + hrexw, r1, r0,
>                           offsetof(CPUTLBEntry, addend));
> +
> +#ifdef CONFIG_PLUGIN
> +    if (opc & MO_HADDR) {
> +        tcg_out_st(s, TCG_TYPE_PTR, r1, TCG_AREG0,
> +                   offsetof(CPUArchState, hostaddr));
> +    }
> +#endif
>  }
>
>  /*
> diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c
> index 7a8015c5a9..b30f0d4440 100644
> --- a/tcg/tcg-op.c
> +++ b/tcg/tcg-op.c
> @@ -31,6 +31,7 @@
>  #include "tcg-mo.h"
>  #include "trace-tcg.h"
>  #include "trace/mem.h"
> +#include "exec/plugin-gen.h"
>
>  /* Reduce the number of ifdefs below.  This assumes that all uses of
>     TCGV_HIGH and TCGV_LOW are properly protected by a conditional that
> @@ -2595,6 +2596,7 @@ void tcg_gen_exit_tb(TranslationBlock *tb, unsigned idx)
>          tcg_debug_assert(idx == TB_EXIT_REQUESTED);
>      }
>
> +    plugin_gen_disable_mem_helpers();
>      tcg_gen_op1i(INDEX_op_exit_tb, val);
>  }
>
> @@ -2607,6 +2609,7 @@ void tcg_gen_goto_tb(unsigned idx)
>      tcg_debug_assert((tcg_ctx->goto_tb_issue_mask & (1 << idx)) == 0);
>      tcg_ctx->goto_tb_issue_mask |= 1 << idx;
>  #endif
> +    plugin_gen_disable_mem_helpers();
>      /* When not chaining, we simply fall through to the "fallback" exit.  */
>      if (!qemu_loglevel_mask(CPU_LOG_TB_NOCHAIN)) {
>          tcg_gen_op1i(INDEX_op_goto_tb, idx);
> @@ -2616,7 +2619,10 @@ void tcg_gen_goto_tb(unsigned idx)
>  void tcg_gen_lookup_and_goto_ptr(void)
>  {
>      if (TCG_TARGET_HAS_goto_ptr && !qemu_loglevel_mask(CPU_LOG_TB_NOCHAIN)) {
> -        TCGv_ptr ptr = tcg_temp_new_ptr();
> +        TCGv_ptr ptr;
> +
> +        plugin_gen_disable_mem_helpers();
> +        ptr = tcg_temp_new_ptr();
>          gen_helper_lookup_tb_ptr(ptr, cpu_env);
>          tcg_gen_op1i(INDEX_op_goto_ptr, tcgv_ptr_arg(ptr));
>          tcg_temp_free_ptr(ptr);
> @@ -2699,26 +2705,42 @@ static void tcg_gen_req_mo(TCGBar type)
>      }
>  }
>
> +static inline void plugin_gen_mem_callbacks(TCGv vaddr, uint8_t info)
> +{
> +#ifdef CONFIG_PLUGIN
> +    if (tcg_ctx->plugin_insn == NULL) {
> +        return;
> +    }
> +    plugin_gen_empty_mem_callback(vaddr, info);
> +#endif
> +}
> +
>  void tcg_gen_qemu_ld_i32(TCGv_i32 val, TCGv addr, TCGArg idx, TCGMemOp memop)
>  {
> +    uint8_t info = trace_mem_get_info(memop, 0);
> +
>      tcg_gen_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD);
>      memop = tcg_canonicalize_memop(memop, 0, 0);
> -    trace_guest_mem_before_tcg(tcg_ctx->cpu, cpu_env,
> -                               addr, trace_mem_get_info(memop, 0));
> +    trace_guest_mem_before_tcg(tcg_ctx->cpu, cpu_env, addr, info);
>      gen_ldst_i32(INDEX_op_qemu_ld_i32, val, addr, memop, idx);
> +    plugin_gen_mem_callbacks(addr, info);
>  }
>
>  void tcg_gen_qemu_st_i32(TCGv_i32 val, TCGv addr, TCGArg idx, TCGMemOp memop)
>  {
> +    uint8_t info = trace_mem_get_info(memop, 1);
> +
>      tcg_gen_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST);
>      memop = tcg_canonicalize_memop(memop, 0, 1);
> -    trace_guest_mem_before_tcg(tcg_ctx->cpu, cpu_env,
> -                               addr, trace_mem_get_info(memop, 1));
> +    trace_guest_mem_before_tcg(tcg_ctx->cpu, cpu_env, addr, info);
>      gen_ldst_i32(INDEX_op_qemu_st_i32, val, addr, memop, idx);
> +    plugin_gen_mem_callbacks(addr, info);
>  }
>
>  void tcg_gen_qemu_ld_i64(TCGv_i64 val, TCGv addr, TCGArg idx, TCGMemOp memop)
>  {
> +    uint8_t info;
> +
>      tcg_gen_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD);
>      if (TCG_TARGET_REG_BITS == 32 && (memop & MO_SIZE) < MO_64) {
>          tcg_gen_qemu_ld_i32(TCGV_LOW(val), addr, idx, memop);
> @@ -2731,13 +2753,16 @@ void tcg_gen_qemu_ld_i64(TCGv_i64 val, TCGv addr, TCGArg idx, TCGMemOp memop)
>      }
>
>      memop = tcg_canonicalize_memop(memop, 1, 0);
> -    trace_guest_mem_before_tcg(tcg_ctx->cpu, cpu_env,
> -                               addr, trace_mem_get_info(memop, 0));
> +    info = trace_mem_get_info(memop, 0);
> +    trace_guest_mem_before_tcg(tcg_ctx->cpu, cpu_env, addr, info);
>      gen_ldst_i64(INDEX_op_qemu_ld_i64, val, addr, memop, idx);
> +    plugin_gen_mem_callbacks(addr, info);
>  }
>
>  void tcg_gen_qemu_st_i64(TCGv_i64 val, TCGv addr, TCGArg idx, TCGMemOp memop)
>  {
> +    uint8_t info;
> +
>      tcg_gen_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST);
>      if (TCG_TARGET_REG_BITS == 32 && (memop & MO_SIZE) < MO_64) {
>          tcg_gen_qemu_st_i32(TCGV_LOW(val), addr, idx, memop);
> @@ -2745,9 +2770,10 @@ void tcg_gen_qemu_st_i64(TCGv_i64 val, TCGv addr, TCGArg idx, TCGMemOp memop)
>      }
>
>      memop = tcg_canonicalize_memop(memop, 1, 1);
> -    trace_guest_mem_before_tcg(tcg_ctx->cpu, cpu_env,
> -                               addr, trace_mem_get_info(memop, 1));
> +    info = trace_mem_get_info(memop, 1);
> +    trace_guest_mem_before_tcg(tcg_ctx->cpu, cpu_env, addr, info);
>      gen_ldst_i64(INDEX_op_qemu_st_i64, val, addr, memop, idx);
> +    plugin_gen_mem_callbacks(addr, info);
>  }
>
>  static void tcg_gen_ext_i32(TCGv_i32 ret, TCGv_i32 val, TCGMemOp opc)


--
Alex Bennée

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [Qemu-devel] [RFC v2 01/38] trace: expand mem_info:size_shift to 3 bits
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 01/38] trace: expand mem_info:size_shift to 3 bits Emilio G. Cota
@ 2019-01-24 14:42   ` Alex Bennée
  0 siblings, 0 replies; 63+ messages in thread
From: Alex Bennée @ 2019-01-24 14:42 UTC (permalink / raw)
  To: Emilio G. Cota; +Cc: qemu-devel, Richard Henderson, Pavel Dovgalyuk


Emilio G. Cota <cota@braap.org> writes:

> This will allow us to trace 16B-long memory accesses.
>
> While at it, add some defines for the mem_info bits and simplify
> trace_mem_get_info by making it a wrapper around trace_mem_build_info.
>
> Signed-off-by: Emilio G. Cota <cota@braap.org>

Reviewed-by: Alex Bennée <alex.bennee@linaro.org>

> ---
>  trace-events | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/trace-events b/trace-events
> index 4fd2cb4b97..9d65d472d2 100644
> --- a/trace-events
> +++ b/trace-events
> @@ -151,7 +151,7 @@ vcpu guest_cpu_reset(void)
>  # Access information can be parsed as:
>  #
>  # struct mem_info {
> -#     uint8_t size_shift : 2; /* interpreted as "1 << size_shift" bytes */
> +#     uint8_t size_shift : 3; /* interpreted as "1 << size_shift" bytes */
>  #     bool    sign_extend: 1; /* sign-extended */
>  #     uint8_t endianness : 1; /* 0: little, 1: big */
>  #     bool    store      : 1; /* wheter it's a store operation */


--
Alex Bennée

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [Qemu-devel] [RFC v2 03/38] cpu: introduce cpu_in_exclusive_work_context()
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 03/38] cpu: introduce cpu_in_exclusive_work_context() Emilio G. Cota
@ 2019-01-24 14:44   ` Alex Bennée
  0 siblings, 0 replies; 63+ messages in thread
From: Alex Bennée @ 2019-01-24 14:44 UTC (permalink / raw)
  To: Emilio G. Cota; +Cc: qemu-devel, Richard Henderson, Pavel Dovgalyuk


Emilio G. Cota <cota@braap.org> writes:

> Suggested-by: Alex Bennée <alex.bennee@linaro.org>
> Signed-off-by: Emilio G. Cota <cota@braap.org>
> ---
>  include/qom/cpu.h | 13 +++++++++++++
>  cpus-common.c     |  2 ++
>  2 files changed, 15 insertions(+)
>
> diff --git a/include/qom/cpu.h b/include/qom/cpu.h
> index 772cc960fe..fab18089db 100644
> --- a/include/qom/cpu.h
> +++ b/include/qom/cpu.h
> @@ -349,6 +349,7 @@ struct CPUState {
>      bool thread_kicked;
>      bool crash_occurred;
>      bool exit_request;
> +    bool in_exclusive_work_context;

I'm not sure if I meant this or in_exclusive_context and push the flag
into start/end_exclusive. But let's see how it shakes out:

Reviewed-by: Alex Bennée <alex.bennee@linaro.org>


>      uint32_t cflags_next_tb;
>      /* updates protected by BQL */
>      uint32_t interrupt_request;
> @@ -913,6 +914,18 @@ void async_run_on_cpu_no_bql(CPUState *cpu, run_on_cpu_func func,
>   */
>  void async_safe_run_on_cpu(CPUState *cpu, run_on_cpu_func func, run_on_cpu_data data);
>
> +/**
> + * cpu_in_exclusive_work_context()
> + * @cpu: The vCPU to check
> + *
> + * Returns true if @cpu is an exclusive work context, which has
> + * previously been queued via async_safe_run_on_cpu().
> + */
> +static inline bool cpu_in_exclusive_work_context(const CPUState *cpu)
> +{
> +    return cpu->in_exclusive_work_context;
> +}
> +
>  /**
>   * qemu_get_cpu:
>   * @index: The CPUState@cpu_index value of the CPU to obtain.
> diff --git a/cpus-common.c b/cpus-common.c
> index 232cb12c46..d6ea42c80c 100644
> --- a/cpus-common.c
> +++ b/cpus-common.c
> @@ -370,7 +370,9 @@ static void process_queued_cpu_work_locked(CPUState *cpu)
>                  qemu_mutex_unlock_iothread();
>              }
>              start_exclusive();
> +            cpu->in_exclusive_work_context = true;
>              wi->func(cpu, wi->data);
> +            cpu->in_exclusive_work_context = false;
>              end_exclusive();
>              if (has_bql) {
>                  qemu_mutex_lock_iothread();


--
Alex Bennée

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [Qemu-devel] [RFC v2 04/38] translate-all: use cpu_in_exclusive_work_context() in tb_flush
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 04/38] translate-all: use cpu_in_exclusive_work_context() in tb_flush Emilio G. Cota
@ 2019-01-24 14:44   ` Alex Bennée
  0 siblings, 0 replies; 63+ messages in thread
From: Alex Bennée @ 2019-01-24 14:44 UTC (permalink / raw)
  To: Emilio G. Cota; +Cc: qemu-devel, Richard Henderson, Pavel Dovgalyuk


Emilio G. Cota <cota@braap.org> writes:

> tb_flush will be called by the plugin module from a safe
> work environment. Prepare for that.
>
> Suggested-by: Alex Bennée <alex.bennee@linaro.org>
> Signed-off-by: Emilio G. Cota <cota@braap.org>

Reviewed-by: Alex Bennée <alex.bennee@linaro.org>

> ---
>  accel/tcg/translate-all.c | 9 +++++++--
>  1 file changed, 7 insertions(+), 2 deletions(-)
>
> diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c
> index 038d82fdb5..62d5e13185 100644
> --- a/accel/tcg/translate-all.c
> +++ b/accel/tcg/translate-all.c
> @@ -1269,8 +1269,13 @@ void tb_flush(CPUState *cpu)
>  {
>      if (tcg_enabled()) {
>          unsigned tb_flush_count = atomic_mb_read(&tb_ctx.tb_flush_count);
> -        async_safe_run_on_cpu(cpu, do_tb_flush,
> -                              RUN_ON_CPU_HOST_INT(tb_flush_count));
> +
> +        if (cpu_in_exclusive_work_context(cpu)) {
> +            do_tb_flush(cpu, RUN_ON_CPU_HOST_INT(tb_flush_count));
> +        } else {
> +            async_safe_run_on_cpu(cpu, do_tb_flush,
> +                                  RUN_ON_CPU_HOST_INT(tb_flush_count));
> +        }
>      }
>  }


--
Alex Bennée

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [Qemu-devel] [RFC v2 09/38] cputlb: introduce get_page_addr_code_hostp
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 09/38] cputlb: introduce get_page_addr_code_hostp Emilio G. Cota
@ 2019-01-24 14:51   ` Alex Bennée
  0 siblings, 0 replies; 63+ messages in thread
From: Alex Bennée @ 2019-01-24 14:51 UTC (permalink / raw)
  To: Emilio G. Cota; +Cc: qemu-devel, Richard Henderson, Pavel Dovgalyuk


Emilio G. Cota <cota@braap.org> writes:

> This will be used by plugins to get the host address
> of instructions.
>
> Signed-off-by: Emilio G. Cota <cota@braap.org>
> ---
>  include/exec/exec-all.h | 13 +++++++++++++
>  accel/tcg/cputlb.c      | 14 +++++++++++++-
>  2 files changed, 26 insertions(+), 1 deletion(-)
>
> diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h
> index 815e5b1e83..afcc01e0e3 100644
> --- a/include/exec/exec-all.h
> +++ b/include/exec/exec-all.h
> @@ -22,6 +22,7 @@
>
>  #include "qemu-common.h"
>  #include "exec/tb-context.h"
> +#include "exec/cpu_ldst.h"
>  #include "sysemu/cpus.h"
>
>  /* allow to see translation results - the slowdown should be negligible, so we leave it */
> @@ -487,12 +488,24 @@ static inline tb_page_addr_t get_page_addr_code(CPUArchState *env1, target_ulong
>  {
>      return addr;
>  }
> +
> +static inline tb_page_addr_t get_page_addr_code_hostp(CPUArchState *env1,
> +                                                      target_ulong addr,
> +                                                      void **hostp)
> +{
> +    if (hostp) {
> +        *hostp = g2h(addr);
> +    }
> +    return addr;
> +}
>  #else
>  static inline void mmap_lock(void) {}
>  static inline void mmap_unlock(void) {}
>
>  /* cputlb.c */
>  tb_page_addr_t get_page_addr_code(CPUArchState *env1, target_ulong addr);
> +tb_page_addr_t get_page_addr_code_hostp(CPUArchState *env1, target_ulong addr,
> +                                        void **hostp);
>

It's probably about time these helpers got proper doc headers. So I take
it the host address of an instruction is where in host memory the code
is or where in host memory the translation for that instruction is?

>  void tlb_reset_dirty(CPUState *cpu, ram_addr_t start1, ram_addr_t length);
>  void tlb_set_dirty(CPUState *cpu, target_ulong vaddr);
> diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c
> index e3582f2f1d..5c61908084 100644
> --- a/accel/tcg/cputlb.c
> +++ b/accel/tcg/cputlb.c
> @@ -1069,7 +1069,8 @@ static bool victim_tlb_hit(CPUArchState *env, size_t mmu_idx, size_t index,
>   * is actually a ram_addr_t (in system mode; the user mode emulation
>   * version of this function returns a guest virtual address).
>   */
> -tb_page_addr_t get_page_addr_code(CPUArchState *env, target_ulong addr)
> +tb_page_addr_t get_page_addr_code_hostp(CPUArchState *env, target_ulong addr,
> +                                        void **hostp)
>  {
>      uintptr_t mmu_idx = cpu_mmu_index(env, true);
>      uintptr_t index = tlb_index(env, mmu_idx, addr);
> @@ -1092,13 +1093,24 @@ tb_page_addr_t get_page_addr_code(CPUArchState *env, target_ulong addr)
>           *    than a target page, so we must redo the MMU check every insn
>           *  - TLB_MMIO: region is not backed by RAM
>           */
> +        if (hostp) {
> +            *hostp = NULL;
> +        }
>          return -1;
>      }
>
>      p = (void *)((uintptr_t)addr + entry->addend);
> +    if (hostp) {
> +        *hostp = p;
> +    }
>      return qemu_ram_addr_from_host_nofail(p);
>  }
>
> +tb_page_addr_t get_page_addr_code(CPUArchState *env, target_ulong addr)
> +{
> +    return get_page_addr_code_hostp(env, addr, NULL);
> +}
> +
>  /* Probe for whether the specified guest write access is permitted.
>   * If it is not permitted then an exception will be taken in the same
>   * way as if this were a real write access (and we will not return).


--
Alex Bennée

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [Qemu-devel] [RFC v2 06/38] plugin: add core code
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 06/38] plugin: add core code Emilio G. Cota
  2018-12-10 11:37   ` Pavel Dovgalyuk
@ 2019-01-24 15:57   ` Alex Bennée
  1 sibling, 0 replies; 63+ messages in thread
From: Alex Bennée @ 2019-01-24 15:57 UTC (permalink / raw)
  To: Emilio G. Cota; +Cc: qemu-devel, Richard Henderson, Pavel Dovgalyuk


Emilio G. Cota <cota@braap.org> writes:

> Some design requirements/goals:

Also this commit breaks bisect-ability:


  15:56:23 [alex@zen:~/l/q/qemu.git] (0138e1a5…)|BISECTING 2 + head -n 3 config.log
  # QEMU configure log Thu Jan 24 15:19:36 GMT 2019
  # Configured with: './configure' '--disable-tools' '--disable-docs' '--python=/usr/bin/python3'
  #
  15:56:35 [alex@zen:~/l/q/qemu.git] (0138e1a5…)|BISECTING + make
          CHK version_gen.h
    CC      qapi/qapi-visit-core.o
  In file included from /home/alex/lsrc/qemu/qemu.git/include/qom/cpu.h:33:0,
                   from ./trace/control-internal.h:13,
                   from ./trace/control.h:270,
                   from qapi/trace.h:7,
                   from qapi/qapi-visit-core.c:21:
  /home/alex/lsrc/qemu/qemu.git/include/qemu/plugin.h:38:49: error: ‘qemu_plugin_list’ defined as wrong kind of tag
                                            struct qemu_plugin_list *head)
                                                   ^~~~~~~~~~~~~~~~
  /home/alex/lsrc/qemu/qemu.git/include/qemu/plugin.h:38:49: error: ‘struct qemu_plugin_list’ declared inside parameter list will not be visible outside of this definition or
  declaration [-Werror]
  /home/alex/lsrc/qemu/qemu.git/include/qemu/plugin.h:44:48: error: ‘qemu_plugin_list’ defined as wrong kind of tag
   static inline int qemu_plugin_load_list(struct qemu_plugin_list *head)
                                                  ^~~~~~~~~~~~~~~~
  /home/alex/lsrc/qemu/qemu.git/include/qemu/plugin.h:44:48: error: ‘struct qemu_plugin_list’ declared inside parameter list will not be visible outside of this definition or
  declaration [-Werror]
  cc1: all warnings being treated as errors
  /home/alex/lsrc/qemu/qemu.git/rules.mak:69: recipe for target 'qapi/qapi-visit-core.o' failed
  make: *** [qapi/qapi-visit-core.o] Error 1

--
Alex Bennée

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [Qemu-devel] [RFC v2 31/38] target/xtensa: fetch code with translator_ld
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 31/38] target/xtensa: " Emilio G. Cota
@ 2019-02-25 14:54   ` Alex Bennée
  2019-03-04  2:36     ` Max Filippov
  0 siblings, 1 reply; 63+ messages in thread
From: Alex Bennée @ 2019-02-25 14:54 UTC (permalink / raw)
  To: Emilio G. Cota; +Cc: qemu-devel, Richard Henderson, Pavel Dovgalyuk


Emilio G. Cota <cota@braap.org> writes:

> Signed-off-by: Emilio G. Cota <cota@braap.org>
> ---
>  target/xtensa/translate.c | 4 ++--
>  1 file changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/target/xtensa/translate.c b/target/xtensa/translate.c
> index 46e1338448..c140742562 100644
> --- a/target/xtensa/translate.c
> +++ b/target/xtensa/translate.c
> @@ -882,7 +882,7 @@ static inline unsigned xtensa_op0_insn_len(DisasContext *dc, uint8_t op0)
>  static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc)
>  {
>      xtensa_isa isa = dc->config->isa;
> -    unsigned char b[MAX_INSN_LENGTH] = {cpu_ldub_code(env, dc->pc)};
> +    unsigned char b[MAX_INSN_LENGTH] = {translator_ldub(env, dc->pc)};
>      unsigned len = xtensa_op0_insn_len(dc, b[0]);
>      xtensa_format fmt;
>      int slot, slots;
> @@ -914,7 +914,7 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc)
>                        dc->pc);
>      }
>      for (i = 1; i < len; ++i) {
> -        b[i] = cpu_ldub_code(env, dc->pc + i);
> +        b[i] = translator_ldub(env, dc->pc + i);
>      }
>      xtensa_insnbuf_from_chars(isa, dc->insnbuf, b, len);
>      fmt = xtensa_format_decode(isa, dc->insnbuf);

There is also:

  static inline unsigned xtensa_insn_len(CPUXtensaState *env, DisasContext *dc)
  {
      uint8_t b0 = cpu_ldub_code(env, dc->pc);
      return xtensa_op0_insn_len(dc, b0);
  }

Or is this usage a re-read of something we've already got?

--
Alex Bennée

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [Qemu-devel] [RFC v2 07/38] queue: add QTAILQ_REMOVE_SEVERAL
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 07/38] queue: add QTAILQ_REMOVE_SEVERAL Emilio G. Cota
@ 2019-02-25 16:22   ` Alex Bennée
  2019-02-25 18:02     ` Emilio G. Cota
  0 siblings, 1 reply; 63+ messages in thread
From: Alex Bennée @ 2019-02-25 16:22 UTC (permalink / raw)
  To: Emilio G. Cota; +Cc: qemu-devel, Richard Henderson, Pavel Dovgalyuk


Emilio G. Cota <cota@braap.org> writes:

> This is faster than removing elements one by one.
>
> Will gain a user soon.
>
> Signed-off-by: Emilio G. Cota <cota@braap.org>
> ---
>  include/qemu/queue.h | 10 ++++++++++
>  1 file changed, 10 insertions(+)
>
> diff --git a/include/qemu/queue.h b/include/qemu/queue.h
> index ac418efc43..0283c2dd7d 100644
> --- a/include/qemu/queue.h
> +++ b/include/qemu/queue.h
> @@ -419,6 +419,16 @@ struct {                                                                \
>          (elm)->field.tqe_prev = NULL;                                   \
>  } while (/*CONSTCOND*/0)
>
> +/* remove @left, @right and all elements in between from @head */
> +#define QTAILQ_REMOVE_SEVERAL(head, left, right, field) do {    \
> +        if (((right)->field.tqe_next) != NULL)                  \
> +            (right)->field.tqe_next->field.tqe_prev =           \
> +                (left)->field.tqe_prev;                         \
> +        else                                                    \
> +            (head)->tqh_last = (left)->field.tqe_prev;          \
> +        *(left)->field.tqe_prev = (right)->field.tqe_next;      \

This seems wrong, shouldn't it be:

  (left)->field.tqe_prev->field.tqe_next = (right)->field.tqe_next;

Although to be honest every time I read the QUEUE macros I end up with a headache...

> +    } while (/*CONSTCOND*/0)
> +
>  #define QTAILQ_FOREACH(var, head, field)                                \
>          for ((var) = ((head)->tqh_first);                               \
>                  (var);                                                  \


--
Alex Bennée

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [Qemu-devel] [RFC v2 07/38] queue: add QTAILQ_REMOVE_SEVERAL
  2019-02-25 16:22   ` Alex Bennée
@ 2019-02-25 18:02     ` Emilio G. Cota
  0 siblings, 0 replies; 63+ messages in thread
From: Emilio G. Cota @ 2019-02-25 18:02 UTC (permalink / raw)
  To: Alex Bennée; +Cc: qemu-devel, Richard Henderson, Pavel Dovgalyuk

On Mon, Feb 25, 2019 at 16:22:52 +0000, Alex Bennée wrote:
> Emilio G. Cota <cota@braap.org> writes:
> > +/* remove @left, @right and all elements in between from @head */
> > +#define QTAILQ_REMOVE_SEVERAL(head, left, right, field) do {    \
> > +        if (((right)->field.tqe_next) != NULL)                  \
> > +            (right)->field.tqe_next->field.tqe_prev =           \
> > +                (left)->field.tqe_prev;                         \
> > +        else                                                    \
> > +            (head)->tqh_last = (left)->field.tqe_prev;          \
> > +        *(left)->field.tqe_prev = (right)->field.tqe_next;      \
> 
> This seems wrong, shouldn't it be:
> 
>   (left)->field.tqe_prev->field.tqe_next = (right)->field.tqe_next;

That would make too much sense, wouldn't it!

Looking at QTAILQ_REMOVE is the easiest way to reason about this:

#define QTAILQ_REMOVE(head, elm, field) do {                            \
        if (((elm)->field.tqe_next) != NULL)                            \
                (elm)->field.tqe_next->field.tqe_prev =                 \
                    (elm)->field.tqe_prev;                              \
        else                                                            \
                (head)->tqh_last = (elm)->field.tqe_prev;               \
        *(elm)->field.tqe_prev = (elm)->field.tqe_next;                 \
        (elm)->field.tqe_prev = NULL;                                   \
} while (/*CONSTCOND*/0)

Imagine we're removing the only element in the list. Thus,
*(elm)->field.tqe_prev will be setting (head)->tqh_first (to NULL).
IOW, we can't assume that the previous "element" (whether true element
or head) has a .tqe_next field (the head has .thq_first); note that
tqe_prev is a **, not a *.

> Although to be honest every time I read the QUEUE macros I end up with a headache...

You're not alone.

BTW, this code had to change for v3 because of 7274f01bb8 ("qemu/queue.h:
reimplement QTAILQ without pointer-to-pointers", 2019-01-11)

I think you might like that implementation better, since it is a little
closer to sanity, i.e. kernel-style lists :-)
It uses a more predictable structure (*prev,*next) that is shoehorned
with a union to avoid changing the memory layout.

Thanks,

		Emilio

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [Qemu-devel] [RFC v2 31/38] target/xtensa: fetch code with translator_ld
  2019-02-25 14:54   ` Alex Bennée
@ 2019-03-04  2:36     ` Max Filippov
  0 siblings, 0 replies; 63+ messages in thread
From: Max Filippov @ 2019-03-04  2:36 UTC (permalink / raw)
  To: Alex Bennée
  Cc: Emilio G. Cota, Richard Henderson, qemu-devel, Pavel Dovgalyuk

On Mon, Feb 25, 2019 at 6:55 AM Alex Bennée <alex.bennee@linaro.org> wrote:
> Emilio G. Cota <cota@braap.org> writes:
> > Signed-off-by: Emilio G. Cota <cota@braap.org>
> > ---
> >  target/xtensa/translate.c | 4 ++--
> >  1 file changed, 2 insertions(+), 2 deletions(-)
> >
> > diff --git a/target/xtensa/translate.c b/target/xtensa/translate.c
> > index 46e1338448..c140742562 100644
> > --- a/target/xtensa/translate.c
> > +++ b/target/xtensa/translate.c
> > @@ -882,7 +882,7 @@ static inline unsigned xtensa_op0_insn_len(DisasContext *dc, uint8_t op0)
> >  static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc)
> >  {
> >      xtensa_isa isa = dc->config->isa;
> > -    unsigned char b[MAX_INSN_LENGTH] = {cpu_ldub_code(env, dc->pc)};
> > +    unsigned char b[MAX_INSN_LENGTH] = {translator_ldub(env, dc->pc)};
> >      unsigned len = xtensa_op0_insn_len(dc, b[0]);
> >      xtensa_format fmt;
> >      int slot, slots;
> > @@ -914,7 +914,7 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc)
> >                        dc->pc);
> >      }
> >      for (i = 1; i < len; ++i) {
> > -        b[i] = cpu_ldub_code(env, dc->pc + i);
> > +        b[i] = translator_ldub(env, dc->pc + i);
> >      }
> >      xtensa_insnbuf_from_chars(isa, dc->insnbuf, b, len);
> >      fmt = xtensa_format_decode(isa, dc->insnbuf);
>
> There is also:
>
>   static inline unsigned xtensa_insn_len(CPUXtensaState *env, DisasContext *dc)
>   {
>       uint8_t b0 = cpu_ldub_code(env, dc->pc);
>       return xtensa_op0_insn_len(dc, b0);
>   }
>
> Or is this usage a re-read of something we've already got?

FWIW xtensa_insn_len is used to look into the instruction that
follows the one that has just been translated, but it may only
touch the same page as the last translated instruction.

-- 
Thanks.
-- Max

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [Qemu-devel] [RFC v2 15/38] tcg: let plugins instrument memory accesses
  2019-01-24 14:39   ` Alex Bennée
@ 2019-05-16 15:06     ` Alex Bennée
  0 siblings, 0 replies; 63+ messages in thread
From: Alex Bennée @ 2019-05-16 15:06 UTC (permalink / raw)
  To: Emilio G. Cota; +Cc: Richard Henderson, qemu-devel, Pavel Dovgalyuk


Alex Bennée <alex.bennee@linaro.org> writes:

> Emilio G. Cota <cota@braap.org> writes:
>
>> XXX: store hostaddr from non-i386 TCG backends
>> XXX: what hostaddr to return for I/O accesses?
>> XXX: what hostaddr to return for cross-page accesses?
>
> Just a heads up this patch now clashes with changes that have been made
> to master.
<snip>

Now the softmmu work is in I re-based the series and fixed up these
clashes. You can find my tree at:

  https://github.com/stsquad/qemu/tree/review/plugin-review-v3-rebase

I'm just paging in context and getting back to the review now.

--
Alex Bennée


^ permalink raw reply	[flat|nested] 63+ messages in thread

* [Qemu-devel] [RFC PATCH] tests/tcg: enable plugin testing
  2018-12-09 19:37 [Qemu-devel] [RFC v2 00/38] Plugin support Emilio G. Cota
                   ` (37 preceding siblings ...)
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 38/38] tests/plugin: add sample plugins Emilio G. Cota
@ 2019-05-17 19:11 ` Alex Bennée
  38 siblings, 0 replies; 63+ messages in thread
From: Alex Bennée @ 2019-05-17 19:11 UTC (permalink / raw)
  To: cota
  Cc: Peter Maydell, qemu-devel, Philippe Mathieu-Daudé,
	open list:ARM, bobby.prani, Alex Bennée

If CONFIG_PLUGINS is enabled then lets enable testing for all our TCG
targets. This is a simple smoke test that ensure we don't crash or
otherwise barf out by running each plugin against each test.

There is a minor knock on effect for additional runners which need
specialised QEMU_OPTS which will also need to declare a plugin version
of the runner. If this gets onerous we might need to add another
helper.

Checking the results of the plugins is left for a later exercise.

Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
---
 tests/Makefile.include                | 10 +++++++-
 tests/tcg/Makefile                    | 34 +++++++++++++++++++++++++++
 tests/tcg/arm/Makefile.softmmu-target |  1 +
 3 files changed, 44 insertions(+), 1 deletion(-)

diff --git a/tests/Makefile.include b/tests/Makefile.include
index 792f685ac11..91d254cdca5 100644
--- a/tests/Makefile.include
+++ b/tests/Makefile.include
@@ -1037,6 +1037,14 @@ check-softfloat:
 		"SKIPPED for non-TCG builds")
 endif
 
+# Plugins
+ifeq ($(CONFIG_PLUGIN),y)
+plugins:
+	$(call quiet-command,\
+		$(MAKE) $(SUBDIR_MAKEFLAGS) -C tests/plugin V="$(V)", \
+		"BUILD", "plugins")
+endif
+
 # Per guest TCG tests
 
 BUILD_TCG_TARGET_RULES=$(patsubst %,build-tcg-tests-%, $(TARGET_DIRS))
@@ -1051,7 +1059,7 @@ $(foreach PROBE_TARGET,$(TARGET_DIRS), 				\
 		$(eval build-tcg-tests-$(PROBE_TARGET): $(DOCKER_PREREQ))))
 endif
 
-build-tcg-tests-%:
+build-tcg-tests-%: $(if $(CONFIG_PLUGIN),plugins)
 	$(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C $* V="$(V)" \
 		SKIP_DOCKER_BUILD=1 TARGET_DIR="$*/" guest-tests, \
 		"BUILD", "TCG tests for $*")
diff --git a/tests/tcg/Makefile b/tests/tcg/Makefile
index 6fa63cc8d53..9474ab29003 100644
--- a/tests/tcg/Makefile
+++ b/tests/tcg/Makefile
@@ -116,11 +116,37 @@ all: $(TESTS)
 #
 
 RUN_TESTS=$(patsubst %,run-%, $(TESTS))
+
+# If plugins exist also include those in the tests
+ifeq ($(CONFIG_PLUGIN),y)
+PLUGIN_DIR=../../tests/plugin
+VPATH+=$(PLUGIN_DIR)
+PLUGINS=$(notdir $(wildcard $(PLUGIN_DIR)/*.so))
+
+# We need to ensure expand the run-plugin-TEST-with-PLUGIN
+# pre-requistes manually here as we can't use stems to handle it. We
+# also add some special helpers the run-plugin- rules can use bellow.
+
+$(foreach p,$(PLUGINS), \
+	$(foreach t,$(TESTS),\
+		$(eval run-plugin-$(t)-with-$(p): $t $p) \
+		$(eval RUN_TESTS+=run-plugin-$(t)-with-$(p))))
+endif
+
+strip-plugin = $(wordlist 1, 1, $(subst -with-, ,$1))
+extract-plugin = $(wordlist 2, 2, $(subst -with-, ,$1))
+
 RUN_TESTS+=$(EXTRA_RUNS)
 
 ifdef CONFIG_USER_ONLY
 run-%: %
 	$(call run-test, $<, $(QEMU) $(QEMU_OPTS) $<, "$< on $(TARGET_NAME)")
+
+run-plugin-%:
+	$(call run-test, $@, $(QEMU) $(QEMU_OPTS) \
+		-plugin $(PLUGIN_DIR)/$(call extract-plugin,$@) \
+		 $(call strip-plugin,$<), \
+	"$< on $(TARGET_NAME)")
 else
 run-%: %
 	$(call run-test, $<, \
@@ -128,6 +154,14 @@ run-%: %
 		  -chardev file$(COMMA)path=$<.out$(COMMA)id=output \
 	   	  $(QEMU_OPTS) $<, \
 	  "$< on $(TARGET_NAME)")
+
+run-plugin-%:
+	$(call run-test, $@, \
+	  $(QEMU) -monitor none -display none \
+		  -chardev file$(COMMA)path=$@.out$(COMMA)id=output \
+	   	  -plugin $(PLUGIN_DIR)/$(call extract-plugin,$@) \
+	   	  $(QEMU_OPTS) $(call strip-plugin,$<), \
+	  "$< on $(TARGET_NAME)")
 endif
 
 gdb-%: %
diff --git a/tests/tcg/arm/Makefile.softmmu-target b/tests/tcg/arm/Makefile.softmmu-target
index 49d48d8a1c3..cd628306b3e 100644
--- a/tests/tcg/arm/Makefile.softmmu-target
+++ b/tests/tcg/arm/Makefile.softmmu-target
@@ -25,5 +25,6 @@ LDFLAGS+=-nostdlib -N -static
 test-armv6m-undef: EXTRA_CFLAGS+=-mcpu=cortex-m0
 
 run-test-armv6m-undef: QEMU_OPTS+=-semihosting -M microbit -kernel
+run-plugin-test-armv6m-undef-%: QEMU_OPTS+=-semihosting -M microbit -kernel
 
 endif
-- 
2.20.1



^ permalink raw reply related	[flat|nested] 63+ messages in thread

* Re: [Qemu-devel] [RFC v2 11/38] tcg: add tcg_gen_st_ptr
  2018-12-09 19:37 ` [Qemu-devel] [RFC v2 11/38] tcg: add tcg_gen_st_ptr Emilio G. Cota
@ 2019-05-20 13:36   ` Alex Bennée
  0 siblings, 0 replies; 63+ messages in thread
From: Alex Bennée @ 2019-05-20 13:36 UTC (permalink / raw)
  To: Emilio G. Cota; +Cc: Richard Henderson, qemu-devel, Pavel Dovgalyuk


Emilio G. Cota <cota@braap.org> writes:

> Will gain a user soon.
>
> Signed-off-by: Emilio G. Cota <cota@braap.org>

Reviewed-by: Alex Bennée <alex.bennee@linaro.org>

> ---
>  tcg/tcg-op.h | 5 +++++
>  1 file changed, 5 insertions(+)
>
> diff --git a/tcg/tcg-op.h b/tcg/tcg-op.h
> index e2948b10a2..d3c79a6cb2 100644
> --- a/tcg/tcg-op.h
> +++ b/tcg/tcg-op.h
> @@ -1219,6 +1219,11 @@ static inline void tcg_gen_ld_ptr(TCGv_ptr r, TCGv_ptr a, intptr_t o)
>      glue(tcg_gen_ld_,PTR)((NAT)r, a, o);
>  }
>
> +static inline void tcg_gen_st_ptr(TCGv_ptr r, TCGv_ptr a, intptr_t o)
> +{
> +    glue(tcg_gen_st_, PTR)((NAT)r, a, o);
> +}
> +
>  static inline void tcg_gen_discard_ptr(TCGv_ptr a)
>  {
>      glue(tcg_gen_discard_,PTR)((NAT)a);


--
Alex Bennée


^ permalink raw reply	[flat|nested] 63+ messages in thread

end of thread, other threads:[~2019-05-20 13:37 UTC | newest]

Thread overview: 63+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-12-09 19:37 [Qemu-devel] [RFC v2 00/38] Plugin support Emilio G. Cota
2018-12-09 19:37 ` [Qemu-devel] [RFC v2 01/38] trace: expand mem_info:size_shift to 3 bits Emilio G. Cota
2019-01-24 14:42   ` Alex Bennée
2018-12-09 19:37 ` [Qemu-devel] [RFC v2 02/38] tcg/README: fix typo s/afterwise/afterwards/ Emilio G. Cota
2018-12-09 19:37 ` [Qemu-devel] [RFC v2 03/38] cpu: introduce cpu_in_exclusive_work_context() Emilio G. Cota
2019-01-24 14:44   ` Alex Bennée
2018-12-09 19:37 ` [Qemu-devel] [RFC v2 04/38] translate-all: use cpu_in_exclusive_work_context() in tb_flush Emilio G. Cota
2019-01-24 14:44   ` Alex Bennée
2018-12-09 19:37 ` [Qemu-devel] [RFC v2 05/38] plugin: add user-facing API Emilio G. Cota
2018-12-14 15:57   ` Aaron Lindsay
2018-12-14 16:04     ` Aaron Lindsay
2018-12-14 17:08     ` Emilio G. Cota
2018-12-14 17:50       ` Emilio G. Cota
2018-12-14 18:47         ` Aaron Lindsay
2018-12-14 19:40           ` Emilio G. Cota
2018-12-14 17:59       ` Aaron Lindsay
2018-12-14 18:23         ` Emilio G. Cota
2018-12-09 19:37 ` [Qemu-devel] [RFC v2 06/38] plugin: add core code Emilio G. Cota
2018-12-10 11:37   ` Pavel Dovgalyuk
2018-12-10 17:40     ` Emilio G. Cota
2019-01-24 15:57   ` Alex Bennée
2018-12-09 19:37 ` [Qemu-devel] [RFC v2 07/38] queue: add QTAILQ_REMOVE_SEVERAL Emilio G. Cota
2019-02-25 16:22   ` Alex Bennée
2019-02-25 18:02     ` Emilio G. Cota
2018-12-09 19:37 ` [Qemu-devel] [RFC v2 08/38] tcg: drop nargs from tcg_op_insert_{before, after} Emilio G. Cota
2018-12-13 23:52   ` Richard Henderson
2018-12-09 19:37 ` [Qemu-devel] [RFC v2 09/38] cputlb: introduce get_page_addr_code_hostp Emilio G. Cota
2019-01-24 14:51   ` Alex Bennée
2018-12-09 19:37 ` [Qemu-devel] [RFC v2 10/38] plugin-gen: add module for TCG-related code Emilio G. Cota
2018-12-09 19:37 ` [Qemu-devel] [RFC v2 11/38] tcg: add tcg_gen_st_ptr Emilio G. Cota
2019-05-20 13:36   ` Alex Bennée
2018-12-09 19:37 ` [Qemu-devel] [RFC v2 12/38] tcg: add MO_HADDR to TCGMemOp Emilio G. Cota
2018-12-09 19:37 ` [Qemu-devel] [RFC v2 13/38] atomic_template: fix indentation in GEN_ATOMIC_HELPER Emilio G. Cota
2018-12-09 19:37 ` [Qemu-devel] [RFC v2 14/38] atomic_template: add inline trace/plugin helpers Emilio G. Cota
2018-12-09 19:37 ` [Qemu-devel] [RFC v2 15/38] tcg: let plugins instrument memory accesses Emilio G. Cota
2019-01-24 14:39   ` Alex Bennée
2019-05-16 15:06     ` Alex Bennée
2018-12-09 19:37 ` [Qemu-devel] [RFC v2 16/38] translate-all: notify plugin code of tb_flush Emilio G. Cota
2018-12-09 19:37 ` [Qemu-devel] [RFC v2 17/38] *-user: notify plugin of exit Emilio G. Cota
2018-12-09 19:37 ` [Qemu-devel] [RFC v2 18/38] *-user: plugin syscalls Emilio G. Cota
2018-12-09 19:37 ` [Qemu-devel] [RFC v2 19/38] cpu: hook plugin vcpu events Emilio G. Cota
2018-12-09 19:37 ` [Qemu-devel] [RFC v2 20/38] plugin-gen: add plugin_insn_append Emilio G. Cota
2018-12-09 19:37 ` [Qemu-devel] [RFC v2 21/38] translator: add translator_ld{ub, sw, uw, l, q} Emilio G. Cota
2018-12-09 19:37 ` [Qemu-devel] [RFC v2 22/38] target/arm: call qemu_plugin_insn_append Emilio G. Cota
2018-12-09 19:37 ` [Qemu-devel] [RFC v2 23/38] target/ppc: fetch code with translator_ld Emilio G. Cota
2018-12-09 19:37 ` [Qemu-devel] [RFC v2 24/38] target/sh4: fetch code with translator_ld (WIP) Emilio G. Cota
2018-12-09 19:37 ` [Qemu-devel] [RFC v2 25/38] target/i386: fetch code with translator_ld Emilio G. Cota
2018-12-09 19:37 ` [Qemu-devel] [RFC v2 26/38] target/hppa: " Emilio G. Cota
2018-12-09 19:37 ` [Qemu-devel] [RFC v2 27/38] target/m68k: " Emilio G. Cota
2018-12-09 19:37 ` [Qemu-devel] [RFC v2 28/38] target/alpha: " Emilio G. Cota
2018-12-09 19:37 ` [Qemu-devel] [RFC v2 29/38] target/riscv: " Emilio G. Cota
2018-12-09 19:37 ` [Qemu-devel] [RFC v2 30/38] target/sparc: " Emilio G. Cota
2018-12-09 19:37 ` [Qemu-devel] [RFC v2 31/38] target/xtensa: " Emilio G. Cota
2019-02-25 14:54   ` Alex Bennée
2019-03-04  2:36     ` Max Filippov
2018-12-09 19:37 ` [Qemu-devel] [RFC v2 32/38] target/openrisc: " Emilio G. Cota
2018-12-09 19:37 ` [Qemu-devel] [RFC v2 33/38] translator: inject instrumentation from plugins Emilio G. Cota
2018-12-09 19:37 ` [Qemu-devel] [RFC v2 34/38] plugin: add API symbols to qemu-plugins.symbols Emilio G. Cota
2018-12-09 19:37 ` [Qemu-devel] [RFC v2 35/38] configure: add --enable-plugins Emilio G. Cota
2018-12-09 19:37 ` [Qemu-devel] [RFC v2 36/38] vl: support -plugin option Emilio G. Cota
2018-12-09 19:37 ` [Qemu-devel] [RFC v2 37/38] linux-user: " Emilio G. Cota
2018-12-09 19:37 ` [Qemu-devel] [RFC v2 38/38] tests/plugin: add sample plugins Emilio G. Cota
2019-05-17 19:11 ` [Qemu-devel] [RFC PATCH] tests/tcg: enable plugin testing Alex Bennée

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.