qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH  v3 00/50] tcg plugin support
@ 2019-06-14 17:11 Alex Bennée
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 01/50] trace: expand mem_info:size_shift to 3 bits Alex Bennée
                   ` (53 more replies)
  0 siblings, 54 replies; 117+ messages in thread
From: Alex Bennée @ 2019-06-14 17:11 UTC (permalink / raw)
  To: qemu-devel; +Cc: Alex Bennée

Hi,

This is v3 of the proposed plugins API for QEMU. As Emilio is busy
having finished his time at Columbia I have volunteered to take the
patch series forward. Emilio's RFC v2 was posted last year:

  Subject: [RFC v2 00/38] Plugin support
  Date: Sun,  9 Dec 2018 14:37:11 -0500
  Message-Id: <20181209193749.12277-1-cota@braap.org>

The idea is to present a plugin interface that allows for interesting
experiments to be written while not exposing the internal details of
how the TCG works to the wider world.

Changes from last year

  - re-basing and fixing up conflicts
  - moved to plugins/ and split files up (core/api/loader)
  - added a design document (docs/devel/plugins.rst)
  - replaced auto-growing structures with glib GArray and friends
  - expanded API to include access to disassembly
  - wrote some more example plugins
  - dropped the RFC tag ;-)

There are a few things left to sort out. Currently the haddr
calculation relies on the softmmu TLB code for each architecture to
dump it in a cpu_env location. Currently this is only done on x86 and
we would require some tweaking to each backend. I think it would
probably be better to just have a wrapper helper which could do the
lookup of the (hot) TLB in C to calculate the address before calling
the helpers.

I also ended up adding a bunch of miscellaneous fixes at the end while
I was re-spinning. If they get some review I'll try and get the
non-controversial stuff up-streamed first.

The area that needs the most review is the single pass TCG op
insertion code. When plugins are enabled each instruction gets dummy
ops inserted and then at the end we swap those out for the actual
calls or remove them from the stream. Hopefully this is not too much
of an abuse of TCG mechanisms.

I'm aiming to get this in for 4.1 but I guess that will depend on how
the review and iteration goes.

Alex Bennée (12):
  docs/devel: add plugins.rst design document
  configure: add --enable-plugins (MOVE TO END)
  plugin: add implementation of the api
  tests/tcg: enable plugin testing
  tests/plugin: add a hotblocks plugin
  plugin: add qemu_plugin_insn_disas helper
  tests/plugin: add instruction execution breakdown
  tests/plugin: add hotpages plugin to breakdown memory access patterns
  accel/stubs: reduce headers from tcg-stub
  include/exec: wrap cpu_ldst.h in CONFIG_TCG
  include/exec/cpu-defs.h: fix typo
  TODO: API changes to make?

Emilio G. Cota (36):
  trace: expand mem_info:size_shift to 3 bits
  tcg/README: fix typo s/afterwise/afterwards/
  cpu: introduce cpu_in_exclusive_work_context()
  translate-all: use cpu_in_exclusive_work_context() in tb_flush
  plugin: add user-facing API
  plugin: add core code
  queue: add QTAILQ_REMOVE_SEVERAL
  cputlb: document get_page_addr_code
  cputlb: introduce get_page_addr_code_hostp
  plugin-gen: add module for TCG-related code
  tcg: add tcg_gen_st_ptr
  tcg: add MO_HADDR to TCGMemOp
  atomic_template: fix indentation in GEN_ATOMIC_HELPER
  atomic_template: add inline trace/plugin helpers
  cpu_ldst_useronly_template: remove redundant #ifndef CODE_ACCESS
  tcg: let plugins instrument memory accesses
  translate-all: notify plugin code of tb_flush
  *-user: notify plugin of exit
  *-user: plugin syscalls
  cpu: hook plugin vcpu events
  plugin-gen: add plugin_insn_append
  translator: add translator_ld{ub,sw,uw,l,q}
  target/arm: call qemu_plugin_insn_append
  target/ppc: fetch code with translator_ld
  target/sh4: fetch code with translator_ld
  target/i386: fetch code with translator_ld
  target/hppa: fetch code with translator_ld
  target/m68k: fetch code with translator_ld
  target/alpha: fetch code with translator_ld
  target/riscv: fetch code with translator_ld
  target/sparc: fetch code with translator_ld
  target/xtensa: fetch code with translator_ld
  target/openrisc: fetch code with translator_ld
  translator: inject instrumentation from plugins
  plugin: add API symbols to qemu-plugins.symbols
  tests/plugin: add sample plugins

Lluís Vilanova (2):
  vl: support -plugin option
  linux-user: support -plugin option

 Makefile                                  |   11 +-
 Makefile.target                           |    2 +
 accel/stubs/tcg-stub.c                    |    3 -
 accel/tcg/Makefile.objs                   |    1 +
 accel/tcg/atomic_template.h               |  117 ++-
 accel/tcg/cpu-exec.c                      |    3 +
 accel/tcg/cputlb.c                        |   56 +-
 accel/tcg/plugin-gen.c                    | 1079 +++++++++++++++++++++
 accel/tcg/plugin-helpers.h                |    6 +
 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 +
 disas.c                                   |  103 ++
 docs/devel/index.rst                      |    1 +
 docs/devel/plugins.rst                    |   99 ++
 exec.c                                    |    2 +
 include/disas/disas.h                     |    2 +
 include/exec/cpu-defs.h                   |   11 +-
 include/exec/cpu_ldst.h                   |    9 +
 include/exec/cpu_ldst_template.h          |   40 +-
 include/exec/cpu_ldst_useronly_template.h |   36 +-
 include/exec/exec-all.h                   |   64 +-
 include/exec/helper-gen.h                 |    1 +
 include/exec/helper-proto.h               |    1 +
 include/exec/helper-tcg.h                 |    1 +
 include/exec/plugin-gen.h                 |   71 ++
 include/exec/translator.h                 |   30 +-
 include/qemu/plugin.h                     |  261 +++++
 include/qemu/qemu-plugin.h                |  349 +++++++
 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 +
 plugins/.gitignore                        |    2 +
 plugins/Makefile.objs                     |   21 +
 plugins/api.c                             |  274 ++++++
 plugins/core.c                            |  524 ++++++++++
 plugins/loader.c                          |  353 +++++++
 plugins/plugin.h                          |   95 ++
 plugins/qemu-plugins.symbols              |   36 +
 qemu-options.hx                           |   17 +
 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                    |    4 +-
 target/sparc/translate.c                  |    2 +-
 target/xtensa/translate.c                 |    4 +-
 tcg/README                                |    2 +-
 tcg/i386/tcg-target.inc.c                 |    8 +
 tcg/tcg-op.c                              |   40 +-
 tcg/tcg-op.h                              |   16 +
 tcg/tcg-opc.h                             |    3 +
 tcg/tcg.c                                 |   22 +
 tcg/tcg.h                                 |   28 +
 tests/Makefile.include                    |   10 +-
 tests/plugin/Makefile                     |   31 +
 tests/plugin/bb.c                         |   66 ++
 tests/plugin/empty.c                      |   30 +
 tests/plugin/hotblocks.c                  |  145 +++
 tests/plugin/hotpages.c                   |  152 +++
 tests/plugin/howvec.c                     |  297 ++++++
 tests/plugin/insn.c                       |   63 ++
 tests/plugin/mem.c                        |   93 ++
 tests/tcg/Makefile                        |   34 +
 tests/tcg/arm/Makefile.softmmu-target     |    1 +
 trace-events                              |    2 +-
 vl.c                                      |   11 +
 78 files changed, 4855 insertions(+), 122 deletions(-)
 create mode 100644 accel/tcg/plugin-gen.c
 create mode 100644 accel/tcg/plugin-helpers.h
 create mode 100644 docs/devel/plugins.rst
 create mode 100644 include/exec/plugin-gen.h
 create mode 100644 include/qemu/plugin.h
 create mode 100644 include/qemu/qemu-plugin.h
 create mode 100644 plugins/.gitignore
 create mode 100644 plugins/Makefile.objs
 create mode 100644 plugins/api.c
 create mode 100644 plugins/core.c
 create mode 100644 plugins/loader.c
 create mode 100644 plugins/plugin.h
 create mode 100644 plugins/qemu-plugins.symbols
 create mode 100644 tests/plugin/Makefile
 create mode 100644 tests/plugin/bb.c
 create mode 100644 tests/plugin/empty.c
 create mode 100644 tests/plugin/hotblocks.c
 create mode 100644 tests/plugin/hotpages.c
 create mode 100644 tests/plugin/howvec.c
 create mode 100644 tests/plugin/insn.c
 create mode 100644 tests/plugin/mem.c

-- 
2.20.1



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

* [Qemu-devel] [PATCH v3 01/50] trace: expand mem_info:size_shift to 3 bits
  2019-06-14 17:11 [Qemu-devel] [PATCH v3 00/50] tcg plugin support Alex Bennée
@ 2019-06-14 17:11 ` Alex Bennée
  2019-06-17  2:12   ` Richard Henderson
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 02/50] tcg/README: fix typo s/afterwise/afterwards/ Alex Bennée
                   ` (52 subsequent siblings)
  53 siblings, 1 reply; 117+ messages in thread
From: Alex Bennée @ 2019-06-14 17:11 UTC (permalink / raw)
  To: qemu-devel; +Cc: Emilio G. Cota, Alex Bennée, Stefan Hajnoczi

From: "Emilio G. Cota" <cota@braap.org>

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

Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
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 844ee58dd9..037169aab3 100644
--- a/trace-events
+++ b/trace-events
@@ -159,7 +159,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.20.1



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

* [Qemu-devel] [PATCH v3 02/50] tcg/README: fix typo s/afterwise/afterwards/
  2019-06-14 17:11 [Qemu-devel] [PATCH v3 00/50] tcg plugin support Alex Bennée
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 01/50] trace: expand mem_info:size_shift to 3 bits Alex Bennée
@ 2019-06-14 17:11 ` Alex Bennée
  2019-06-17  2:13   ` Richard Henderson
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 03/50] cpu: introduce cpu_in_exclusive_work_context() Alex Bennée
                   ` (51 subsequent siblings)
  53 siblings, 1 reply; 117+ messages in thread
From: Alex Bennée @ 2019-06-14 17:11 UTC (permalink / raw)
  To: qemu-devel; +Cc: Emilio G. Cota, Alex Bennée, Richard Henderson

From: "Emilio G. Cota" <cota@braap.org>

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 21fcdf737f..ef9be5ba90 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.20.1



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

* [Qemu-devel] [PATCH v3 03/50] cpu: introduce cpu_in_exclusive_work_context()
  2019-06-14 17:11 [Qemu-devel] [PATCH v3 00/50] tcg plugin support Alex Bennée
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 01/50] trace: expand mem_info:size_shift to 3 bits Alex Bennée
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 02/50] tcg/README: fix typo s/afterwise/afterwards/ Alex Bennée
@ 2019-06-14 17:11 ` Alex Bennée
  2019-06-17  2:15   ` Richard Henderson
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 04/50] translate-all: use cpu_in_exclusive_work_context() in tb_flush Alex Bennée
                   ` (50 subsequent siblings)
  53 siblings, 1 reply; 117+ messages in thread
From: Alex Bennée @ 2019-06-14 17:11 UTC (permalink / raw)
  To: qemu-devel; +Cc: Emilio G. Cota, Alex Bennée

From: "Emilio G. Cota" <cota@braap.org>

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

diff --git a/cpus-common.c b/cpus-common.c
index 3ca58c64e8..960058457a 100644
--- a/cpus-common.c
+++ b/cpus-common.c
@@ -335,7 +335,9 @@ void process_queued_cpu_work(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();
             qemu_mutex_lock_iothread();
         } else {
diff --git a/include/qom/cpu.h b/include/qom/cpu.h
index 5ee0046b62..08481ad304 100644
--- a/include/qom/cpu.h
+++ b/include/qom/cpu.h
@@ -373,6 +373,7 @@ struct CPUState {
     bool unplug;
     bool crash_occurred;
     bool exit_request;
+    bool in_exclusive_work_context;
     uint32_t cflags_next_tb;
     /* updates protected by BQL */
     uint32_t interrupt_request;
@@ -785,6 +786,18 @@ void async_run_on_cpu(CPUState *cpu, run_on_cpu_func func, run_on_cpu_data data)
  */
 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.
-- 
2.20.1



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

* [Qemu-devel] [PATCH v3 04/50] translate-all: use cpu_in_exclusive_work_context() in tb_flush
  2019-06-14 17:11 [Qemu-devel] [PATCH v3 00/50] tcg plugin support Alex Bennée
                   ` (2 preceding siblings ...)
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 03/50] cpu: introduce cpu_in_exclusive_work_context() Alex Bennée
@ 2019-06-14 17:11 ` Alex Bennée
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 05/50] docs/devel: add plugins.rst design document Alex Bennée
                   ` (49 subsequent siblings)
  53 siblings, 0 replies; 117+ messages in thread
From: Alex Bennée @ 2019-06-14 17:11 UTC (permalink / raw)
  To: qemu-devel
  Cc: Paolo Bonzini, Emilio G. Cota, Alex Bennée, Richard Henderson

From: "Emilio G. Cota" <cota@braap.org>

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>
Reviewed-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 5d1e08b169..54998e39c6 100644
--- a/accel/tcg/translate-all.c
+++ b/accel/tcg/translate-all.c
@@ -1268,8 +1268,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.20.1



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

* [Qemu-devel] [PATCH v3 05/50] docs/devel: add plugins.rst design document
  2019-06-14 17:11 [Qemu-devel] [PATCH v3 00/50] tcg plugin support Alex Bennée
                   ` (3 preceding siblings ...)
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 04/50] translate-all: use cpu_in_exclusive_work_context() in tb_flush Alex Bennée
@ 2019-06-14 17:11 ` Alex Bennée
  2019-06-19  3:34   ` Pranith Kumar
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 06/50] configure: add --enable-plugins (MOVE TO END) Alex Bennée
                   ` (48 subsequent siblings)
  53 siblings, 1 reply; 117+ messages in thread
From: Alex Bennée @ 2019-06-14 17:11 UTC (permalink / raw)
  To: qemu-devel; +Cc: Alex Bennée

This is mostly extracted from Emilio's more verbose commit comments
with some additional verbiage from me.

Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
---
 docs/devel/index.rst   |  1 +
 docs/devel/plugins.rst | 99 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 100 insertions(+)
 create mode 100644 docs/devel/plugins.rst

diff --git a/docs/devel/index.rst b/docs/devel/index.rst
index 2a4ddf40ad..7e6d20c970 100644
--- a/docs/devel/index.rst
+++ b/docs/devel/index.rst
@@ -21,3 +21,4 @@ Contents:
    testing
    decodetree
    secure-coding-practices
+   plugins
diff --git a/docs/devel/plugins.rst b/docs/devel/plugins.rst
new file mode 100644
index 0000000000..b0c30375ef
--- /dev/null
+++ b/docs/devel/plugins.rst
@@ -0,0 +1,99 @@
+..
+   Copyright (C) 2017, Emilio G. Cota <cota@braap.org>
+   Copyright (c) 2019, Linaro Limited
+   Written by Emilio Cota and Alex Bennée
+
+================
+QEMU TCG Plugins
+================
+
+QEMU TCG plugins provide a way for users to run experiments taking
+advantage of the total system control emulation can have over a guest.
+It provides a mechanism for plugins to subscribe to events during
+translation and execution and optionally callback into the plugin
+during these events.
+
+API Stability
+=============
+
+This is a new feature for QEMU and it does allow people to develop
+out-of-tree plugins than can be dynamically linked into a running QEMU
+process. However the project reserves the right to change or break the
+API should it need to do so.
+
+Exposure of QEMU internals
+--------------------------
+
+The plugin architecture actively avoids leaking implementation details
+about how QEMU's translation works to the plugins. While there are
+conceptions such as translation time and translation blocks the
+details are opaque to plugins. The plugin is able to query select
+details of instructions and system configuration only through the
+exported *qemu_plugin* functions. The types used to describe
+instructions and events are opaque to the plugins themselves.
+
+Usage
+=====
+
+The QEMU binary needs to be compiled for plugin support:
+
+::
+    configure --enable-plugins
+
+Once built a program can be run with multiple plugins loaded each with
+their own arguments:
+
+::
+    $QEMU $OTHER_QEMU_ARGS \
+      -plugin tests/plugin/libhowvec.so,arg=inline,arg=hint \
+      -plugin tests/plugin/libhotblocks.so
+
+Plugin Life cycle
+=================
+
+First the plugin is loaded and the public qemu_plugin_install function
+is called. The plugin with then register callbacks for various plugin
+events. Generally at least the atexit_cb is registered so the plugin
+can dump its information at the end of a run.
+
+When a registered event occurs the plugin callback is called. The
+callbacks may provide additional information. In the case of a
+translation event the plugin has an option to enumerate the
+instructions in a block of instructions and optionally register
+callbacks to some or all instructions when they are executed.
+
+There is also a facility to add an inline event where code to
+increment a counter can be directly inlined with the translation.
+Currently only a simple increment is supported. This is not atomic so
+the plugin must either keep it's counters separated and indexed by CPU
+or use a callback which can ensure atomicity.
+
+Finally when QEMU exits all the registered atexit callbacks are called
+
+Internals
+=========
+
+Locking
+-------
+
+We have to ensure we cannot deadlock, particularly under MTTCG. For
+this we acquire a lock when called from plugin code. We also keep the
+list of callbacks under RCU 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.
+
+As a result 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.
+
+We support the uninstallation of a plugin at any time (e.g. from plugin
+callbacks). This means some callbacks might still be called after the uninstall
+function returns. The plugin isn't completely uninstalled until the
+safe work has executed while all vCPUs are quiescent.
-- 
2.20.1



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

* [Qemu-devel] [PATCH v3 06/50] configure: add --enable-plugins (MOVE TO END)
  2019-06-14 17:11 [Qemu-devel] [PATCH v3 00/50] tcg plugin support Alex Bennée
                   ` (4 preceding siblings ...)
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 05/50] docs/devel: add plugins.rst design document Alex Bennée
@ 2019-06-14 17:11 ` Alex Bennée
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 07/50] plugin: add user-facing API Alex Bennée
                   ` (47 subsequent siblings)
  53 siblings, 0 replies; 117+ messages in thread
From: Alex Bennée @ 2019-06-14 17:11 UTC (permalink / raw)
  To: qemu-devel; +Cc: Alex Bennée

This adds the basic boilerplate feature enable option for the build.
We shall expand it later.

XXX: currently this patch is included at the start of development to
aid with incremental building. It should be moved to the end once the
plugins are feature complete.

[AJB: split from larger patch]
Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
---
 configure | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/configure b/configure
index b091b82cb3..48f3263e55 100755
--- a/configure
+++ b/configure
@@ -493,6 +493,7 @@ docker="no"
 debug_mutex="no"
 libpmem=""
 default_devices="yes"
+plugins="no"
 
 # cross compilers defaults, can be overridden with --cross-cc-ARCH
 cross_cc_aarch64="aarch64-linux-gnu-gcc"
@@ -1537,6 +1538,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"
@@ -1734,6 +1739,8 @@ Advanced options (experts only):
   --enable-profiler        profiler support
   --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:
@@ -6479,6 +6486,7 @@ echo "docker            $docker"
 echo "libpmem support   $libpmem"
 echo "libudev           $libudev"
 echo "default devices   $default_devices"
+echo "plugin support    $plugins"
 
 if test "$supported_cpu" = "no"; then
     echo
@@ -7296,6 +7304,11 @@ if test "$sheepdog" = "yes" ; then
   echo "CONFIG_SHEEPDOG=y" >> $config_host_mak
 fi
 
+if test "$plugins" = "yes" ; then
+    echo "CONFIG_PLUGIN=y" >> $config_host_mak
+    LIBS="-ldl $LIBS"
+fi
+
 if test "$tcg_interpreter" = "yes"; then
   QEMU_INCLUDES="-iquote \$(SRC_PATH)/tcg/tci $QEMU_INCLUDES"
 elif test "$ARCH" = "sparc64" ; then
-- 
2.20.1



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

* [Qemu-devel] [PATCH  v3 07/50] plugin: add user-facing API
  2019-06-14 17:11 [Qemu-devel] [PATCH v3 00/50] tcg plugin support Alex Bennée
                   ` (5 preceding siblings ...)
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 06/50] configure: add --enable-plugins (MOVE TO END) Alex Bennée
@ 2019-06-14 17:11 ` Alex Bennée
  2019-06-19  3:34   ` Pranith Kumar
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 08/50] plugin: add core code Alex Bennée
                   ` (46 subsequent siblings)
  53 siblings, 1 reply; 117+ messages in thread
From: Alex Bennée @ 2019-06-14 17:11 UTC (permalink / raw)
  To: qemu-devel; +Cc: Emilio G. Cota, Alex Bennée

From: "Emilio G. Cota" <cota@braap.org>

Add the API first to ease review.

Signed-off-by: Emilio G. Cota <cota@braap.org>
Signed-off-by: Alex Bennée <alex.bennee@linaro.org>

---
v3
  - merge in changes to plugin install/reset/uninstall
  - split api file
---
 include/qemu/qemu-plugin.h | 339 +++++++++++++++++++++++++++++++++++++
 1 file changed, 339 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..0db1ef9714
--- /dev/null
+++ b/include/qemu/qemu-plugin.h
@@ -0,0 +1,339 @@
+/*
+ * Copyright (C) 2017, Emilio G. Cota <cota@braap.org>
+ * Copyright (C) 2019, Linaro
+ *
+ * License: GNU GPL, version 2 or later.
+ *   See the COPYING file in the top-level directory.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#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: Calling qemu_plugin_uninstall() from this function is a bug. To raise
+ * an error during install, return !0.
+ *
+ * Note: @argv remains valid throughout the lifetime of the loaded plugin.
+ */
+QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, int argc,
+                                           char **argv);
+
+/*
+ * Prototypes for the various callback styles we will be registering
+ * in the following functions.
+ */
+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_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 receives callbacks until @cb is called.
+ *
+ * Note: Calling this function from qemu_plugin_install() is a bug.
+ */
+void qemu_plugin_uninstall(qemu_plugin_id_t id, qemu_plugin_simple_cb_t cb);
+
+/**
+ * qemu_plugin_reset() - Reset a plugin
+ * @id: this plugin's opaque ID
+ * @cb: callback to be called once the plugin has been reset
+ *
+ * Unregisters all callbacks for the plugin given by @id.
+ *
+ * Do NOT assume that the plugin has been reset once this function returns.
+ * Plugins are reset asynchronously, and therefore the given plugin receives
+ * callbacks until @cb is called.
+ */
+void qemu_plugin_reset(qemu_plugin_id_t id, qemu_plugin_simple_cb_t cb);
+
+/**
+ * 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);
+
+/**
+ * qemu_plugin_register_vcpu_idle_cb() - register a vCPU idle callback
+ * @id: plugin ID
+ * @cb: callback function
+ *
+ * The @cb function is called every time a vCPU idles.
+ */
+void qemu_plugin_register_vcpu_idle_cb(qemu_plugin_id_t id,
+                                       qemu_plugin_vcpu_simple_cb_t cb);
+
+/**
+ * qemu_plugin_register_vcpu_resume_cb() - register a vCPU resume callback
+ * @id: plugin ID
+ * @cb: callback function
+ *
+ * The @cb function is called every time a vCPU resumes execution.
+ */
+void qemu_plugin_register_vcpu_resume_cb(qemu_plugin_id_t id,
+                                         qemu_plugin_vcpu_simple_cb_t cb);
+
+/*
+ * Opaque types that the plugin is given during the translation and
+ * instrumentation phase.
+ */
+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,
+};
+
+/**
+ * qemu_plugin_register_vcpu_tb_trans_cb() - register a translate cb
+ * @id: plugin ID
+ * @cb: callback function
+ *
+ * The @cb function is called every time a translation occurs. The @cb
+ * function is passed an opaque qemu_plugin_type which is can query
+ * for additional information including the list of translated
+ * instructions. At this point the plugin can register further
+ * callbacks to be triggered when the block or individual instruction
+ * executes.
+ */
+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);
+
+/**
+ * qemu_plugin_register_vcpu_tb_trans_exec_cb() - register execution callback
+ * @tb: the opaque qemu_plugin_tb handle for the translation
+ * @cb: callback function
+ * @flags: does the plugin read or write the CPU's registers?
+ * @userdata: any plugin data to pass to the @cb?
+ *
+ * The @cb function is called every time a translated unit executes.
+ */
+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,
+};
+
+/**
+ * qemu_plugin_register_vcpu_tb_trans_exec_inline() - execution inline op
+ * @tb: the opaque qemu_plugin_tb handle for the translation
+ * @op: the type of qemu_plugin_op (e.g. ADD_U64)
+ * @ptr: the target memory location for the op
+ * @imm: the op data (e.g. 1)
+ *
+ * Insert an inline op to every time a translated unit executes.
+ * Useful if you just want to increment a single counter somewhere in
+ * memory.
+ */
+void qemu_plugin_register_vcpu_tb_exec_inline(struct qemu_plugin_tb *tb,
+                                              enum qemu_plugin_op op,
+                                              void *ptr, uint64_t imm);
+
+/**
+ * qemu_plugin_register_vcpu_insn_exec_cb() - register insn execution cb
+ * @insn: the opaque qemu_plugin_insn handle for an instruction
+ * @cb: callback function
+ * @flags: does the plugin read or write the CPU's registers?
+ * @userdata: any plugin data to pass to the @cb?
+ *
+ * The @cb function is called every time an instruction is executed
+ */
+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);
+
+/**
+ * qemu_plugin_register_vcpu_insn_exec_inline() - insn execution inline op
+ * @insn: the opaque qemu_plugin_insn handle for an instruction
+ * @cb: callback function
+ * @op: the type of qemu_plugin_op (e.g. ADD_U64)
+ * @ptr: the target memory location for the op
+ * @imm: the op data (e.g. 1)
+ *
+ * Insert an inline op to every time an instruction executes. Useful
+ * if you just want to increment a single counter somewhere in memory.
+ */
+void qemu_plugin_register_vcpu_insn_exec_inline(struct qemu_plugin_insn *insn,
+                                                enum qemu_plugin_op op,
+                                                void *ptr, uint64_t imm);
+
+/*
+ * Helpers to query information about the instructions in a block
+ */
+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);
+
+/*
+ * Memory Instrumentation
+ */
+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);
+
+
+/**
+ * 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.20.1



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

* [Qemu-devel] [PATCH  v3 08/50] plugin: add core code
  2019-06-14 17:11 [Qemu-devel] [PATCH v3 00/50] tcg plugin support Alex Bennée
                   ` (6 preceding siblings ...)
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 07/50] plugin: add user-facing API Alex Bennée
@ 2019-06-14 17:11 ` Alex Bennée
       [not found]   ` <CADYwmhGiU_1GrBrR_tzBx+Lw+Hs3=Hi3AoPxRwkEj2pv9awqUg@mail.gmail.com>
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 09/50] plugin: add implementation of the api Alex Bennée
                   ` (45 subsequent siblings)
  53 siblings, 1 reply; 117+ messages in thread
From: Alex Bennée @ 2019-06-14 17:11 UTC (permalink / raw)
  To: qemu-devel; +Cc: Emilio G. Cota, Alex Bennée

From: "Emilio G. Cota" <cota@braap.org>

Signed-off-by: Emilio G. Cota <cota@braap.org>
[AJB: moved directory and merged various fixes]
Signed-off-by: Alex Bennée <alex.bennee@linaro.org>

---
v3
  - moved to plugins/
  - merged
    plugin: remove uninstall_cb_t and just use simple_cb_t
    plugin: handle uninstall when !current_cpu
    plugin: introduce qemu_plugin_reset
  - split into core/loader
  - use GArray/GByteArray/GPtrArray instead of homegrown funcs
---
 Makefile              |  10 +-
 Makefile.target       |   2 +
 include/qemu/plugin.h | 261 +++++++++++++++++++++
 include/qom/cpu.h     |   6 +
 plugins/Makefile.objs |   6 +
 plugins/core.c        | 524 ++++++++++++++++++++++++++++++++++++++++++
 plugins/loader.c      | 353 ++++++++++++++++++++++++++++
 plugins/plugin.h      |  95 ++++++++
 8 files changed, 1256 insertions(+), 1 deletion(-)
 create mode 100644 include/qemu/plugin.h
 create mode 100644 plugins/Makefile.objs
 create mode 100644 plugins/core.c
 create mode 100644 plugins/loader.c
 create mode 100644 plugins/plugin.h

diff --git a/Makefile b/Makefile
index 8e2fc6624c..37d396ab88 100644
--- a/Makefile
+++ b/Makefile
@@ -827,8 +827,13 @@ endif
 
 ICON_SIZES=16x16 24x24 32x32 48x48 64x64 128x128 256x256 512x512
 
-install: all $(if $(BUILD_DOCS),install-doc) install-datadir install-localstatedir \
+install-includedir:
+	$(INSTALL_DIR) "$(DESTDIR)$(includedir)"
+
+install: all $(if $(BUILD_DOCS),install-doc) \
+	install-datadir install-localstatedir install-includedir \
 	$(if $(INSTALL_BLOBS),$(edk2-decompressed))
+
 ifneq ($(TOOLS),)
 	$(call install-prog,$(subst qemu-ga,qemu-ga$(EXESUF),$(TOOLS)),$(DESTDIR)$(bindir))
 endif
@@ -888,6 +893,9 @@ endif
 		"$(DESTDIR)/$(qemu_desktopdir)/qemu.desktop"
 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 ecd856e3a3..d492e24da2 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -118,6 +118,8 @@ obj-y += target/$(TARGET_BASE_ARCH)/
 obj-y += disas.o
 obj-$(call notempty,$(TARGET_XML_FILES)) += gdbstub-xml.o
 
+obj-$(CONFIG_PLUGIN) += plugins/
+
 #########################################################
 # Linux user emulator target
 
diff --git a/include/qemu/plugin.h b/include/qemu/plugin.h
new file mode 100644
index 0000000000..93e39f44d2
--- /dev/null
+++ b/include/qemu/plugin.h
@@ -0,0 +1,261 @@
+/*
+ * 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;
+typedef QTAILQ_HEAD(, qemu_plugin_desc) QemuPluginList;
+
+#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, QemuPluginList *head);
+int qemu_plugin_load_list(QemuPluginList *head);
+#else /* !CONFIG_PLUGIN */
+static inline void qemu_plugin_add_opts(void)
+{ }
+
+static inline void qemu_plugin_opt_parse(const char *optarg,
+                                         QemuPluginList *head)
+{
+    error_report("plugin interface not enabled in this build");
+    exit(1);
+}
+
+static inline int qemu_plugin_load_list(QemuPluginList *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_insn {
+    GByteArray *data;
+    uint64_t vaddr;
+    void *haddr;
+    GArray *cbs[PLUGIN_N_CB_TYPES][PLUGIN_N_CB_SUBTYPES];
+    bool calls_helpers;
+    bool mem_helper;
+};
+
+/*
+ * qemu_plugin_insn allocate and cleanup functions. We don't expect to
+ * cleanup many of these structures. They are reused for each fresh
+ * translation.
+ */
+
+static inline void qemu_plugin_insn_cleanup_fn(gpointer data)
+{
+    struct qemu_plugin_insn *insn = (struct qemu_plugin_insn *) data;
+    g_byte_array_free(insn->data, true);
+}
+
+static inline struct qemu_plugin_insn * qemu_plugin_insn_alloc(void)
+{
+    int i, j;
+    struct qemu_plugin_insn *insn = g_new0(struct qemu_plugin_insn, 1);
+    insn->data = g_byte_array_sized_new(4);
+
+    for (i = 0; i < PLUGIN_N_CB_TYPES; i++) {
+        for (j = 0; j < PLUGIN_N_CB_SUBTYPES; j++) {
+            insn->cbs[i][j] = g_array_new(false, false,
+                                          sizeof(struct qemu_plugin_dyn_cb));
+        }
+    }
+    return insn;
+}
+
+struct qemu_plugin_tb {
+    GPtrArray *insns;
+    size_t n;
+    uint64_t vaddr;
+    uint64_t vaddr2;
+    void *haddr1;
+    void *haddr2;
+    GArray *cbs[PLUGIN_N_CB_SUBTYPES];
+};
+
+/**
+ * qemu_plugin_tb_insn_get(): get next plugin record for translation.
+ *
+ */
+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->insns->len)) {
+        struct qemu_plugin_insn *new_insn = qemu_plugin_insn_alloc();
+        g_ptr_array_add(tb->insns, new_insn);
+    }
+    insn = g_ptr_array_index(tb->insns, tb->n++);
+    g_byte_array_set_size(insn->data, 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++) {
+            g_array_set_size(insn->cbs[i][j], 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(GArray *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(GArray *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 08481ad304..27b67a4e49 100644
--- a/include/qom/cpu.h
+++ b/include/qom/cpu.h
@@ -29,6 +29,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);
@@ -345,6 +346,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.
@@ -430,6 +432,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);
+
+    GArray *plugin_mem_cbs;
+
     /* TODO Move common fields from CPUArchState here. */
     int cpu_index;
     int cluster_index;
diff --git a/plugins/Makefile.objs b/plugins/Makefile.objs
new file mode 100644
index 0000000000..58940335bc
--- /dev/null
+++ b/plugins/Makefile.objs
@@ -0,0 +1,6 @@
+#
+# Plugin Support
+#
+
+obj-y += loader.o
+obj-y += core.o
diff --git a/plugins/core.c b/plugins/core.c
new file mode 100644
index 0000000000..8c46373773
--- /dev/null
+++ b/plugins/core.c
@@ -0,0 +1,524 @@
+/* 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 */
+#include "plugin.h"
+
+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_state plugin;
+
+struct qemu_plugin_ctx *plugin_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);
+    }
+}
+
+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);
+    }
+}
+
+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 = plugin_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);
+}
+
+void plugin_register_cb(qemu_plugin_id_t id, enum qemu_plugin_event ev,
+                        void *func)
+{
+    do_plugin_register_cb(id, ev, func, NULL);
+}
+
+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_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 = plugin_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);
+}
+
+/* Allocate and return a callback record */
+static struct qemu_plugin_dyn_cb *plugin_get_dyn_cb(GArray **arr)
+{
+    GArray *cbs = *arr;
+
+    if (!cbs) {
+        cbs = g_array_sized_new(false, false, sizeof(struct qemu_plugin_dyn_cb), 1);
+        *arr = cbs;
+    }
+
+    g_array_set_size(cbs, cbs->len + 1);
+    return &g_array_index(cbs, struct qemu_plugin_dyn_cb, cbs->len - 1);
+}
+
+void plugin_register_inline_op(GArray **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;
+}
+
+inline void
+plugin_register_dyn_cb__udata(GArray **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 plugin_register_vcpu_mem_cb(GArray **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_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_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_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_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);
+}
+
+static bool free_dyn_cb_arr(void *p, uint32_t h, void *userp)
+{
+    g_array_free((GArray *) p, true);
+    return true;
+}
+
+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);
+}
+
+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)
+{
+    GArray *arr = cpu->plugin_mem_cbs;
+    size_t i;
+
+    if (arr == NULL) {
+        return;
+    }
+    for (i = 0; i < arr->len; i++) {
+        struct qemu_plugin_dyn_cb *cb =
+            &g_array_index(arr, struct qemu_plugin_dyn_cb, 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();
+        }
+    }
+}
+
+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 bool plugin_dyn_cb_arr_cmp(const void *ap, const void *bp)
+{
+    return ap == bp;
+}
+
+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);
+}
diff --git a/plugins/loader.c b/plugins/loader.c
new file mode 100644
index 0000000000..bdd875868f
--- /dev/null
+++ b/plugins/loader.c
@@ -0,0 +1,353 @@
+#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/qht.h"
+#include "qemu/bitmap.h"
+#include "qemu/xxhash.h"
+#include "qemu/plugin.h"
+#include "qom/cpu.h"
+#include <dlfcn.h>
+#include "cpu.h"
+#include "exec/exec-all.h"
+
+#include "plugin.h"
+
+/*
+ * 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 {
+    QemuPluginList *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 **);
+
+extern struct qemu_plugin_state plugin;
+
+void qemu_plugin_add_dyn_cb_arr(GArray *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 struct qemu_plugin_desc *plugin_find_desc(QemuPluginList *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, QemuPluginList *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);
+    ctx->installing = true;
+    rc = install(ctx->id, desc->argc, desc->argv);
+    ctx->installing = false;
+    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 yet call it.
+         */
+        /* if (!ctx->uninstalling) { */
+        /*     qemu_plugin_uninstall(ctx->id, NULL); */
+        /* } */
+    }
+
+    qemu_rec_mutex_unlock(&plugin.lock);
+    return rc;
+
+ 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(QemuPluginList *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;
+}
+
+struct qemu_plugin_reset_data {
+    struct qemu_plugin_ctx *ctx;
+    qemu_plugin_simple_cb_t cb;
+    bool reset;
+};
+
+static void plugin_reset_destroy__locked(struct qemu_plugin_reset_data *data)
+{
+    struct qemu_plugin_ctx *ctx = data->ctx;
+    enum qemu_plugin_event ev;
+    bool success;
+
+    /*
+     * After updating the subscription lists there is no need to wait for an RCU
+     * grace period to elapse, because right now we either are in a "safe async"
+     * work environment (i.e. all vCPUs are asleep), or no vCPUs have yet been
+     * created.
+     */
+    for (ev = 0; ev < QEMU_PLUGIN_EV_MAX; ev++) {
+        plugin_unregister_cb__locked(ctx, ev);
+    }
+
+    if (data->reset) {
+        g_assert(ctx->resetting);
+        if (data->cb) {
+            data->cb(ctx->id);
+        }
+        ctx->resetting = false;
+        g_free(data);
+        return;
+    }
+
+    g_assert(ctx->uninstalling);
+    /* we cannot dlclose if we are going to return to plugin code */
+    if (ctx->installing) {
+        error_report("Calling qemu_plugin_uninstall from the install function "
+                     "is a bug. Instead, return !0 from the install function.");
+        abort();
+    }
+
+    success = g_hash_table_remove(plugin.id_ht, &ctx->id);
+    g_assert(success);
+    QTAILQ_REMOVE(&plugin.ctxs, ctx, entry);
+    if (data->cb) {
+        data->cb(ctx->id);
+    }
+    if (dlclose(ctx->handle)) {
+        warn_report("%s: %s", __func__, dlerror());
+    }
+    plugin_desc_free(ctx->desc);
+    qemu_vfree(ctx);
+    g_free(data);
+}
+
+static void plugin_reset_destroy(struct qemu_plugin_reset_data *data)
+{
+    qemu_rec_mutex_lock(&plugin.lock);
+    plugin_reset_destroy__locked(data);
+    qemu_rec_mutex_lock(&plugin.lock);
+}
+
+static void plugin_flush_destroy(CPUState *cpu, run_on_cpu_data arg)
+{
+    struct qemu_plugin_reset_data *data = arg.host_ptr;
+
+    g_assert(cpu_in_exclusive_work_context(cpu));
+    tb_flush(cpu);
+    plugin_reset_destroy(data);
+}
+
+void plugin_reset_uninstall(qemu_plugin_id_t id,
+                            qemu_plugin_simple_cb_t cb,
+                            bool reset)
+{
+    struct qemu_plugin_reset_data *data;
+    struct qemu_plugin_ctx *ctx;
+
+    qemu_rec_mutex_lock(&plugin.lock);
+    ctx = plugin_id_to_ctx_locked(id);
+    if (ctx->uninstalling || (reset && ctx->resetting)) {
+        qemu_rec_mutex_unlock(&plugin.lock);
+        return;
+    }
+    ctx->resetting = reset;
+    ctx->uninstalling = !reset;
+    qemu_rec_mutex_unlock(&plugin.lock);
+
+    data = g_new(struct qemu_plugin_reset_data, 1);
+    data->ctx = ctx;
+    data->cb = cb;
+    data->reset = reset;
+    /*
+     * Only flush the code cache if the vCPUs have been created. If so,
+     * current_cpu must be non-NULL.
+     */
+    if (current_cpu) {
+        async_safe_run_on_cpu(current_cpu, plugin_flush_destroy,
+                              RUN_ON_CPU_HOST_PTR(data));
+    } else {
+        /*
+         * If current_cpu isn't set, then we don't have yet any vCPU threads
+         * and we therefore can remove the callbacks synchronously.
+         */
+        plugin_reset_destroy(data);
+    }
+}
diff --git a/plugins/plugin.h b/plugins/plugin.h
new file mode 100644
index 0000000000..e89f9ef0ea
--- /dev/null
+++ b/plugins/plugin.h
@@ -0,0 +1,95 @@
+/*
+ * Plugin Shared Internal Functions
+ *
+ * Copyright (C) 2019, Linaro
+ *
+ * License: GNU GPL, version 2 or later.
+ *   See the COPYING file in the top-level directory.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef _PLUGIN_INTERNAL_H_
+#define _PLUGIN_INTERNAL_H_
+
+/* 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;
+};
+
+
+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;
+    /*
+     * keep a reference to @desc until uninstall, so that plugins do not have
+     * to strdup plugin args.
+     */
+    struct qemu_plugin_desc *desc;
+    bool installing;
+    bool uninstalling;
+    bool resetting;
+};
+
+struct qemu_plugin_ctx *plugin_id_to_ctx_locked(qemu_plugin_id_t id);
+
+void plugin_register_inline_op(GArray **arr,
+                               enum qemu_plugin_mem_rw rw,
+                               enum qemu_plugin_op op, void *ptr,
+                               uint64_t imm);
+
+void plugin_reset_uninstall(qemu_plugin_id_t id,
+                            qemu_plugin_simple_cb_t cb,
+                            bool reset);
+
+void plugin_register_cb(qemu_plugin_id_t id, enum qemu_plugin_event ev,
+                        void *func);
+
+void plugin_unregister_cb__locked(struct qemu_plugin_ctx *ctx,
+                                  enum qemu_plugin_event ev);
+
+void
+plugin_register_cb_udata(qemu_plugin_id_t id, enum qemu_plugin_event ev,
+                         void *func, void *udata);
+
+void
+plugin_register_dyn_cb__udata(GArray **arr,
+                              qemu_plugin_vcpu_udata_cb_t cb,
+                              enum qemu_plugin_cb_flags flags, void *udata);
+
+
+void plugin_register_vcpu_mem_cb(GArray **arr,
+                                 void *cb,
+                                 enum qemu_plugin_cb_flags flags,
+                                 enum qemu_plugin_mem_rw rw,
+                                 void *udata, bool haddr);
+
+void exec_inline_op(struct qemu_plugin_dyn_cb *cb);
+
+#endif /* _PLUGIN_INTERNAL_H_ */
-- 
2.20.1



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

* [Qemu-devel] [PATCH v3 09/50] plugin: add implementation of the api
  2019-06-14 17:11 [Qemu-devel] [PATCH v3 00/50] tcg plugin support Alex Bennée
                   ` (7 preceding siblings ...)
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 08/50] plugin: add core code Alex Bennée
@ 2019-06-14 17:11 ` Alex Bennée
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 10/50] queue: add QTAILQ_REMOVE_SEVERAL Alex Bennée
                   ` (44 subsequent siblings)
  53 siblings, 0 replies; 117+ messages in thread
From: Alex Bennée @ 2019-06-14 17:11 UTC (permalink / raw)
  To: qemu-devel; +Cc: Alex Bennée

[AJB: split from the core code commit]
Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
---
 plugins/Makefile.objs |   1 +
 plugins/api.c         | 267 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 268 insertions(+)
 create mode 100644 plugins/api.c

diff --git a/plugins/Makefile.objs b/plugins/Makefile.objs
index 58940335bc..95baabf3d2 100644
--- a/plugins/Makefile.objs
+++ b/plugins/Makefile.objs
@@ -4,3 +4,4 @@
 
 obj-y += loader.o
 obj-y += core.o
+obj-y += api.o
diff --git a/plugins/api.c b/plugins/api.c
new file mode 100644
index 0000000000..1c3aa64702
--- /dev/null
+++ b/plugins/api.c
@@ -0,0 +1,267 @@
+/*
+ * QEMU Plugin API
+ *
+ * This provides the API that is available to the plugins to interact
+ * with QEMU. We have to be careful not to expose internal details of
+ * how QEMU works so we abstract out things like translation and
+ * instructions to anonymous data types:
+ *
+ *  qemu_plugin_tb
+ *  qemu_plugin_insn
+ *
+ * Which can then be passed back into the API to do additional things.
+ * As such all the public functions in here are exported in
+ * qemu-plugin.h.
+ *
+ * The general life-cycle of a plugin is:
+ *
+ *  - plugin is loaded, public qemu_plugin_install called
+ *    - the install func registers callbacks for events
+ *    - usually an atexit_cb is registered to dump info at the end
+ *  - when a registered event occurs the plugin is called
+ *     - some events pass additional info
+ *     - during translation the plugin can decide to instrument any
+ *       instruction
+ *  - when QEMU exits all the registered atexit callbacks are called
+ *
+ * Copyright (C) 2017, Emilio G. Cota <cota@braap.org>
+ * Copyright (C) 2019, Linaro
+ *
+ * License: GNU GPL, version 2 or later.
+ *   See the COPYING file in the top-level directory.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/plugin.h"
+#include "cpu.h"
+#include "sysemu/sysemu.h"
+#include "tcg/tcg.h"
+#include "trace/mem-internal.h" /* mem_info macros */
+#include "plugin.h"
+
+/* Uninstall and Reset handlers */
+
+void qemu_plugin_uninstall(qemu_plugin_id_t id, qemu_plugin_simple_cb_t cb)
+{
+    plugin_reset_uninstall(id, cb, false);
+}
+
+void qemu_plugin_reset(qemu_plugin_id_t id, qemu_plugin_simple_cb_t cb)
+{
+    plugin_reset_uninstall(id, cb, true);
+}
+
+/*
+ * Plugin Register Functions
+ *
+ * This allows the plugin to register callbacks for various events
+ * during the translation.
+ */
+
+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_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);
+}
+
+
+
+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_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_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_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);
+}
+
+/*
+ * Plugin Queries
+ *
+ * These are queries that the plugin can make to gauge information
+ * from our opaque data types. We do not want to leak internal details
+ * here just information useful to the plugin.
+ */
+
+/*
+ * Translation block information:
+ *
+ * A plugin can query the virtual address of the start of the block
+ * and the number of instructions in it. It can also get access to
+ * each translated instruction.
+ */
+
+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 g_ptr_array_index(tb->insns, idx);
+}
+
+/*
+ * Instruction information
+ *
+ * These queries allow the plugin to retrieve information about each
+ * instruction being translated.
+ */
+
+const void *qemu_plugin_insn_data(const struct qemu_plugin_insn *insn)
+{
+    return insn->data->data;
+}
+
+size_t qemu_plugin_insn_size(const struct qemu_plugin_insn *insn)
+{
+    return insn->data->len;
+}
+
+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;
+}
+
+/*
+ * The memory queries allow the plugin to query information about a
+ * memory access.
+ */
+
+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);
+}
+
+/*
+ * Queries to the number and potential maximum number of vCPUs there
+ * will be. This helps the plugin dimension per-vcpu arrays.
+ */
+
+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
+}
-- 
2.20.1



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

* [Qemu-devel] [PATCH  v3 10/50] queue: add QTAILQ_REMOVE_SEVERAL
  2019-06-14 17:11 [Qemu-devel] [PATCH v3 00/50] tcg plugin support Alex Bennée
                   ` (8 preceding siblings ...)
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 09/50] plugin: add implementation of the api Alex Bennée
@ 2019-06-14 17:11 ` Alex Bennée
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 11/50] cputlb: document get_page_addr_code Alex Bennée
                   ` (43 subsequent siblings)
  53 siblings, 0 replies; 117+ messages in thread
From: Alex Bennée @ 2019-06-14 17:11 UTC (permalink / raw)
  To: qemu-devel; +Cc: Emilio G. Cota

From: "Emilio G. Cota" <cota@braap.org>

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 0379bd8fdb..66e834dad1 100644
--- a/include/qemu/queue.h
+++ b/include/qemu/queue.h
@@ -422,6 +422,16 @@ union {                                                                 \
         (elm)->field.tqe_circ.tql_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_circ.tql_prev =          \
+                (left)->field.tqe_circ.tql_prev;                        \
+        else                                                            \
+            (head)->tqh_circ.tql_prev = (left)->field.tqe_circ.tql_prev; \
+        (left)->field.tqe_circ.tql_prev->tql_next = (right)->field.tqe_next; \
+    } while (/*CONSTCOND*/0)
+
 #define QTAILQ_FOREACH(var, head, field)                                \
         for ((var) = ((head)->tqh_first);                               \
                 (var);                                                  \
-- 
2.20.1



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

* [Qemu-devel] [PATCH v3 11/50] cputlb: document get_page_addr_code
  2019-06-14 17:11 [Qemu-devel] [PATCH v3 00/50] tcg plugin support Alex Bennée
                   ` (9 preceding siblings ...)
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 10/50] queue: add QTAILQ_REMOVE_SEVERAL Alex Bennée
@ 2019-06-14 17:11 ` Alex Bennée
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 12/50] cputlb: introduce get_page_addr_code_hostp Alex Bennée
                   ` (42 subsequent siblings)
  53 siblings, 0 replies; 117+ messages in thread
From: Alex Bennée @ 2019-06-14 17:11 UTC (permalink / raw)
  To: qemu-devel
  Cc: Paolo Bonzini, Emilio G. Cota, Alex Bennée, Richard Henderson

From: "Emilio G. Cota" <cota@braap.org>

Suggested-by: Alex Bennée <alex.bennee@linaro.org>
Signed-off-by: Emilio G. Cota <cota@braap.org>
---
 accel/tcg/cputlb.c      |  5 -----
 include/exec/exec-all.h | 24 +++++++++++++++++++++---
 2 files changed, 21 insertions(+), 8 deletions(-)

diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c
index bb9897b25a..3237ff4def 100644
--- a/accel/tcg/cputlb.c
+++ b/accel/tcg/cputlb.c
@@ -1015,11 +1015,6 @@ static bool victim_tlb_hit(CPUArchState *env, size_t mmu_idx, size_t index,
   victim_tlb_hit(env, mmu_idx, index, offsetof(CPUTLBEntry, TY), \
                  (ADDR) & TARGET_PAGE_MASK)
 
-/* NOTE: this function can trigger an exception */
-/* NOTE2: the returned address is not exactly the physical address: it
- * 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)
 {
     uintptr_t mmu_idx = cpu_mmu_index(env, true);
diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h
index 16034ee651..8b1c3d5b9d 100644
--- a/include/exec/exec-all.h
+++ b/include/exec/exec-all.h
@@ -480,7 +480,15 @@ void mmap_lock(void);
 void mmap_unlock(void);
 bool have_mmap_lock(void);
 
-static inline tb_page_addr_t get_page_addr_code(CPUArchState *env1, target_ulong addr)
+/**
+ * get_page_addr_code() - user-mode version
+ * @env: CPUArchState
+ * @addr: guest virtual address of guest code
+ *
+ * Returns @addr.
+ */
+static inline tb_page_addr_t get_page_addr_code(CPUArchState *env,
+                                                target_ulong addr)
 {
     return addr;
 }
@@ -488,8 +496,18 @@ static inline tb_page_addr_t get_page_addr_code(CPUArchState *env1, target_ulong
 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);
+/**
+ * get_page_addr_code() - full-system version
+ * @env: CPUArchState
+ * @addr: guest virtual address of guest code
+ *
+ * If we cannot translate and execute from the entire RAM page, or if
+ * the region is not backed by RAM, returns -1. Otherwise, returns the
+ * ram_addr_t corresponding to the guest code at @addr.
+ *
+ * Note: this function can trigger an exception.
+ */
+tb_page_addr_t get_page_addr_code(CPUArchState *env, target_ulong addr);
 
 void tlb_reset_dirty(CPUState *cpu, ram_addr_t start1, ram_addr_t length);
 void tlb_set_dirty(CPUState *cpu, target_ulong vaddr);
-- 
2.20.1



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

* [Qemu-devel] [PATCH v3 12/50] cputlb: introduce get_page_addr_code_hostp
  2019-06-14 17:11 [Qemu-devel] [PATCH v3 00/50] tcg plugin support Alex Bennée
                   ` (10 preceding siblings ...)
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 11/50] cputlb: document get_page_addr_code Alex Bennée
@ 2019-06-14 17:11 ` Alex Bennée
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 13/50] plugin-gen: add module for TCG-related code Alex Bennée
                   ` (41 subsequent siblings)
  53 siblings, 0 replies; 117+ messages in thread
From: Alex Bennée @ 2019-06-14 17:11 UTC (permalink / raw)
  To: qemu-devel; +Cc: Paolo Bonzini, Emilio G. Cota, Richard Henderson

From: "Emilio G. Cota" <cota@braap.org>

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

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

diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c
index 3237ff4def..6a0dc438ff 100644
--- a/accel/tcg/cputlb.c
+++ b/accel/tcg/cputlb.c
@@ -1015,7 +1015,8 @@ static bool victim_tlb_hit(CPUArchState *env, size_t mmu_idx, size_t index,
   victim_tlb_hit(env, mmu_idx, index, offsetof(CPUTLBEntry, TY), \
                  (ADDR) & TARGET_PAGE_MASK)
 
-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);
@@ -1040,13 +1041,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).
diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h
index 8b1c3d5b9d..90045e77c1 100644
--- a/include/exec/exec-all.h
+++ b/include/exec/exec-all.h
@@ -21,6 +21,7 @@
 #define EXEC_ALL_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 */
@@ -492,6 +493,26 @@ static inline tb_page_addr_t get_page_addr_code(CPUArchState *env,
 {
     return addr;
 }
+
+/**
+ * get_page_addr_code_hostp() - user-mode version
+ * @env: CPUArchState
+ * @addr: guest virtual address of guest code
+ *
+ * Returns @addr.
+ *
+ * If @hostp is non-NULL, sets *@hostp to the host address where @addr's content
+ * is kept.
+ */
+static inline tb_page_addr_t get_page_addr_code_hostp(CPUArchState *env,
+                                                      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) {}
@@ -509,6 +530,23 @@ static inline void mmap_unlock(void) {}
  */
 tb_page_addr_t get_page_addr_code(CPUArchState *env, target_ulong addr);
 
+/**
+ * get_page_addr_code_hostp() - full-system version
+ * @env: CPUArchState
+ * @addr: guest virtual address of guest code
+ *
+ * See get_page_addr_code() (full-system version) for documentation on the
+ * return value.
+ *
+ * Sets *@hostp (when @hostp is non-NULL) as follows.
+ * If the return value is -1, sets *@hostp to NULL. Otherwise, sets *@hostp
+ * to the host address where @addr's content is kept.
+ *
+ * Note: this function can trigger an exception.
+ */
+tb_page_addr_t get_page_addr_code_hostp(CPUArchState *env, 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);
 
-- 
2.20.1



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

* [Qemu-devel] [PATCH v3 13/50] plugin-gen: add module for TCG-related code
  2019-06-14 17:11 [Qemu-devel] [PATCH v3 00/50] tcg plugin support Alex Bennée
                   ` (11 preceding siblings ...)
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 12/50] cputlb: introduce get_page_addr_code_hostp Alex Bennée
@ 2019-06-14 17:11 ` Alex Bennée
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 14/50] tcg: add tcg_gen_st_ptr Alex Bennée
                   ` (40 subsequent siblings)
  53 siblings, 0 replies; 117+ messages in thread
From: Alex Bennée @ 2019-06-14 17:11 UTC (permalink / raw)
  To: qemu-devel
  Cc: Paolo Bonzini, Emilio G. Cota, Alex Bennée, Richard Henderson

From: "Emilio G. Cota" <cota@braap.org>

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>
[AJB: add alloc_tcg_plugin_context, use glib]
Signed-off-by: Alex Bennée <alex.bennee@linaro.org>

---
v3
  - introduce alloc_tcg_plugin_context
  - allocate GPtrArray for tracking insns
---
 accel/tcg/Makefile.objs     |    1 +
 accel/tcg/plugin-gen.c      | 1072 +++++++++++++++++++++++++++++++++++
 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.c                   |   22 +
 tcg/tcg.h                   |   20 +
 11 files changed, 1204 insertions(+)
 create mode 100644 accel/tcg/plugin-gen.c
 create mode 100644 accel/tcg/plugin-helpers.h
 create mode 100644 include/exec/plugin-gen.h

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
diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c
new file mode 100644
index 0000000000..7994819fe6
--- /dev/null
+++ b/accel/tcg/plugin-gen.c
@@ -0,0 +1,1072 @@
+/*
+ * 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, 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 GArray *cbs, TCGOp *begin_op, inject_fn inject,
+                    op_ok_fn ok)
+{
+    TCGOp *end_op;
+    TCGOp *op;
+    int cb_idx = -1;
+    int i;
+
+    if (!cbs || cbs->len == 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->len; i++) {
+        struct qemu_plugin_dyn_cb *cb =
+            &g_array_index(cbs, struct qemu_plugin_dyn_cb, 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 GArray *cbs, TCGOp *begin_op)
+{
+    inject_cb_type(cbs, begin_op, append_udata_cb, op_ok);
+}
+
+static void
+inject_inline_cb(const GArray *cbs, TCGOp *begin_op, op_ok_fn ok)
+{
+    inject_cb_type(cbs, begin_op, append_inline_cb, ok);
+}
+
+static void
+inject_mem_cb(const GArray *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, GArray *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)
+{
+    GArray *cbs[3];
+    GArray *arr;
+    size_t 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]->len;
+    }
+
+    plugin_insn->mem_helper = plugin_insn->calls_helpers && n_cbs;
+    if (likely(!plugin_insn->mem_helper)) {
+        rm_ops(begin_op);
+        return;
+    }
+
+    arr = g_array_sized_new(false, false,
+                            sizeof(struct qemu_plugin_dyn_cb), n_cbs);
+
+    for (i = 0; i < ARRAY_SIZE(cbs); i++) {
+        g_array_append_vals(arr, cbs[i]->data, cbs[i]->len);
+    }
+
+    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)
+{
+    struct qemu_plugin_insn *insn = g_ptr_array_index(ptb->insns, insn_idx);
+
+    inject_udata_cb(insn->cbs[PLUGIN_CB_INSN][PLUGIN_CB_REGULAR], begin_op);
+}
+
+static void plugin_gen_insn_inline(const struct qemu_plugin_tb *ptb,
+                                   TCGOp *begin_op, int insn_idx)
+{
+    struct qemu_plugin_insn *insn = g_ptr_array_index(ptb->insns, insn_idx);
+    inject_inline_cb(insn->cbs[PLUGIN_CB_INSN][PLUGIN_CB_INLINE],
+                     begin_op, op_ok);
+}
+
+static void plugin_gen_mem_regular(const struct qemu_plugin_tb *ptb,
+                                   TCGOp *begin_op, int insn_idx)
+{
+    struct qemu_plugin_insn *insn = g_ptr_array_index(ptb->insns, insn_idx);
+    inject_mem_cb(insn->cbs[PLUGIN_CB_MEM][PLUGIN_CB_REGULAR], begin_op);
+}
+
+static void plugin_gen_mem_inline(const struct qemu_plugin_tb *ptb,
+                                  TCGOp *begin_op, int insn_idx)
+{
+    const GArray *cbs;
+    struct qemu_plugin_insn *insn = g_ptr_array_index(ptb->insns, insn_idx);
+
+    cbs = insn->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 GArray *cbs;
+    struct qemu_plugin_insn *insn = g_ptr_array_index(ptb->insns, insn_idx);
+
+    cbs = insn->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)
+{
+    struct qemu_plugin_insn *insn = g_ptr_array_index(ptb->insns, insn_idx);
+    inject_mem_enable_helper(insn, begin_op);
+}
+
+static void plugin_gen_disable_mem_helper(const struct qemu_plugin_tb *ptb,
+                                          TCGOp *begin_op, int insn_idx)
+{
+    struct qemu_plugin_insn *insn = g_ptr_array_index(ptb->insns, insn_idx);
+    inject_mem_disable_helper(insn, 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++) {
+        if (ptb->cbs[i]) {
+            g_array_set_size(ptb->cbs[i], 0);
+        }
+    }
+    ptb->n = 0;
+    tcg_ctx->plugin_insn = NULL;
+}
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 268e0f804b..573c2ce2e9 100644
--- a/include/exec/helper-tcg.h
+++ b/include/exec/helper-tcg.h
@@ -55,6 +55,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 2d4dd5cd7d..e997346c97 100644
--- a/tcg/tcg-op.h
+++ b/tcg/tcg-op.h
@@ -833,6 +833,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 242d608e6d..9288a04946 100644
--- a/tcg/tcg-opc.h
+++ b/tcg/tcg-opc.h
@@ -198,6 +198,9 @@ DEF(goto_tb, 0, 0, 1, TCG_OPF_BB_EXIT | TCG_OPF_BB_END)
 DEF(goto_ptr, 0, 1, 0,
     TCG_OPF_BB_EXIT | 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.c b/tcg/tcg.c
index 02a2680169..962e17da33 100644
--- a/tcg/tcg.c
+++ b/tcg/tcg.c
@@ -729,6 +729,15 @@ void tcg_region_init(void)
 #endif
 }
 
+static void alloc_tcg_plugin_context(TCGContext *s)
+{
+#ifdef CONFIG_PLUGIN
+    s->plugin_tb = g_new0(struct qemu_plugin_tb, 1);
+    s->plugin_tb->insns =
+        g_ptr_array_new_with_free_func(qemu_plugin_insn_cleanup_fn);
+#endif
+}
+
 /*
  * All TCG threads except the parent (i.e. the one that called tcg_context_init
  * and registered the target's TCG globals) must register with this function
@@ -772,6 +781,10 @@ void tcg_register_thread(void)
     g_assert(n < max_cpus);
     atomic_set(&tcg_ctxs[n], s);
 
+    if (n > 0) {
+        alloc_tcg_plugin_context(s);
+    }
+
     tcg_ctx = s;
     qemu_mutex_lock(&region.lock);
     err = tcg_region_initial_alloc__locked(tcg_ctx);
@@ -968,6 +981,8 @@ void tcg_context_init(TCGContext *s)
         indirect_reg_alloc_order[i] = tcg_target_reg_alloc_order[i];
     }
 
+    alloc_tcg_plugin_context(s);
+
     tcg_ctx = s;
     /*
      * In user-mode we simply share the init context among threads, since we
@@ -1671,6 +1686,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/tcg/tcg.h b/tcg/tcg.h
index b411e17a28..2385e758e5 100644
--- a/tcg/tcg.h
+++ b/tcg/tcg.h
@@ -625,6 +625,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];
@@ -726,6 +729,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 */
 
-- 
2.20.1



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

* [Qemu-devel] [PATCH  v3 14/50] tcg: add tcg_gen_st_ptr
  2019-06-14 17:11 [Qemu-devel] [PATCH v3 00/50] tcg plugin support Alex Bennée
                   ` (12 preceding siblings ...)
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 13/50] plugin-gen: add module for TCG-related code Alex Bennée
@ 2019-06-14 17:11 ` Alex Bennée
  2019-06-17 20:19   ` Richard Henderson
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 15/50] tcg: add MO_HADDR to TCGMemOp Alex Bennée
                   ` (39 subsequent siblings)
  53 siblings, 1 reply; 117+ messages in thread
From: Alex Bennée @ 2019-06-14 17:11 UTC (permalink / raw)
  To: qemu-devel; +Cc: Emilio G. Cota, Richard Henderson

From: "Emilio G. Cota" <cota@braap.org>

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 e997346c97..edf12de1f3 100644
--- a/tcg/tcg-op.h
+++ b/tcg/tcg-op.h
@@ -1260,6 +1260,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.20.1



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

* [Qemu-devel] [PATCH  v3 15/50] tcg: add MO_HADDR to TCGMemOp
  2019-06-14 17:11 [Qemu-devel] [PATCH v3 00/50] tcg plugin support Alex Bennée
                   ` (13 preceding siblings ...)
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 14/50] tcg: add tcg_gen_st_ptr Alex Bennée
@ 2019-06-14 17:11 ` Alex Bennée
  2019-06-17 20:43   ` Richard Henderson
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 16/50] atomic_template: fix indentation in GEN_ATOMIC_HELPER Alex Bennée
                   ` (38 subsequent siblings)
  53 siblings, 1 reply; 117+ messages in thread
From: Alex Bennée @ 2019-06-14 17:11 UTC (permalink / raw)
  To: qemu-devel; +Cc: Emilio G. Cota, Richard Henderson

From: "Emilio G. Cota" <cota@braap.org>

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 2385e758e5..966e89104d 100644
--- a/tcg/tcg.h
+++ b/tcg/tcg.h
@@ -367,6 +367,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.20.1



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

* [Qemu-devel] [PATCH v3 16/50] atomic_template: fix indentation in GEN_ATOMIC_HELPER
  2019-06-14 17:11 [Qemu-devel] [PATCH v3 00/50] tcg plugin support Alex Bennée
                   ` (14 preceding siblings ...)
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 15/50] tcg: add MO_HADDR to TCGMemOp Alex Bennée
@ 2019-06-14 17:11 ` Alex Bennée
  2019-06-17 20:43   ` Richard Henderson
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 17/50] atomic_template: add inline trace/plugin helpers Alex Bennée
                   ` (37 subsequent siblings)
  53 siblings, 1 reply; 117+ messages in thread
From: Alex Bennée @ 2019-06-14 17:11 UTC (permalink / raw)
  To: qemu-devel
  Cc: Paolo Bonzini, Emilio G. Cota, Alex Bennée, Richard Henderson

From: "Emilio G. Cota" <cota@braap.org>

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 5aaf186253..df9c838817 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.20.1



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

* [Qemu-devel] [PATCH v3 17/50] atomic_template: add inline trace/plugin helpers
  2019-06-14 17:11 [Qemu-devel] [PATCH v3 00/50] tcg plugin support Alex Bennée
                   ` (15 preceding siblings ...)
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 16/50] atomic_template: fix indentation in GEN_ATOMIC_HELPER Alex Bennée
@ 2019-06-14 17:11 ` Alex Bennée
  2019-06-17 20:47   ` Richard Henderson
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 18/50] cpu_ldst_useronly_template: remove redundant #ifndef CODE_ACCESS Alex Bennée
                   ` (36 subsequent siblings)
  53 siblings, 1 reply; 117+ messages in thread
From: Alex Bennée @ 2019-06-14 17:11 UTC (permalink / raw)
  To: qemu-devel; +Cc: Paolo Bonzini, Emilio G. Cota, Richard Henderson

From: "Emilio G. Cota" <cota@braap.org>

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 df9c838817..04c4c7b0d2 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_cpu(env), addr, info);      \
-        trace_guest_mem_before_exec(env_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_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_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_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_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_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.20.1



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

* [Qemu-devel] [PATCH v3 18/50] cpu_ldst_useronly_template: remove redundant #ifndef CODE_ACCESS
  2019-06-14 17:11 [Qemu-devel] [PATCH v3 00/50] tcg plugin support Alex Bennée
                   ` (16 preceding siblings ...)
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 17/50] atomic_template: add inline trace/plugin helpers Alex Bennée
@ 2019-06-14 17:11 ` Alex Bennée
  2019-06-17 20:47   ` Richard Henderson
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 19/50] tcg: let plugins instrument memory accesses Alex Bennée
                   ` (35 subsequent siblings)
  53 siblings, 1 reply; 117+ messages in thread
From: Alex Bennée @ 2019-06-14 17:11 UTC (permalink / raw)
  To: qemu-devel; +Cc: Paolo Bonzini, Emilio G. Cota, Richard Henderson

From: "Emilio G. Cota" <cota@braap.org>

This function is already under #ifndef CODE_ACCESS.

Signed-off-by: Emilio G. Cota <cota@braap.org>
---
 include/exec/cpu_ldst_useronly_template.h | 2 --
 1 file changed, 2 deletions(-)

diff --git a/include/exec/cpu_ldst_useronly_template.h b/include/exec/cpu_ldst_useronly_template.h
index bc45e2b8d4..42a95237f1 100644
--- a/include/exec/cpu_ldst_useronly_template.h
+++ b/include/exec/cpu_ldst_useronly_template.h
@@ -114,11 +114,9 @@ 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_cpu(env), ptr,
         trace_mem_build_info(SHIFT, false, MO_TE, true));
-#endif
     glue(glue(st, SUFFIX), _p)(g2h(ptr), v);
 }
 
-- 
2.20.1



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

* [Qemu-devel] [PATCH v3 19/50] tcg: let plugins instrument memory accesses
  2019-06-14 17:11 [Qemu-devel] [PATCH v3 00/50] tcg plugin support Alex Bennée
                   ` (17 preceding siblings ...)
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 18/50] cpu_ldst_useronly_template: remove redundant #ifndef CODE_ACCESS Alex Bennée
@ 2019-06-14 17:11 ` Alex Bennée
  2019-06-17 20:51   ` Richard Henderson
  2019-06-28 15:30   ` Aaron Lindsay OS via Qemu-devel
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 20/50] translate-all: notify plugin code of tb_flush Alex Bennée
                   ` (34 subsequent siblings)
  53 siblings, 2 replies; 117+ messages in thread
From: Alex Bennée @ 2019-06-14 17:11 UTC (permalink / raw)
  To: qemu-devel
  Cc: Paolo Bonzini, Emilio G. Cota, Alex Bennée, Richard Henderson

From: "Emilio G. Cota" <cota@braap.org>

XXX: store hostaddr from non-i386 TCG backends (do it in a helper?)
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>
Signed-off-by: Alex Bennée <alex.bennee@linaro.org>

---
v3
  - fixes for cpu_neg()
---
 accel/tcg/atomic_template.h               |  5 +++
 accel/tcg/cpu-exec.c                      |  3 ++
 accel/tcg/cputlb.c                        | 37 +++++++++++++++++----
 accel/tcg/plugin-gen.c                    | 17 +++++-----
 include/exec/cpu-defs.h                   |  9 +++++
 include/exec/cpu_ldst.h                   |  9 +++++
 include/exec/cpu_ldst_template.h          | 40 ++++++++++++++---------
 include/exec/cpu_ldst_useronly_template.h | 34 ++++++++++++-------
 tcg/i386/tcg-target.inc.c                 |  8 +++++
 tcg/tcg-op.c                              | 40 ++++++++++++++++++-----
 tcg/tcg.h                                 |  1 +
 11 files changed, 153 insertions(+), 50 deletions(-)

diff --git a/accel/tcg/atomic_template.h b/accel/tcg/atomic_template.h
index 04c4c7b0d2..33ddfd498c 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_cpu(env), addr, haddr, info);
+    qemu_plugin_vcpu_mem_cb(env_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_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_cpu(env), addr, haddr, info);
 }
 #endif /* ATOMIC_TEMPLATE_COMMON */
 
diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c
index 6c85c3ee1e..c21353e54f 100644
--- a/accel/tcg/cpu-exec.c
+++ b/accel/tcg/cpu-exec.c
@@ -272,6 +272,7 @@ void cpu_exec_step_atomic(CPUState *cpu)
             qemu_mutex_unlock_iothread();
         }
         assert_no_pages_locked();
+        qemu_plugin_disable_mem_helpers(cpu);
     }
 
     if (in_exclusive_region) {
@@ -705,6 +706,8 @@ int cpu_exec(CPUState *cpu)
         if (qemu_mutex_iothread_locked()) {
             qemu_mutex_unlock_iothread();
         }
+        qemu_plugin_disable_mem_helpers(cpu);
+
         assert_no_pages_locked();
     }
 
diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c
index 6a0dc438ff..b39c1f06f7 100644
--- a/accel/tcg/cputlb.c
+++ b/accel/tcg/cputlb.c
@@ -879,9 +879,18 @@ static void tlb_fill(CPUState *cpu, target_ulong addr, int size,
     assert(ok);
 }
 
+static inline void set_hostaddr(CPUArchState *env, TCGMemOp mo, void *haddr)
+{
+#ifdef CONFIG_PLUGIN
+    if (mo & MO_HADDR) {
+        env_tlb(env)->c.hostaddr = haddr;
+    }
+#endif
+}
+
 static uint64_t io_readx(CPUArchState *env, CPUIOTLBEntry *iotlbentry,
                          int mmu_idx, target_ulong addr, uintptr_t retaddr,
-                         MMUAccessType access_type, int size)
+                         TCGMemOp mo, MMUAccessType access_type, int size)
 {
     CPUState *cpu = env_cpu(env);
     hwaddr mr_offset;
@@ -891,6 +900,9 @@ static uint64_t io_readx(CPUArchState *env, CPUIOTLBEntry *iotlbentry,
     bool locked = false;
     MemTxResult r;
 
+    /* XXX Any sensible choice other than NULL? */
+    set_hostaddr(env, mo, NULL);
+
     section = iotlb_to_section(cpu, iotlbentry->addr, iotlbentry->attrs);
     mr = section->mr;
     mr_offset = (iotlbentry->addr & TARGET_PAGE_MASK) + addr;
@@ -925,7 +937,7 @@ static uint64_t io_readx(CPUArchState *env, CPUIOTLBEntry *iotlbentry,
 
 static void io_writex(CPUArchState *env, CPUIOTLBEntry *iotlbentry,
                       int mmu_idx, uint64_t val, target_ulong addr,
-                      uintptr_t retaddr, int size)
+                      uintptr_t retaddr, TCGMemOp mo, int size)
 {
     CPUState *cpu = env_cpu(env);
     hwaddr mr_offset;
@@ -934,6 +946,8 @@ static void io_writex(CPUArchState *env, CPUIOTLBEntry *iotlbentry,
     bool locked = false;
     MemTxResult r;
 
+    set_hostaddr(env, mo, NULL);
+
     section = iotlb_to_section(cpu, iotlbentry->addr, iotlbentry->attrs);
     mr = section->mr;
     mr_offset = (iotlbentry->addr & TARGET_PAGE_MASK) + addr;
@@ -1264,7 +1278,8 @@ load_helper(CPUArchState *env, target_ulong addr, TCGMemOpIdx oi,
         offsetof(CPUTLBEntry, addr_code) : offsetof(CPUTLBEntry, addr_read);
     const MMUAccessType access_type =
         code_read ? MMU_INST_FETCH : MMU_DATA_LOAD;
-    unsigned a_bits = get_alignment_bits(get_memop(oi));
+    TCGMemOp mo = get_memop(oi);
+    unsigned a_bits = get_alignment_bits(mo);
     void *haddr;
     uint64_t res;
 
@@ -1313,7 +1328,7 @@ load_helper(CPUArchState *env, target_ulong addr, TCGMemOpIdx oi,
         }
 
         res = io_readx(env, &env_tlb(env)->d[mmu_idx].iotlb[index],
-                       mmu_idx, addr, retaddr, access_type, size);
+                       mmu_idx, addr, retaddr, mo, access_type, size);
         return handle_bswap(res, size, big_endian);
     }
 
@@ -1331,6 +1346,12 @@ load_helper(CPUArchState *env, target_ulong addr, TCGMemOpIdx oi,
         r2 = full_load(env, addr2, oi, retaddr);
         shift = (addr & (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);
+
         if (big_endian) {
             /* Big-endian combine.  */
             res = (r1 << shift) | (r2 >> ((size * 8) - shift));
@@ -1343,6 +1364,7 @@ load_helper(CPUArchState *env, target_ulong addr, TCGMemOpIdx oi,
 
  do_aligned_access:
     haddr = (void *)((uintptr_t)addr + entry->addend);
+    set_hostaddr(env, mo, (void *)haddr);
     switch (size) {
     case 1:
         res = ldub_p(haddr);
@@ -1513,7 +1535,8 @@ store_helper(CPUArchState *env, target_ulong addr, uint64_t val,
     CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr);
     target_ulong tlb_addr = tlb_addr_write(entry);
     const size_t tlb_off = offsetof(CPUTLBEntry, addr_write);
-    unsigned a_bits = get_alignment_bits(get_memop(oi));
+    TCGMemOp mo = get_memop(oi);
+    unsigned a_bits = get_alignment_bits(mo);
     void *haddr;
 
     /* Handle CPU specific unaligned behaviour */
@@ -1562,7 +1585,7 @@ store_helper(CPUArchState *env, target_ulong addr, uint64_t val,
 
         io_writex(env, &env_tlb(env)->d[mmu_idx].iotlb[index], mmu_idx,
                   handle_bswap(val, size, big_endian),
-                  addr, retaddr, size);
+                  addr, retaddr, mo, size);
         return;
     }
 
@@ -1607,11 +1630,13 @@ store_helper(CPUArchState *env, target_ulong addr, uint64_t val,
             }
             helper_ret_stb_mmu(env, addr + i, val8, oi, retaddr);
         }
+        set_hostaddr(env, mo, NULL);
         return;
     }
 
  do_aligned_access:
     haddr = (void *)((uintptr_t)addr + entry->addend);
+    set_hostaddr(env, mo, (void *)haddr);
     switch (size) {
     case 1:
         stb_p(haddr, val);
diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c
index 7994819fe6..9d9ec29765 100644
--- a/accel/tcg/plugin-gen.c
+++ b/accel/tcg/plugin-gen.c
@@ -95,8 +95,7 @@ static void do_gen_mem_cb(TCGv vaddr, uint8_t info, bool is_haddr)
     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_ld_i32(cpu_index, cpu_env, -offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index));
     tcg_gen_extu_tl_i64(vaddr64, vaddr);
 
     if (is_haddr) {
@@ -106,7 +105,9 @@ static void do_gen_mem_cb(TCGv vaddr, uint8_t info, bool is_haddr)
          */
 #ifdef CONFIG_SOFTMMU
         haddr = tcg_temp_new_ptr();
-        tcg_gen_ld_ptr(haddr, cpu_env, offsetof(CPUArchState, hostaddr));
+        tcg_gen_ld_ptr(haddr, cpu_env,
+                       offsetof(ArchCPU, neg.tlb.c.hostaddr) -
+                       offsetof(ArchCPU, env));
 #else
         haddr = tcg_const_ptr(NULL);
 #endif
@@ -128,8 +129,8 @@ 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));
+    tcg_gen_ld_i32(cpu_index, cpu_env, -offsetof(ArchCPU, env) +
+                   offsetof(CPUState, cpu_index));
     gen_helper_plugin_vcpu_udata_cb(cpu_index, udata);
 
     tcg_temp_free_ptr(udata);
@@ -172,8 +173,7 @@ 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_gen_st_ptr(ptr, cpu_env, offsetof(CPUState, plugin_mem_cbs));
     tcg_temp_free_ptr(ptr);
 }
 
@@ -784,8 +784,7 @@ void plugin_gen_disable_mem_helpers(void)
         return;
     }
     ptr = tcg_const_ptr(NULL);
-    tcg_gen_st_ptr(ptr, cpu_env, -ENV_OFFSET + offsetof(CPUState,
-                                                        plugin_mem_cbs));
+    tcg_gen_st_ptr(ptr, cpu_env, offsetof(CPUState, plugin_mem_cbs));
     tcg_temp_free_ptr(ptr);
     tcg_ctx->plugin_insn->mem_helper = false;
 }
diff --git a/include/exec/cpu-defs.h b/include/exec/cpu-defs.h
index 9bc713a70b..354788385b 100644
--- a/include/exec/cpu-defs.h
+++ b/include/exec/cpu-defs.h
@@ -207,6 +207,14 @@ typedef struct CPUTLBCommon {
     size_t full_flush_count;
     size_t part_flush_count;
     size_t elide_flush_count;
+#ifdef CONFIG_PLUGIN
+    /*
+     * TODO: remove and calculate on the fly
+     *
+     * Stores the host address of a guest access
+     */
+    void *hostaddr;
+#endif
 } CPUTLBCommon;
 
 /*
@@ -215,6 +223,7 @@ typedef struct CPUTLBCommon {
  * Since this is placed within CPUNegativeOffsetState, the smallest
  * negative offsets are at the end of the struct.
  */
+
 typedef struct CPUTLB {
     CPUTLBCommon c;
     CPUTLBDesc d[NB_MMU_MODES];
diff --git a/include/exec/cpu_ldst.h b/include/exec/cpu_ldst.h
index a08b11bd2c..ac07556d25 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_tlb(env)->c.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 af7e0b49f2..38df113676 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,10 @@ 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_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_cpu(env), ptr, meminfo);
 #endif
 
     addr = ptr;
@@ -101,10 +101,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_cpu(env), ptr, (void *)hostaddr, meminfo);
+#endif
     return res;
 }
 
@@ -125,11 +129,10 @@ 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_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_cpu(env), ptr, meminfo);
 #endif
 
     addr = ptr;
@@ -140,10 +143,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_cpu(env), ptr, (void *)hostaddr, meminfo);
+#endif
     return res;
 }
 
@@ -167,11 +174,10 @@ 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_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_cpu(env), ptr, meminfo);
 #endif
 
     addr = ptr;
@@ -182,10 +188,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_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 42a95237f1..cc625a3da8 100644
--- a/include/exec/cpu_ldst_useronly_template.h
+++ b/include/exec/cpu_ldst_useronly_template.h
@@ -64,12 +64,18 @@
 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_cpu(env), ptr, meminfo);
+#endif
+
+    ret = glue(glue(ld, USUFFIX), _p)(g2h(ptr));
+
 #if !defined(CODE_ACCESS)
-    trace_guest_mem_before_exec(
-        env_cpu(env), ptr,
-        trace_mem_build_info(SHIFT, false, MO_TE, false));
+    qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, NULL, meminfo);
 #endif
-    return glue(glue(ld, USUFFIX), _p)(g2h(ptr));
+    return ret;
 }
 
 static inline RES_TYPE
@@ -88,12 +94,18 @@ 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)
+    uint8_t meminfo = trace_mem_build_info(SHIFT, true, MO_TE, false);
+    trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo);
+#endif
+
+    ret = glue(glue(lds, SUFFIX), _p)(g2h(ptr));
+
 #if !defined(CODE_ACCESS)
-    trace_guest_mem_before_exec(
-        env_cpu(env), ptr,
-        trace_mem_build_info(SHIFT, true, MO_TE, false));
+    qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, NULL, meminfo);
 #endif
-    return glue(glue(lds, SUFFIX), _p)(g2h(ptr));
+    return ret;
 }
 
 static inline int
@@ -114,10 +126,10 @@ static inline void
 glue(glue(cpu_st, SUFFIX), MEMSUFFIX)(CPUArchState *env, abi_ptr ptr,
                                       RES_TYPE v)
 {
-    trace_guest_mem_before_exec(
-        env_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_cpu(env), ptr, meminfo);
     glue(glue(st, SUFFIX), _p)(g2h(ptr), v);
+    qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, NULL, meminfo);
 }
 
 static inline void
diff --git a/tcg/i386/tcg-target.inc.c b/tcg/i386/tcg-target.inc.c
index 6ddeebf4bc..8519fd0eb0 100644
--- a/tcg/i386/tcg-target.inc.c
+++ b/tcg/i386/tcg-target.inc.c
@@ -1775,6 +1775,14 @@ 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(ArchCPU, neg.tlb.c.hostaddr) -
+                   offsetof(ArchCPU, env));
+    }
+#endif
 }
 
 /*
diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c
index 587d092238..e8094e27d0 100644
--- a/tcg/tcg-op.c
+++ b/tcg/tcg-op.c
@@ -30,6 +30,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
@@ -2684,6 +2685,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);
 }
 
@@ -2696,6 +2698,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);
@@ -2705,7 +2708,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);
@@ -2788,14 +2794,24 @@ 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)
 {
     TCGMemOp orig_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);
 
     orig_memop = memop;
     if (!TCG_TARGET_HAS_MEMORY_BSWAP && (memop & MO_BSWAP)) {
@@ -2807,6 +2823,7 @@ void tcg_gen_qemu_ld_i32(TCGv_i32 val, TCGv addr, TCGArg idx, TCGMemOp memop)
     }
 
     gen_ldst_i32(INDEX_op_qemu_ld_i32, val, addr, memop, idx);
+    plugin_gen_mem_callbacks(addr, info);
 
     if ((orig_memop ^ memop) & MO_BSWAP) {
         switch (orig_memop & MO_SIZE) {
@@ -2828,11 +2845,11 @@ void tcg_gen_qemu_ld_i32(TCGv_i32 val, TCGv addr, TCGArg idx, TCGMemOp memop)
 void tcg_gen_qemu_st_i32(TCGv_i32 val, TCGv addr, TCGArg idx, TCGMemOp memop)
 {
     TCGv_i32 swap = NULL;
+    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);
 
     if (!TCG_TARGET_HAS_MEMORY_BSWAP && (memop & MO_BSWAP)) {
         swap = tcg_temp_new_i32();
@@ -2852,6 +2869,7 @@ void tcg_gen_qemu_st_i32(TCGv_i32 val, TCGv addr, TCGArg idx, TCGMemOp memop)
     }
 
     gen_ldst_i32(INDEX_op_qemu_st_i32, val, addr, memop, idx);
+    plugin_gen_mem_callbacks(addr, info);
 
     if (swap) {
         tcg_temp_free_i32(swap);
@@ -2861,6 +2879,7 @@ void tcg_gen_qemu_st_i32(TCGv_i32 val, TCGv addr, TCGArg idx, TCGMemOp memop)
 void tcg_gen_qemu_ld_i64(TCGv_i64 val, TCGv addr, TCGArg idx, TCGMemOp memop)
 {
     TCGMemOp orig_memop;
+    uint8_t info;
 
     if (TCG_TARGET_REG_BITS == 32 && (memop & MO_SIZE) < MO_64) {
         tcg_gen_qemu_ld_i32(TCGV_LOW(val), addr, idx, memop);
@@ -2874,8 +2893,8 @@ void tcg_gen_qemu_ld_i64(TCGv_i64 val, TCGv addr, TCGArg idx, TCGMemOp memop)
 
     tcg_gen_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD);
     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);
 
     orig_memop = memop;
     if (!TCG_TARGET_HAS_MEMORY_BSWAP && (memop & MO_BSWAP)) {
@@ -2887,6 +2906,7 @@ void tcg_gen_qemu_ld_i64(TCGv_i64 val, TCGv addr, TCGArg idx, TCGMemOp memop)
     }
 
     gen_ldst_i64(INDEX_op_qemu_ld_i64, val, addr, memop, idx);
+    plugin_gen_mem_callbacks(addr, info);
 
     if ((orig_memop ^ memop) & MO_BSWAP) {
         switch (orig_memop & MO_SIZE) {
@@ -2914,6 +2934,7 @@ void tcg_gen_qemu_ld_i64(TCGv_i64 val, TCGv addr, TCGArg idx, TCGMemOp memop)
 void tcg_gen_qemu_st_i64(TCGv_i64 val, TCGv addr, TCGArg idx, TCGMemOp memop)
 {
     TCGv_i64 swap = NULL;
+    uint8_t info;
 
     if (TCG_TARGET_REG_BITS == 32 && (memop & MO_SIZE) < MO_64) {
         tcg_gen_qemu_st_i32(TCGV_LOW(val), addr, idx, memop);
@@ -2922,8 +2943,8 @@ void tcg_gen_qemu_st_i64(TCGv_i64 val, TCGv addr, TCGArg idx, TCGMemOp memop)
 
     tcg_gen_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST);
     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);
 
     if (!TCG_TARGET_HAS_MEMORY_BSWAP && (memop & MO_BSWAP)) {
         swap = tcg_temp_new_i64();
@@ -2947,6 +2968,7 @@ void tcg_gen_qemu_st_i64(TCGv_i64 val, TCGv addr, TCGArg idx, TCGMemOp memop)
     }
 
     gen_ldst_i64(INDEX_op_qemu_st_i64, val, addr, memop, idx);
+    plugin_gen_mem_callbacks(addr, info);
 
     if (swap) {
         tcg_temp_free_i64(swap);
diff --git a/tcg/tcg.h b/tcg/tcg.h
index 966e89104d..0e86e18ccb 100644
--- a/tcg/tcg.h
+++ b/tcg/tcg.h
@@ -28,6 +28,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"
-- 
2.20.1



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

* [Qemu-devel] [PATCH v3 20/50] translate-all: notify plugin code of tb_flush
  2019-06-14 17:11 [Qemu-devel] [PATCH v3 00/50] tcg plugin support Alex Bennée
                   ` (18 preceding siblings ...)
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 19/50] tcg: let plugins instrument memory accesses Alex Bennée
@ 2019-06-14 17:11 ` Alex Bennée
  2019-06-17 20:54   ` Richard Henderson
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 21/50] *-user: notify plugin of exit Alex Bennée
                   ` (33 subsequent siblings)
  53 siblings, 1 reply; 117+ messages in thread
From: Alex Bennée @ 2019-06-14 17:11 UTC (permalink / raw)
  To: qemu-devel
  Cc: Paolo Bonzini, Emilio G. Cota, Alex Bennée, Richard Henderson

From: "Emilio G. Cota" <cota@braap.org>

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 54998e39c6..28a92e6d8b 100644
--- a/accel/tcg/translate-all.c
+++ b/accel/tcg/translate-all.c
@@ -1231,6 +1231,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.
@@ -1238,6 +1240,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();
@@ -1262,6 +1265,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.20.1



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

* [Qemu-devel] [PATCH  v3 21/50] *-user: notify plugin of exit
  2019-06-14 17:11 [Qemu-devel] [PATCH v3 00/50] tcg plugin support Alex Bennée
                   ` (19 preceding siblings ...)
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 20/50] translate-all: notify plugin code of tb_flush Alex Bennée
@ 2019-06-14 17:11 ` Alex Bennée
  2019-06-17 20:54   ` Richard Henderson
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 22/50] *-user: plugin syscalls Alex Bennée
                   ` (32 subsequent siblings)
  53 siblings, 1 reply; 117+ messages in thread
From: Alex Bennée @ 2019-06-14 17:11 UTC (permalink / raw)
  To: qemu-devel; +Cc: Riku Voipio, Emilio G. Cota, Alex Bennée, Laurent Vivier

From: "Emilio G. Cota" <cota@braap.org>

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 1ee6195d9f..84a983a9a1 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 bdda720553..a362ef67d2 100644
--- a/linux-user/exit.c
+++ b/linux-user/exit.c
@@ -35,4 +35,5 @@ void preexit_cleanup(CPUArchState *env, int code)
         __gcov_dump();
 #endif
         gdb_exit(env, code);
+        qemu_plugin_atexit_cb();
 }
-- 
2.20.1



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

* [Qemu-devel] [PATCH  v3 22/50] *-user: plugin syscalls
  2019-06-14 17:11 [Qemu-devel] [PATCH v3 00/50] tcg plugin support Alex Bennée
                   ` (20 preceding siblings ...)
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 21/50] *-user: notify plugin of exit Alex Bennée
@ 2019-06-14 17:11 ` Alex Bennée
  2019-06-17 20:56   ` Richard Henderson
  2019-06-19  3:35   ` Pranith Kumar
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 23/50] cpu: hook plugin vcpu events Alex Bennée
                   ` (31 subsequent siblings)
  53 siblings, 2 replies; 117+ messages in thread
From: Alex Bennée @ 2019-06-14 17:11 UTC (permalink / raw)
  To: qemu-devel; +Cc: Riku Voipio, Emilio G. Cota, Laurent Vivier

From: "Emilio G. Cota" <cota@braap.org>

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 84a983a9a1..50e47d217c 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 b187c1281d..7f3cfdee84 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -11724,6 +11724,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);
@@ -11736,5 +11738,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.20.1



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

* [Qemu-devel] [PATCH  v3 23/50] cpu: hook plugin vcpu events
  2019-06-14 17:11 [Qemu-devel] [PATCH v3 00/50] tcg plugin support Alex Bennée
                   ` (21 preceding siblings ...)
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 22/50] *-user: plugin syscalls Alex Bennée
@ 2019-06-14 17:11 ` Alex Bennée
  2019-06-17 21:00   ` Richard Henderson
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 24/50] plugin-gen: add plugin_insn_append Alex Bennée
                   ` (30 subsequent siblings)
  53 siblings, 1 reply; 117+ messages in thread
From: Alex Bennée @ 2019-06-14 17:11 UTC (permalink / raw)
  To: qemu-devel; +Cc: Paolo Bonzini, Emilio G. Cota, Richard Henderson

From: "Emilio G. Cota" <cota@braap.org>

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 dde3b7b981..2f86af9a87 100644
--- a/cpus.c
+++ b/cpus.c
@@ -46,6 +46,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"
@@ -1243,9 +1244,18 @@ static void qemu_tcg_rr_wait_io_event(void)
 
 static void qemu_wait_io_event(CPUState *cpu)
 {
+    bool slept = false;
+
     while (cpu_thread_is_idle(cpu)) {
+        if (!slept) {
+            slept = true;
+            qemu_plugin_vcpu_idle_cb(cpu);
+        }
         qemu_cond_wait(cpu->halt_cond, &qemu_global_mutex);
     }
+    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 e7622d1956..4a29471c3d 100644
--- a/exec.c
+++ b/exec.c
@@ -974,6 +974,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 f376f782d8..90ebb214bb 100644
--- a/qom/cpu.c
+++ b/qom/cpu.c
@@ -33,6 +33,7 @@
 #include "hw/boards.h"
 #include "hw/qdev-properties.h"
 #include "trace-root.h"
+#include "qemu/plugin.h"
 
 CPUInterruptHandler cpu_interrupt_handler;
 
@@ -354,6 +355,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.20.1



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

* [Qemu-devel] [PATCH  v3 24/50] plugin-gen: add plugin_insn_append
  2019-06-14 17:11 [Qemu-devel] [PATCH v3 00/50] tcg plugin support Alex Bennée
                   ` (22 preceding siblings ...)
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 23/50] cpu: hook plugin vcpu events Alex Bennée
@ 2019-06-14 17:11 ` Alex Bennée
  2019-06-17 21:03   ` Richard Henderson
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 25/50] translator: add translator_ld{ub, sw, uw, l, q} Alex Bennée
                   ` (29 subsequent siblings)
  53 siblings, 1 reply; 117+ messages in thread
From: Alex Bennée @ 2019-06-14 17:11 UTC (permalink / raw)
  To: qemu-devel; +Cc: Paolo Bonzini, Emilio G. Cota, Richard Henderson

From: "Emilio G. Cota" <cota@braap.org>

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>

---
v3
  - use g_byte_array
---
 accel/tcg/plugin-gen.c    | 10 +++++++++-
 include/exec/plugin-gen.h | 23 ++++++++++++++---------
 2 files changed, 23 insertions(+), 10 deletions(-)

diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c
index 9d9ec29765..758fc5d099 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,
diff --git a/include/exec/plugin-gen.h b/include/exec/plugin-gen.h
index 449ea16034..316638c736 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,17 @@ 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;
+    }
+
+    insn->data = g_byte_array_append(insn->data, from, size);
+}
+
 #else /* !CONFIG_PLUGIN */
 
 static inline
@@ -60,6 +62,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 */
-- 
2.20.1



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

* [Qemu-devel] [PATCH  v3 25/50] translator: add translator_ld{ub, sw, uw, l, q}
  2019-06-14 17:11 [Qemu-devel] [PATCH v3 00/50] tcg plugin support Alex Bennée
                   ` (23 preceding siblings ...)
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 24/50] plugin-gen: add plugin_insn_append Alex Bennée
@ 2019-06-14 17:11 ` Alex Bennée
  2019-06-17 21:06   ` Richard Henderson
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 26/50] target/arm: call qemu_plugin_insn_append Alex Bennée
                   ` (28 subsequent siblings)
  53 siblings, 1 reply; 117+ messages in thread
From: Alex Bennée @ 2019-06-14 17:11 UTC (permalink / raw)
  To: qemu-devel; +Cc: Emilio G. Cota, Richard Henderson

From: "Emilio G. Cota" <cota@braap.org>

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

diff --git a/include/exec/translator.h b/include/exec/translator.h
index 180c51d509..33fa709ba6 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"
 
 
@@ -142,4 +145,29 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
 
 void translator_loop_temp_check(DisasContextBase *db);
 
-#endif /* EXEC__TRANSLATOR_H */
+#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.20.1



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

* [Qemu-devel] [PATCH v3 26/50] target/arm: call qemu_plugin_insn_append
  2019-06-14 17:11 [Qemu-devel] [PATCH v3 00/50] tcg plugin support Alex Bennée
                   ` (24 preceding siblings ...)
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 25/50] translator: add translator_ld{ub, sw, uw, l, q} Alex Bennée
@ 2019-06-14 17:11 ` Alex Bennée
  2019-06-17 22:28   ` Richard Henderson
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 27/50] target/ppc: fetch code with translator_ld Alex Bennée
                   ` (27 subsequent siblings)
  53 siblings, 1 reply; 117+ messages in thread
From: Alex Bennée @ 2019-06-14 17:11 UTC (permalink / raw)
  To: qemu-devel; +Cc: Peter Maydell, Emilio G. Cota, open list:ARM TCG CPUs

From: "Emilio G. Cota" <cota@braap.org>

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 ae739f6575..25dd34a745 100644
--- a/target/arm/translate-a64.c
+++ b/target/arm/translate-a64.c
@@ -39,6 +39,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;
@@ -14205,6 +14206,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 c274c8b460..d049844b4a 100644
--- a/target/arm/translate.c
+++ b/target/arm/translate.c
@@ -12122,6 +12122,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);
@@ -12192,11 +12193,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.20.1



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

* [Qemu-devel] [PATCH v3 27/50] target/ppc: fetch code with translator_ld
  2019-06-14 17:11 [Qemu-devel] [PATCH v3 00/50] tcg plugin support Alex Bennée
                   ` (25 preceding siblings ...)
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 26/50] target/arm: call qemu_plugin_insn_append Alex Bennée
@ 2019-06-14 17:11 ` Alex Bennée
  2019-06-17 22:30   ` Richard Henderson
  2019-06-19  9:39   ` David Gibson
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 28/50] target/sh4: " Alex Bennée
                   ` (26 subsequent siblings)
  53 siblings, 2 replies; 117+ messages in thread
From: Alex Bennée @ 2019-06-14 17:11 UTC (permalink / raw)
  To: qemu-devel; +Cc: Emilio G. Cota, open list:PowerPC TCG CPUs, David Gibson

From: "Emilio G. Cota" <cota@braap.org>

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 4a5de28036..a27b5659f4 100644
--- a/target/ppc/translate.c
+++ b/target/ppc/translate.c
@@ -7864,11 +7864,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.20.1



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

* [Qemu-devel] [PATCH v3 28/50] target/sh4: fetch code with translator_ld
  2019-06-14 17:11 [Qemu-devel] [PATCH v3 00/50] tcg plugin support Alex Bennée
                   ` (26 preceding siblings ...)
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 27/50] target/ppc: fetch code with translator_ld Alex Bennée
@ 2019-06-14 17:11 ` Alex Bennée
  2019-06-17 22:33   ` Richard Henderson
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 29/50] target/i386: " Alex Bennée
                   ` (25 subsequent siblings)
  53 siblings, 1 reply; 117+ messages in thread
From: Alex Bennée @ 2019-06-14 17:11 UTC (permalink / raw)
  To: qemu-devel; +Cc: Emilio G. Cota, Alex Bennée, Aurelien Jarno

From: "Emilio G. Cota" <cota@braap.org>

There is a small wrinkle with the gUSA instruction. The translator
effectively treats a (known) gUSA sequence as a single instruction.
For the purposes of the plugin we end up with a long multi-instruction
qemu_plugin_insn.

If the known sequence isn't detected we shall never run this
translation anyway.

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

diff --git a/target/sh4/translate.c b/target/sh4/translate.c
index 5a7d8c4535..922785e225 100644
--- a/target/sh4/translate.c
+++ b/target/sh4/translate.c
@@ -1917,7 +1917,7 @@ static void decode_gusa(DisasContext *ctx, CPUSH4State *env)
 
     /* Read all of the insns for the region.  */
     for (i = 0; i < max_insns; ++i) {
-        insns[i] = cpu_lduw_code(env, pc + i * 2);
+        insns[i] = translator_lduw(env, pc + i * 2);
     }
 
     ld_adr = ld_dst = ld_mop = -1;
@@ -2332,7 +2332,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.20.1



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

* [Qemu-devel] [PATCH v3 29/50] target/i386: fetch code with translator_ld
  2019-06-14 17:11 [Qemu-devel] [PATCH v3 00/50] tcg plugin support Alex Bennée
                   ` (27 preceding siblings ...)
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 28/50] target/sh4: " Alex Bennée
@ 2019-06-14 17:11 ` Alex Bennée
  2019-06-17 22:33   ` Richard Henderson
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 30/50] target/hppa: " Alex Bennée
                   ` (24 subsequent siblings)
  53 siblings, 1 reply; 117+ messages in thread
From: Alex Bennée @ 2019-06-14 17:11 UTC (permalink / raw)
  To: qemu-devel
  Cc: Paolo Bonzini, Emilio G. Cota, Eduardo Habkost, Richard Henderson

From: "Emilio G. Cota" <cota@braap.org>

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 03150a86e2..bf4cd875ab 100644
--- a/target/i386/translate.c
+++ b/target/i386/translate.c
@@ -1925,28 +1925,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.20.1



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

* [Qemu-devel] [PATCH v3 30/50] target/hppa: fetch code with translator_ld
  2019-06-14 17:11 [Qemu-devel] [PATCH v3 00/50] tcg plugin support Alex Bennée
                   ` (28 preceding siblings ...)
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 29/50] target/i386: " Alex Bennée
@ 2019-06-14 17:11 ` Alex Bennée
  2019-06-17 22:34   ` Richard Henderson
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 31/50] target/m68k: " Alex Bennée
                   ` (23 subsequent siblings)
  53 siblings, 1 reply; 117+ messages in thread
From: Alex Bennée @ 2019-06-14 17:11 UTC (permalink / raw)
  To: qemu-devel; +Cc: Emilio G. Cota, Richard Henderson

From: "Emilio G. Cota" <cota@braap.org>

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 188fe688cb..36a784e293 100644
--- a/target/hppa/translate.c
+++ b/target/hppa/translate.c
@@ -4217,7 +4217,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.20.1



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

* [Qemu-devel] [PATCH v3 31/50] target/m68k: fetch code with translator_ld
  2019-06-14 17:11 [Qemu-devel] [PATCH v3 00/50] tcg plugin support Alex Bennée
                   ` (29 preceding siblings ...)
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 30/50] target/hppa: " Alex Bennée
@ 2019-06-14 17:11 ` Alex Bennée
  2019-06-17 22:35   ` Richard Henderson
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 32/50] target/alpha: " Alex Bennée
                   ` (22 subsequent siblings)
  53 siblings, 1 reply; 117+ messages in thread
From: Alex Bennée @ 2019-06-14 17:11 UTC (permalink / raw)
  To: qemu-devel; +Cc: Emilio G. Cota, Laurent Vivier

From: "Emilio G. Cota" <cota@braap.org>

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 2ae537461f..dc05c0d2ad 100644
--- a/target/m68k/translate.c
+++ b/target/m68k/translate.c
@@ -378,7 +378,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.20.1



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

* [Qemu-devel] [PATCH v3 32/50] target/alpha: fetch code with translator_ld
  2019-06-14 17:11 [Qemu-devel] [PATCH v3 00/50] tcg plugin support Alex Bennée
                   ` (30 preceding siblings ...)
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 31/50] target/m68k: " Alex Bennée
@ 2019-06-14 17:11 ` Alex Bennée
  2019-06-17 22:35   ` Richard Henderson
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 33/50] target/riscv: " Alex Bennée
                   ` (21 subsequent siblings)
  53 siblings, 1 reply; 117+ messages in thread
From: Alex Bennée @ 2019-06-14 17:11 UTC (permalink / raw)
  To: qemu-devel; +Cc: Emilio G. Cota, Richard Henderson

From: "Emilio G. Cota" <cota@braap.org>

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 2c9cccf6c1..daf1e05cba 100644
--- a/target/alpha/translate.c
+++ b/target/alpha/translate.c
@@ -2989,7 +2989,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.20.1



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

* [Qemu-devel] [PATCH v3 33/50] target/riscv: fetch code with translator_ld
  2019-06-14 17:11 [Qemu-devel] [PATCH v3 00/50] tcg plugin support Alex Bennée
                   ` (31 preceding siblings ...)
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 32/50] target/alpha: " Alex Bennée
@ 2019-06-14 17:11 ` Alex Bennée
  2019-06-17 22:38   ` Richard Henderson
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 34/50] target/sparc: " Alex Bennée
                   ` (20 subsequent siblings)
  53 siblings, 1 reply; 117+ messages in thread
From: Alex Bennée @ 2019-06-14 17:11 UTC (permalink / raw)
  To: qemu-devel
  Cc: open list:RISC-V TCG CPUs, Sagar Karandikar, Bastian Koppelmann,
	Palmer Dabbelt, Emilio G. Cota, Alistair Francis

From: "Emilio G. Cota" <cota@braap.org>

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 313c27b700..899abf41fa 100644
--- a/target/riscv/translate.c
+++ b/target/riscv/translate.c
@@ -793,7 +793,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(ctx);
     ctx->base.pc_next = ctx->pc_succ_insn;
 
-- 
2.20.1



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

* [Qemu-devel] [PATCH v3 34/50] target/sparc: fetch code with translator_ld
  2019-06-14 17:11 [Qemu-devel] [PATCH v3 00/50] tcg plugin support Alex Bennée
                   ` (32 preceding siblings ...)
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 33/50] target/riscv: " Alex Bennée
@ 2019-06-14 17:11 ` Alex Bennée
  2019-06-17 22:39   ` Richard Henderson
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 35/50] target/xtensa: " Alex Bennée
                   ` (19 subsequent siblings)
  53 siblings, 1 reply; 117+ messages in thread
From: Alex Bennée @ 2019-06-14 17:11 UTC (permalink / raw)
  To: qemu-devel; +Cc: Emilio G. Cota, Mark Cave-Ayland, Artyom Tarasenko

From: "Emilio G. Cota" <cota@braap.org>

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 091bab53af..c91ff11809 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.20.1



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

* [Qemu-devel] [PATCH v3 35/50] target/xtensa: fetch code with translator_ld
  2019-06-14 17:11 [Qemu-devel] [PATCH v3 00/50] tcg plugin support Alex Bennée
                   ` (33 preceding siblings ...)
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 34/50] target/sparc: " Alex Bennée
@ 2019-06-14 17:11 ` Alex Bennée
  2019-06-17 22:41   ` Richard Henderson
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 36/50] target/openrisc: " Alex Bennée
                   ` (18 subsequent siblings)
  53 siblings, 1 reply; 117+ messages in thread
From: Alex Bennée @ 2019-06-14 17:11 UTC (permalink / raw)
  To: qemu-devel; +Cc: Max Filippov, Emilio G. Cota

From: "Emilio G. Cota" <cota@braap.org>

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 6f1da87875..cb849ae2d9 100644
--- a/target/xtensa/translate.c
+++ b/target/xtensa/translate.c
@@ -863,7 +863,7 @@ static int arg_copy_compare(const void *a, const void *b)
 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;
@@ -887,7 +887,7 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc)
 
     dc->base.pc_next = dc->pc + len;
     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.20.1



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

* [Qemu-devel] [PATCH v3 36/50] target/openrisc: fetch code with translator_ld
  2019-06-14 17:11 [Qemu-devel] [PATCH v3 00/50] tcg plugin support Alex Bennée
                   ` (34 preceding siblings ...)
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 35/50] target/xtensa: " Alex Bennée
@ 2019-06-14 17:11 ` Alex Bennée
  2019-06-17 22:41   ` Richard Henderson
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 37/50] translator: inject instrumentation from plugins Alex Bennée
                   ` (17 subsequent siblings)
  53 siblings, 1 reply; 117+ messages in thread
From: Alex Bennée @ 2019-06-14 17:11 UTC (permalink / raw)
  To: qemu-devel; +Cc: Stafford Horne, Emilio G. Cota

From: "Emilio G. Cota" <cota@braap.org>

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 4360ce4045..7df4c68d15 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.20.1



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

* [Qemu-devel] [PATCH v3 37/50] translator: inject instrumentation from plugins
  2019-06-14 17:11 [Qemu-devel] [PATCH v3 00/50] tcg plugin support Alex Bennée
                   ` (35 preceding siblings ...)
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 36/50] target/openrisc: " Alex Bennée
@ 2019-06-14 17:11 ` Alex Bennée
  2019-06-17 22:44   ` Richard Henderson
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 38/50] plugin: add API symbols to qemu-plugins.symbols Alex Bennée
                   ` (16 subsequent siblings)
  53 siblings, 1 reply; 117+ messages in thread
From: Alex Bennée @ 2019-06-14 17:11 UTC (permalink / raw)
  To: qemu-devel; +Cc: Paolo Bonzini, Emilio G. Cota, Richard Henderson

From: "Emilio G. Cota" <cota@braap.org>

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 9226a348a3..161b494a54 100644
--- a/accel/tcg/translator.c
+++ b/accel/tcg/translator.c
@@ -16,6 +16,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
@@ -34,6 +35,7 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
                      CPUState *cpu, TranslationBlock *tb, int max_insns)
 {
     int bp_insn = 0;
+    bool plugin_enabled;
 
     /* Initialize DisasContext */
     db->tb = tb;
@@ -55,11 +57,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))) {
@@ -95,6 +103,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;
@@ -112,6 +124,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.20.1



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

* [Qemu-devel] [PATCH v3 38/50] plugin: add API symbols to qemu-plugins.symbols
  2019-06-14 17:11 [Qemu-devel] [PATCH v3 00/50] tcg plugin support Alex Bennée
                   ` (36 preceding siblings ...)
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 37/50] translator: inject instrumentation from plugins Alex Bennée
@ 2019-06-14 17:11 ` Alex Bennée
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 39/50] vl: support -plugin option Alex Bennée
                   ` (15 subsequent siblings)
  53 siblings, 0 replies; 117+ messages in thread
From: Alex Bennée @ 2019-06-14 17:11 UTC (permalink / raw)
  To: qemu-devel; +Cc: Emilio G. Cota, Alex Bennée

From: "Emilio G. Cota" <cota@braap.org>

Signed-off-by: Emilio G. Cota <cota@braap.org>
[AJB: moved into plugins]
Signed-off-by: Alex Bennée <alex.bennee@linaro.org>

---
v3
  - moved to plugins/
  - include qemu_plugin_reset
---
 Makefile                     |  1 +
 configure                    | 69 ++++++++++++++++++++++++++++++++++++
 plugins/.gitignore           |  2 ++
 plugins/Makefile.objs        | 14 ++++++++
 plugins/qemu-plugins.symbols | 35 ++++++++++++++++++
 5 files changed, 121 insertions(+)
 create mode 100644 plugins/.gitignore
 create mode 100644 plugins/qemu-plugins.symbols

diff --git a/Makefile b/Makefile
index 37d396ab88..f3f0c68e85 100644
--- a/Makefile
+++ b/Makefile
@@ -718,6 +718,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/configure b/configure
index 48f3263e55..d3bf254191 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
 
@@ -5496,6 +5497,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.
@@ -7307,6 +7360,22 @@ 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/plugins/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/plugins/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
diff --git a/plugins/.gitignore b/plugins/.gitignore
new file mode 100644
index 0000000000..7b8aaa1f10
--- /dev/null
+++ b/plugins/.gitignore
@@ -0,0 +1,2 @@
+qemu-plugins-ld.symbols
+qemu-plugins-ld64.symbols
diff --git a/plugins/Makefile.objs b/plugins/Makefile.objs
index 95baabf3d2..6f14d91ccb 100644
--- a/plugins/Makefile.objs
+++ b/plugins/Makefile.objs
@@ -5,3 +5,17 @@
 obj-y += loader.o
 obj-y += core.o
 obj-y += api.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
+api.o-libs := -Wl,--dynamic-list=$(BUILD_DIR)/qemu-plugins-ld.symbols
+else
+ifdef CONFIG_HAS_LD_EXPORTED_SYMBOLS_LIST
+api.o-libs := -Wl,-exported_symbols_list,$(BUILD_DIR)/qemu-plugins-ld64.symbols
+endif
+endif
diff --git a/plugins/qemu-plugins.symbols b/plugins/qemu-plugins.symbols
new file mode 100644
index 0000000000..38d47b7922
--- /dev/null
+++ b/plugins/qemu-plugins.symbols
@@ -0,0 +1,35 @@
+{
+  qemu_plugin_uninstall;
+  qemu_plugin_reset;
+  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.20.1



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

* [Qemu-devel] [PATCH  v3 39/50] vl: support -plugin option
  2019-06-14 17:11 [Qemu-devel] [PATCH v3 00/50] tcg plugin support Alex Bennée
                   ` (37 preceding siblings ...)
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 38/50] plugin: add API symbols to qemu-plugins.symbols Alex Bennée
@ 2019-06-14 17:11 ` Alex Bennée
  2019-06-17 22:53   ` Richard Henderson
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 40/50] linux-user: " Alex Bennée
                   ` (14 subsequent siblings)
  53 siblings, 1 reply; 117+ messages in thread
From: Alex Bennée @ 2019-06-14 17:11 UTC (permalink / raw)
  To: qemu-devel; +Cc: Paolo Bonzini, Emilio G . Cota, 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>
---
 qemu-options.hx | 17 +++++++++++++++++
 vl.c            | 11 +++++++++++
 2 files changed, 28 insertions(+)

diff --git a/qemu-options.hx b/qemu-options.hx
index 0d8beb4afd..47bbf358db 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -4130,6 +4130,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)
diff --git a/vl.c b/vl.c
index 005468cbfb..0be9667315 100644
--- a/vl.c
+++ b/vl.c
@@ -110,6 +110,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"
 
@@ -2954,6 +2955,7 @@ int main(int argc, char **argv, char **envp)
     bool list_data_dirs = false;
     char *dir, **dirs;
     BlockdevOptionsQueue bdo_queue = QSIMPLEQ_HEAD_INITIALIZER(bdo_queue);
+    QemuPluginList plugin_list = QTAILQ_HEAD_INITIALIZER(plugin_list);
 
     error_init(argv[0]);
     module_call_init(MODULE_INIT_TRACE);
@@ -2982,6 +2984,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);
@@ -3760,6 +3763,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);
@@ -4068,6 +4074,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"),
-- 
2.20.1



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

* [Qemu-devel] [PATCH  v3 40/50] linux-user: support -plugin option
  2019-06-14 17:11 [Qemu-devel] [PATCH v3 00/50] tcg plugin support Alex Bennée
                   ` (38 preceding siblings ...)
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 39/50] vl: support -plugin option Alex Bennée
@ 2019-06-14 17:11 ` Alex Bennée
  2019-06-17 22:54   ` Richard Henderson
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 41/50] tests/plugin: add sample plugins Alex Bennée
                   ` (13 subsequent siblings)
  53 siblings, 1 reply; 117+ messages in thread
From: Alex Bennée @ 2019-06-14 17:11 UTC (permalink / raw)
  To: qemu-devel
  Cc: Riku Voipio, Emilio G . Cota, Lluís Vilanova, Laurent Vivier

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 a59ae9439d..1009eab92d 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -33,6 +33,7 @@
 #include "qemu/error-report.h"
 #include "qemu/help_option.h"
 #include "qemu/module.h"
+#include "qemu/plugin.h"
 #include "cpu.h"
 #include "exec/exec-all.h"
 #include "tcg.h"
@@ -387,6 +388,15 @@ static void handle_arg_trace(const char *arg)
     trace_file = trace_opt_parse(arg);
 }
 
+static QemuPluginList 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;
@@ -438,6 +448,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}
@@ -628,6 +642,7 @@ int main(int argc, char **argv, char **envp)
     cpu_model = NULL;
 
     qemu_add_opts(&qemu_trace_opts);
+    qemu_plugin_add_opts();
 
     optind = parse_args(argc, argv);
 
@@ -635,6 +650,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.20.1



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

* [Qemu-devel] [PATCH  v3 41/50] tests/plugin: add sample plugins
  2019-06-14 17:11 [Qemu-devel] [PATCH v3 00/50] tcg plugin support Alex Bennée
                   ` (39 preceding siblings ...)
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 40/50] linux-user: " Alex Bennée
@ 2019-06-14 17:11 ` Alex Bennée
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 42/50] tests/tcg: enable plugin testing Alex Bennée
                   ` (12 subsequent siblings)
  53 siblings, 0 replies; 117+ messages in thread
From: Alex Bennée @ 2019-06-14 17:11 UTC (permalink / raw)
  To: qemu-devel; +Cc: Emilio G. Cota

From: "Emilio G. Cota" <cota@braap.org>

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

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

diff --git a/configure b/configure
index d3bf254191..2af8c436f2 100755
--- a/configure
+++ b/configure
@@ -7995,14 +7995,14 @@ fi
 # tests might fail. Prefer to keep the relevant files in their own
 # directory and symlink the directory instead.
 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 tests/qgraph"
+DIRS="$DIRS tests/fp tests/qgraph 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"
 LINKS="Makefile tests/tcg/Makefile"
 LINKS="$LINKS tests/tcg/cris/Makefile tests/tcg/cris/.gdbinit"
 LINKS="$LINKS tests/tcg/lm32/Makefile tests/tcg/xtensa/Makefile po/Makefile"
-LINKS="$LINKS tests/fp/Makefile"
+LINKS="$LINKS tests/fp/Makefile tests/plugin/Makefile"
 LINKS="$LINKS pc-bios/optionrom/Makefile pc-bios/keymaps"
 LINKS="$LINKS pc-bios/spapr-rtas/Makefile"
 LINKS="$LINKS pc-bios/s390-ccw/Makefile"
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
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;
+}
-- 
2.20.1



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

* [Qemu-devel] [PATCH  v3 42/50] tests/tcg: enable plugin testing
  2019-06-14 17:11 [Qemu-devel] [PATCH v3 00/50] tcg plugin support Alex Bennée
                   ` (40 preceding siblings ...)
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 41/50] tests/plugin: add sample plugins Alex Bennée
@ 2019-06-14 17:11 ` Alex Bennée
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 43/50] tests/plugin: add a hotblocks plugin Alex Bennée
                   ` (11 subsequent siblings)
  53 siblings, 0 replies; 117+ messages in thread
From: Alex Bennée @ 2019-06-14 17:11 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, open list:ARM TCG CPUs, Alex Bennée,
	Philippe Mathieu-Daudé

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 db750dd6d0..04b0460262 100644
--- a/tests/Makefile.include
+++ b/tests/Makefile.include
@@ -1047,6 +1047,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))
@@ -1061,7 +1069,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 6fa63cc8d5..9474ab2900 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 49d48d8a1c..cd628306b3 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] 117+ messages in thread

* [Qemu-devel] [PATCH v3 43/50] tests/plugin: add a hotblocks plugin
  2019-06-14 17:11 [Qemu-devel] [PATCH v3 00/50] tcg plugin support Alex Bennée
                   ` (41 preceding siblings ...)
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 42/50] tests/tcg: enable plugin testing Alex Bennée
@ 2019-06-14 17:11 ` Alex Bennée
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 44/50] plugin: add qemu_plugin_insn_disas helper Alex Bennée
                   ` (10 subsequent siblings)
  53 siblings, 0 replies; 117+ messages in thread
From: Alex Bennée @ 2019-06-14 17:11 UTC (permalink / raw)
  To: qemu-devel; +Cc: Alex Bennée

This is a simple plugin to track which translation blocks are call
most often. As we don't have a view of the internals of TCG we can
only work by the address of the start of the block so we also need to
tracks how often the address is translated.

As there will be multiple blocks starting at the same address. We can
try and work around this by futzing the value to feed to the hash with
the insn count.

Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
---
 tests/plugin/Makefile    |   1 +
 tests/plugin/hotblocks.c | 145 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 146 insertions(+)
 create mode 100644 tests/plugin/hotblocks.c

diff --git a/tests/plugin/Makefile b/tests/plugin/Makefile
index f9a3546ea3..e74940eaac 100644
--- a/tests/plugin/Makefile
+++ b/tests/plugin/Makefile
@@ -10,6 +10,7 @@ NAMES += bb
 NAMES += empty
 NAMES += insn
 NAMES += mem
+NAMES += hotblocks
 
 SONAMES := $(addsuffix .so,$(addprefix lib,$(NAMES)))
 
diff --git a/tests/plugin/hotblocks.c b/tests/plugin/hotblocks.c
new file mode 100644
index 0000000000..acb890e320
--- /dev/null
+++ b/tests/plugin/hotblocks.c
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2019, Alex Bennée <alex.bennee@linaro.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 <inttypes.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <glib.h>
+
+#include <qemu-plugin.h>
+
+static bool do_inline;
+static int stdout_fd;
+
+/* Plugins need to take care of their own locking */
+static GMutex lock;
+static GHashTable *hotblocks;
+static guint64 limit = 20;
+
+/*
+ * Counting Structure
+ *
+ * The internals of the TCG are not exposed to plugins so we can only
+ * get the starting PC for each block. As a result we will see
+ * multiple translations at the same PC, we aggregate them here.
+ */
+typedef struct {
+    uint64_t start_addr;
+    uint64_t exec_count;
+    int      trans_count;
+    unsigned long insns;
+} ExecCount;
+
+static gint cmp_exec_count(gconstpointer a, gconstpointer b)
+{
+    ExecCount *ea = (ExecCount *) a;
+    ExecCount *eb = (ExecCount *) b;
+    return ea->exec_count > eb->exec_count ? -1 : 1;
+}
+
+static void plugin_exit(qemu_plugin_id_t id, void *p)
+{
+    GString *report = g_string_new("collected ");
+    GList *counts, *it;
+    int i;
+
+    g_mutex_lock(&lock);
+    g_string_append_printf(report, "%d entries in the hash table\n",
+                           g_hash_table_size(hotblocks));
+    counts = g_hash_table_get_values(hotblocks);
+    it = g_list_sort(counts, cmp_exec_count);
+
+    for (i = 0; i < limit && it->next; i++, it = it->next) {
+        ExecCount *rec = (ExecCount *) it->data;
+        g_string_append_printf(report, "  pc: %#" PRIx64 " (%d block%s, %ld insns, %" PRId64" total hits)\n",
+                               rec->start_addr,
+                               rec->trans_count, rec->trans_count < 2 ? "" : "s",
+                               rec->insns, rec->exec_count);
+    }
+
+    g_mutex_unlock(&lock);
+    g_list_free(it);
+
+    dprintf(stdout_fd, "%s", report->str);
+    g_string_free(report, true);
+}
+
+static void plugin_init(void)
+{
+    hotblocks = g_hash_table_new(NULL, g_direct_equal);
+}
+
+static void vcpu_tb_exec(unsigned int cpu_index, void *udata)
+{
+    ExecCount *cnt;
+    uint64_t cheap_hash = (uint64_t) udata;
+
+    g_mutex_lock(&lock);
+    cnt = (ExecCount *) g_hash_table_lookup(hotblocks, (gconstpointer) cheap_hash);
+    /* should always succeed */
+    g_assert(cnt);
+    cnt->exec_count++;
+    g_mutex_unlock(&lock);
+}
+
+/*
+ * When do_inline we ask the plugin to increment the counter for us.
+ * Otherwise a helper is inserted which calls the vcpu_tb_exec
+ * callback.
+ */
+static void vcpu_tb_trans(qemu_plugin_id_t id, unsigned int cpu_index,
+                          struct qemu_plugin_tb *tb)
+{
+    ExecCount *cnt;
+    uint64_t pc = qemu_plugin_tb_vaddr(tb);
+    unsigned long insns = qemu_plugin_tb_n_insns(tb);
+    uint64_t cheap_hash = pc ^ insns;
+
+    g_mutex_lock(&lock);
+    cnt = (ExecCount *) g_hash_table_lookup(hotblocks, (gconstpointer) cheap_hash);
+    if (cnt) {
+        cnt->trans_count++;
+    } else {
+        cnt = g_new0(ExecCount, 1);
+        cnt->start_addr = pc;
+        cnt->trans_count = 1;
+        cnt->insns = insns;
+        g_hash_table_insert(hotblocks, (gpointer) cheap_hash, (gpointer) cnt);
+    }
+
+    g_mutex_unlock(&lock);
+
+    if (do_inline) {
+        qemu_plugin_register_vcpu_tb_exec_inline(tb, QEMU_PLUGIN_INLINE_ADD_U64,
+                                                 &cnt->exec_count, 1);
+    } else {
+        qemu_plugin_register_vcpu_tb_exec_cb(tb, vcpu_tb_exec,
+                                             QEMU_PLUGIN_CB_NO_REGS,
+                                             (void *)cheap_hash);
+    }
+}
+
+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);
+
+    plugin_init();
+
+    qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans);
+    qemu_plugin_register_atexit_cb(id, plugin_exit, NULL);
+    return 0;
+}
-- 
2.20.1



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

* [Qemu-devel] [PATCH v3 44/50] plugin: add qemu_plugin_insn_disas helper
  2019-06-14 17:11 [Qemu-devel] [PATCH v3 00/50] tcg plugin support Alex Bennée
                   ` (42 preceding siblings ...)
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 43/50] tests/plugin: add a hotblocks plugin Alex Bennée
@ 2019-06-14 17:11 ` Alex Bennée
  2019-06-17 23:09   ` Richard Henderson
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 45/50] tests/plugin: add instruction execution breakdown Alex Bennée
                   ` (9 subsequent siblings)
  53 siblings, 1 reply; 117+ messages in thread
From: Alex Bennée @ 2019-06-14 17:11 UTC (permalink / raw)
  To: qemu-devel; +Cc: Alex Bennée

Give the plugins access to the QEMU dissasembler so they don't have to
re-invent the wheel.

Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
---
 disas.c                      | 103 +++++++++++++++++++++++++++++++++++
 include/disas/disas.h        |   2 +
 include/qemu/qemu-plugin.h   |   9 +++
 plugins/api.c                |   7 +++
 plugins/qemu-plugins.symbols |   1 +
 5 files changed, 122 insertions(+)

diff --git a/disas.c b/disas.c
index 3e2bfa572b..d8b75f0b73 100644
--- a/disas.c
+++ b/disas.c
@@ -475,6 +475,109 @@ void target_disas(FILE *out, CPUState *cpu, target_ulong code,
     }
 }
 
+static GString plugin_disas_output;
+
+static int plugin_printf(FILE *stream, const char *fmt, ...)
+{
+    va_list va;
+    GString *s = &plugin_disas_output;
+    int initial_len = s->len;
+
+    va_start(va, fmt);
+    g_string_append_printf(s, fmt, va);
+    va_end(va);
+
+    return s->len - initial_len;
+}
+
+static void plugin_print_address (bfd_vma addr, struct disassemble_info *info)
+{
+    /* does nothing */
+}
+
+/* Disassemble a single instruction directly into plugin output */
+static bool plugin_cap_disas_insn(disassemble_info *info, uint64_t pc, size_t size)
+{
+    uint8_t cap_buf[1024];
+    csh handle;
+    cs_insn *insn;
+    size_t csize = 0;
+    int count;
+    GString *s = &plugin_disas_output;
+
+    if (cap_disas_start(info, &handle) != CS_ERR_OK) {
+        return false;
+    }
+    insn = cap_insn;
+
+    size_t tsize = MIN(sizeof(cap_buf) - csize, size);
+    const uint8_t *cbuf = cap_buf;
+    target_read_memory(pc, cap_buf, tsize, info);
+
+    count = cs_disasm(handle, cbuf, size, 0, 1, &insn);
+
+    if (count) {
+        g_string_printf(s, "%s %s", insn->mnemonic, insn->op_str);
+    } else {
+        g_string_printf(s, "cs_disasm failed");
+    }
+
+    cs_close(&handle);
+    return true;
+}
+
+char * plugin_disas(CPUState *cpu, uint64_t addr, size_t size)
+{
+    CPUClass *cc = CPU_GET_CLASS(cpu);
+    target_ulong pc;
+    int count;
+    CPUDebug s;
+    GString *ds = g_string_set_size(&plugin_disas_output, 0);
+
+    g_assert(ds == &plugin_disas_output);
+
+    INIT_DISASSEMBLE_INFO(s.info, NULL, plugin_printf);
+
+    s.cpu = cpu;
+    s.info.read_memory_func = target_read_memory;
+    s.info.buffer_vma = addr;
+    s.info.buffer_length = size;
+    s.info.print_address_func = plugin_print_address;
+    s.info.cap_arch = -1;
+    s.info.cap_mode = 0;
+    s.info.cap_insn_unit = 4;
+    s.info.cap_insn_split = 4;
+
+#ifdef TARGET_WORDS_BIGENDIAN
+    s.info.endian = BFD_ENDIAN_BIG;
+#else
+    s.info.endian = BFD_ENDIAN_LITTLE;
+#endif
+
+    if (cc->disas_set_info) {
+        cc->disas_set_info(cpu, &s.info);
+    }
+
+    if (s.info.cap_arch >= 0 && plugin_cap_disas_insn(&s.info, addr, size)) {
+        return g_strdup(ds->str);
+    }
+
+    if (s.info.print_insn == NULL) {
+        s.info.print_insn = print_insn_od_target;
+    }
+
+    for (pc = addr; size > 0; pc += count, size -= count) {
+        count = s.info.print_insn(pc, &s.info);
+
+        if (count < 0)
+            break;
+
+        g_assert(size >= count);
+    }
+
+    return g_strdup(ds->str);
+}
+
 /* Disassemble this for me please... (debugging). */
 void disas(FILE *out, void *code, unsigned long size)
 {
diff --git a/include/disas/disas.h b/include/disas/disas.h
index 15da511f49..119df9e9bd 100644
--- a/include/disas/disas.h
+++ b/include/disas/disas.h
@@ -13,6 +13,8 @@ void target_disas(FILE *out, CPUState *cpu, target_ulong code,
 void monitor_disas(Monitor *mon, CPUState *cpu,
                    target_ulong pc, int nb_insn, int is_physical);
 
+char * plugin_disas(CPUState *cpu, uint64_t addr, size_t size);
+
 /* Look up symbol for debugging purpose.  Returns "" if unknown. */
 const char *lookup_symbol(target_ulong orig_addr);
 #endif
diff --git a/include/qemu/qemu-plugin.h b/include/qemu/qemu-plugin.h
index 0db1ef9714..21f056d4ab 100644
--- a/include/qemu/qemu-plugin.h
+++ b/include/qemu/qemu-plugin.h
@@ -312,6 +312,15 @@ qemu_plugin_register_vcpu_syscall_ret_cb(qemu_plugin_id_t id,
                                          qemu_plugin_vcpu_syscall_ret_cb_t cb);
 
 
+/**
+ * qemu_plugin_insn_disas() - return disassembly string for instruction
+ * @insn: instruction reference
+ *
+ * Returns an allocated string containing the disassembly
+ */
+
+char * qemu_plugin_insn_disas(const struct qemu_plugin_insn *insn);
+
 /**
  * qemu_plugin_vcpu_for_each() - iterate over the existing vCPU
  * @id: plugin ID
diff --git a/plugins/api.c b/plugins/api.c
index 1c3aa64702..c0a1e98e68 100644
--- a/plugins/api.c
+++ b/plugins/api.c
@@ -40,6 +40,7 @@
 #include "sysemu/sysemu.h"
 #include "tcg/tcg.h"
 #include "trace/mem-internal.h" /* mem_info macros */
+#include "disas/disas.h"
 #include "plugin.h"
 
 /* Uninstall and Reset handlers */
@@ -218,6 +219,12 @@ void *qemu_plugin_insn_haddr(const struct qemu_plugin_insn *insn)
     return insn->haddr;
 }
 
+char *qemu_plugin_insn_disas(const struct qemu_plugin_insn *insn)
+{
+    CPUState *cpu = current_cpu;
+    return plugin_disas(cpu, insn->vaddr, insn->data->len);
+}
+
 /*
  * The memory queries allow the plugin to query information about a
  * memory access.
diff --git a/plugins/qemu-plugins.symbols b/plugins/qemu-plugins.symbols
index 38d47b7922..3fd51112b5 100644
--- a/plugins/qemu-plugins.symbols
+++ b/plugins/qemu-plugins.symbols
@@ -25,6 +25,7 @@
   qemu_plugin_insn_size;
   qemu_plugin_insn_vaddr;
   qemu_plugin_insn_haddr;
+  qemu_plugin_insn_disas;
   qemu_plugin_mem_size_shift;
   qemu_plugin_mem_is_sign_extended;
   qemu_plugin_mem_is_big_endian;
-- 
2.20.1



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

* [Qemu-devel] [PATCH v3 45/50] tests/plugin: add instruction execution breakdown
  2019-06-14 17:11 [Qemu-devel] [PATCH v3 00/50] tcg plugin support Alex Bennée
                   ` (43 preceding siblings ...)
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 44/50] plugin: add qemu_plugin_insn_disas helper Alex Bennée
@ 2019-06-14 17:11 ` Alex Bennée
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 46/50] tests/plugin: add hotpages plugin to breakdown memory access patterns Alex Bennée
                   ` (8 subsequent siblings)
  53 siblings, 0 replies; 117+ messages in thread
From: Alex Bennée @ 2019-06-14 17:11 UTC (permalink / raw)
  To: qemu-devel; +Cc: Alex Bennée

This gives a break down of instruction classes and individual
instruction types.

Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
---
 tests/plugin/Makefile |   1 +
 tests/plugin/howvec.c | 297 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 298 insertions(+)
 create mode 100644 tests/plugin/howvec.c

diff --git a/tests/plugin/Makefile b/tests/plugin/Makefile
index e74940eaac..3656429d46 100644
--- a/tests/plugin/Makefile
+++ b/tests/plugin/Makefile
@@ -11,6 +11,7 @@ NAMES += empty
 NAMES += insn
 NAMES += mem
 NAMES += hotblocks
+NAMES += howvec
 
 SONAMES := $(addsuffix .so,$(addprefix lib,$(NAMES)))
 
diff --git a/tests/plugin/howvec.c b/tests/plugin/howvec.c
new file mode 100644
index 0000000000..0796523af3
--- /dev/null
+++ b/tests/plugin/howvec.c
@@ -0,0 +1,297 @@
+/*
+ * Copyright (C) 2019, Alex Bennée <alex.bennee@linaro.org>
+ *
+ * How vectorised is this code?
+ *
+ * Attempt to measure the amount of vectorisation that has been done
+ * on some code by counting classes of instruction. This is very much
+ * ARM specific.
+ *
+ * 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 <inttypes.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <glib.h>
+
+#include <qemu-plugin.h>
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+
+typedef enum {
+    COUNT_CLASS,
+    COUNT_INDIVIDUAL,
+    COUNT_NONE
+} CountType;
+
+static int limit = 50;
+static int stdout_fd;
+static bool do_inline;
+static bool verbose;
+
+static GMutex lock;
+static GHashTable *insns;
+
+typedef struct {
+    const char *class;
+    const char *opt;
+    uint32_t mask;
+    uint32_t pattern;
+    CountType what;
+    uint64_t count;
+} InsnClassExecCount;
+
+typedef struct {
+    char *insn;
+    uint32_t opcode;
+    uint64_t count;
+    InsnClassExecCount *class;
+} InsnExecCount;
+
+/*
+ * Matchers for classes of instructions, order is important.
+ *
+ * Your most precise match must be before looser matches. If no match
+ * is found in the table we can create an individual entry.
+ */
+InsnClassExecCount insn_classes[] = {
+    /* "Reserved"" */
+    { "  UDEF",              "udef",   0xffff0000, 0x00000000, COUNT_NONE},
+    { "  SVE",               "sve",    0x1e000000, 0x04000000, COUNT_CLASS},
+    { "Reserved",            "res",    0x1e000000, 0x00000000, COUNT_CLASS},
+    /* Data Processing Immediate */
+    { "  PCrel addr",        "pcrel",  0x1f000000, 0x10000000, COUNT_CLASS},
+    { "  Add/Sub (imm,tags)","asit",   0x1f800000, 0x11800000, COUNT_CLASS},
+    { "  Add/Sub (imm)",     "asi",    0x1f000000, 0x11000000, COUNT_CLASS},
+    { "  Logical (imm)",     "logi",   0x1f800000, 0x12000000, COUNT_CLASS},
+    { "  Move Wide (imm)",   "movwi",  0x1f800000, 0x12800000, COUNT_CLASS},
+    { "  Bitfield",          "bitf",   0x1f800000, 0x13000000, COUNT_CLASS},
+    { "  Extract",           "extr",   0x1f800000, 0x13800000, COUNT_CLASS},
+    { "Data Proc Imm",       "dpri",   0x1c000000, 0x10000000, COUNT_CLASS},
+    /* Branches */
+    { "  Cond Branch (imm)", "cndb",   0xfe000000, 0x54000000, COUNT_CLASS},
+    { "  Exception Gen",     "excp",   0xff000000, 0xd4000000, COUNT_CLASS},
+    { "    NOP",             "nop",    0xffffffff, 0xd503201f, COUNT_NONE},
+    { "  Hints",             "hint",   0xfffff000, 0xd5032000, COUNT_CLASS},
+    { "  Barriers",          "barr",   0xfffff000, 0xd5033000, COUNT_CLASS},
+    { "  PSTATE",            "psta",   0xfff8f000, 0xd5004000, COUNT_CLASS},
+    { "  System Insn",       "sins",   0xffd80000, 0xd5080000, COUNT_CLASS},
+    { "  System Reg",        "sreg",   0xffd00000, 0xd5100000, COUNT_CLASS},
+    { "  Branch (reg)",      "breg",   0xfe000000, 0xd6000000, COUNT_CLASS},
+    { "  Branch (imm)",      "bimm",   0x7c000000, 0x14000000, COUNT_CLASS},
+    { "  Cmp & Branch",      "cmpb",   0x7e000000, 0x34000000, COUNT_CLASS},
+    { "  Tst & Branch",      "tstb",   0x7e000000, 0x36000000, COUNT_CLASS},
+    { "Branches",            "branch", 0x1c000000, 0x14000000, COUNT_CLASS},
+    /* Loads and Stores */
+    { "  AdvSimd ldstmult",  "advlsm", 0xbfbf0000, 0x0c000000, COUNT_CLASS},
+    { "  AdvSimd ldstmult++","advlsmp",0xbfb00000, 0x0c800000, COUNT_CLASS},
+    { "  AdvSimd ldst",      "advlss", 0xbf9f0000, 0x0d000000, COUNT_CLASS},
+    { "  AdvSimd ldst++",    "advlssp",0xbf800000, 0x0d800000, COUNT_CLASS},
+    { "  ldst excl",         "ldstx",  0x3f000000, 0x08000000, COUNT_CLASS},
+    { "    Prefetch",        "prfm",   0xff000000, 0xd8000000, COUNT_CLASS},
+    { "  Load Reg (lit)",    "ldlit",  0x1b000000, 0x18000000, COUNT_CLASS},
+    { "  ldst noalloc pair", "ldstnap",0x3b800000, 0x28000000, COUNT_CLASS},
+    { "  ldst pair",         "ldstp",  0x38000000, 0x28000000, COUNT_CLASS},
+    { "  ldst reg",          "ldstr",  0x3b200000, 0x38000000, COUNT_CLASS},
+    { "  Atomic ldst",       "atomic", 0x3b200c00, 0x38200000, COUNT_CLASS},
+    { "  ldst reg (reg off)","ldstro", 0x3b200b00, 0x38200800, COUNT_CLASS},
+    { "  ldst reg (pac)",    "ldstpa", 0x3b200200, 0x38200800, COUNT_CLASS},
+    { "  ldst reg (imm)",    "ldsti",  0x3b000000, 0x39000000, COUNT_CLASS},
+    { "Loads & Stores",      "ldst",   0x0a000000, 0x08000000, COUNT_CLASS},
+    /* Data Processing Register */
+    { "Data Proc Reg",       "dprr",   0x0e000000, 0x0a000000, COUNT_CLASS},
+    /* Scalar FP */
+    { "Scalar FP ",          "fpsimd", 0x0e000000, 0x0e000000, COUNT_CLASS},
+    /* Unclassified */
+    { "Unclassified",        "unclas", 0x00000000, 0x00000000, COUNT_CLASS}
+};
+
+static gint cmp_exec_count(gconstpointer a, gconstpointer b)
+{
+    InsnExecCount *ea = (InsnExecCount *) a;
+    InsnExecCount *eb = (InsnExecCount *) b;
+    return ea->count > eb->count ? -1 : 1;
+}
+
+static void plugin_exit(qemu_plugin_id_t id, void *p)
+{
+    GString *report = g_string_new("Instruction Classes:\n");
+    int i;
+    GList *counts;
+
+    for (i = 0; i < ARRAY_SIZE(insn_classes); i++) {
+        switch (insn_classes[i].what) {
+        case COUNT_CLASS:
+            if (insn_classes[i].count || verbose) {
+                g_string_append_printf(report,"Class: %-24s\t(%ld hits)\n",
+                                       insn_classes[i].class, insn_classes[i].count);
+            }
+            break;
+        case COUNT_INDIVIDUAL:
+            g_string_append_printf(report,"Class: %-24s\tcounted individually\n",
+                                   insn_classes[i].class);
+            break;
+        case COUNT_NONE:
+            g_string_append_printf(report,"Class: %-24s\tnot counted\n",
+                                   insn_classes[i].class);
+            break;
+        default:
+            break;
+        }
+    }
+
+    counts = g_hash_table_get_values(insns);
+    if (counts && g_list_next(counts)) {
+        GList *it;
+
+        g_string_append_printf(report,"Individual Instructions:\n");
+
+        it = g_list_sort(counts, cmp_exec_count);
+
+        for (i = 0; i < limit && it->next; i++, it = it->next) {
+            InsnExecCount *rec = (InsnExecCount *) it->data;
+            g_string_append_printf(report, "Instr: %-24s\t(%ld hits)\t(op=%#08x/%s)\n",
+                                   rec->insn,
+                                   rec->count,
+                                   rec->opcode,
+                                   rec->class ? rec->class->class : "un-categorised");
+        }
+        g_list_free(it);
+    }
+
+    dprintf(stdout_fd, "%s", report->str);
+    g_string_free(report, true);
+}
+
+static void plugin_init(void)
+{
+    insns = g_hash_table_new(NULL, g_direct_equal);
+}
+
+static void vcpu_insn_exec_before(unsigned int cpu_index, void *udata)
+{
+    uint64_t *count = (uint64_t *) udata;
+    (*count)++;
+}
+
+static uint64_t * find_counter(struct qemu_plugin_insn *insn)
+{
+    int i;
+    uint64_t *cnt = NULL;
+    uint32_t opcode;
+    InsnClassExecCount *class = NULL;
+
+    /* we expect all instructions to by 32 bits for ARM */
+    g_assert(qemu_plugin_insn_size(insn) == 4);
+    opcode = *((uint32_t *)qemu_plugin_insn_data(insn));
+
+    for (i = 0; !cnt && i < ARRAY_SIZE(insn_classes); i++) {
+        uint32_t masked_bits = opcode & insn_classes[i].mask;
+        if (masked_bits == insn_classes[i].pattern) {
+            class = &insn_classes[i];
+            break;
+        }
+    }
+
+    g_assert(class);
+
+    switch (class->what) {
+    case COUNT_NONE:
+        return NULL;
+    case COUNT_CLASS:
+        return &class->count;
+    case COUNT_INDIVIDUAL:
+    {
+        InsnExecCount *icount;
+
+        g_mutex_lock(&lock);
+        icount = (InsnExecCount *) g_hash_table_lookup(insns, GUINT_TO_POINTER(opcode));
+
+        if (!icount) {
+            icount = g_new0(InsnExecCount, 1);
+            icount->opcode = opcode;
+            icount->insn = qemu_plugin_insn_disas(insn);
+            icount->class = class;
+
+            if (verbose) {
+                dprintf(stdout_fd, "adding instrumentation for %s (%#08x @ %#20lx from %s)\n",
+                        icount->insn, opcode, qemu_plugin_insn_vaddr(insn), class->class);
+            }
+            g_hash_table_insert(insns, GUINT_TO_POINTER(opcode), (gpointer) icount);
+        }
+        g_mutex_unlock(&lock);
+
+        return &icount->count;
+    }
+    default:
+        g_assert_not_reached();
+    }
+
+    return NULL;
+}
+
+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++) {
+        uint64_t *cnt;
+        struct qemu_plugin_insn *insn = qemu_plugin_tb_get_insn(tb, i);
+        cnt = find_counter(insn);
+
+        if (cnt) {
+            if (do_inline) {
+                qemu_plugin_register_vcpu_insn_exec_inline(
+                    insn, QEMU_PLUGIN_INLINE_ADD_U64, cnt, 1);
+            } else {
+                qemu_plugin_register_vcpu_insn_exec_cb(
+                    insn, vcpu_insn_exec_before, QEMU_PLUGIN_CB_NO_REGS, cnt);
+            }
+        }
+    }
+}
+
+QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, int argc,
+                                           char **argv)
+{
+    int i;
+
+    for (i = 0; i < argc; i++) {
+        char *p = argv[i];
+        if (strcmp(p, "inline") == 0) {
+            do_inline = true;
+        } else if (strcmp(p, "verbose") == 0) {
+            verbose = true;
+        } else {
+            int j;
+            CountType type = COUNT_INDIVIDUAL;
+            if (*p == '!') {
+                type = COUNT_NONE;
+                p++;
+            }
+            for (j = 0; j < ARRAY_SIZE(insn_classes); j++) {
+                if (strcmp(p, insn_classes[j].opt) == 0) {
+                    insn_classes[j].what = type;
+                    break;
+                }
+            }
+        }
+    }
+
+    /* to be used when in the exit hook */
+    stdout_fd = dup(STDOUT_FILENO);
+    assert(stdout_fd);
+
+    plugin_init();
+
+    qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans);
+    qemu_plugin_register_atexit_cb(id, plugin_exit, NULL);
+    return 0;
+}
-- 
2.20.1



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

* [Qemu-devel] [PATCH v3 46/50] tests/plugin: add hotpages plugin to breakdown memory access patterns
  2019-06-14 17:11 [Qemu-devel] [PATCH v3 00/50] tcg plugin support Alex Bennée
                   ` (44 preceding siblings ...)
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 45/50] tests/plugin: add instruction execution breakdown Alex Bennée
@ 2019-06-14 17:11 ` Alex Bennée
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 47/50] accel/stubs: reduce headers from tcg-stub Alex Bennée
                   ` (7 subsequent siblings)
  53 siblings, 0 replies; 117+ messages in thread
From: Alex Bennée @ 2019-06-14 17:11 UTC (permalink / raw)
  To: qemu-devel; +Cc: Alex Bennée

This plugin gives a break down of access patterns grouped into pages.

Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
---
 tests/plugin/Makefile   |   1 +
 tests/plugin/hotpages.c | 152 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 153 insertions(+)
 create mode 100644 tests/plugin/hotpages.c

diff --git a/tests/plugin/Makefile b/tests/plugin/Makefile
index 3656429d46..75467b6db8 100644
--- a/tests/plugin/Makefile
+++ b/tests/plugin/Makefile
@@ -12,6 +12,7 @@ NAMES += insn
 NAMES += mem
 NAMES += hotblocks
 NAMES += howvec
+NAMES += hotpages
 
 SONAMES := $(addsuffix .so,$(addprefix lib,$(NAMES)))
 
diff --git a/tests/plugin/hotpages.c b/tests/plugin/hotpages.c
new file mode 100644
index 0000000000..ddeaad077b
--- /dev/null
+++ b/tests/plugin/hotpages.c
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2019, Alex Bennée <alex.bennee@linaro.org>
+ *
+ * Hot Pages - show which pages saw the most memory accesses.
+ *
+ * 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 <inttypes.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <glib.h>
+
+#include <qemu-plugin.h>
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+
+static bool do_inline;
+static uint64_t page_size;
+static uint64_t page_mask;
+static int stdout_fd;
+static int limit = 50;
+static enum qemu_plugin_mem_rw rw = QEMU_PLUGIN_MEM_RW;
+
+typedef struct {
+    uint64_t page_address;
+    int cpu_read;
+    int cpu_write;
+    uint64_t reads;
+    uint64_t writes;
+} PageCounters;
+
+static GMutex lock;
+static GHashTable *pages;
+
+static gint cmp_access_count(gconstpointer a, gconstpointer b)
+{
+    PageCounters *ea = (PageCounters *) a;
+    PageCounters *eb = (PageCounters *) b;
+    return (ea->reads + ea->writes) > (eb->reads + eb->writes) ? -1 : 1;
+}
+
+
+static void plugin_exit(qemu_plugin_id_t id, void *p)
+{
+    GString *report = g_string_new("Pages:\n");
+    int i;
+    GList *counts;
+
+    counts = g_hash_table_get_values(pages);
+    if (counts && g_list_next(counts)) {
+        GList *it;
+
+        g_string_append_printf(report,"Individual Instructions:\n");
+
+        it = g_list_sort(counts, cmp_access_count);
+
+        for (i = 0; i < limit && it->next; i++, it = it->next) {
+            PageCounters *rec = (PageCounters *) it->data;
+            g_string_append_printf(report, "Page: %"PRIx64" cpus:%#04x/%#04x %"PRId64"/%"PRId64"\n",
+                                   rec->page_address,
+                                   rec->cpu_read, rec->cpu_write,
+                                   rec->reads, rec->writes);
+        }
+        g_list_free(it);
+    }
+
+    dprintf(stdout_fd, "%s", report->str);
+    g_string_free(report, true);
+}
+
+static void plugin_init(void)
+{
+    page_mask = (page_size - 1);
+    pages = g_hash_table_new(NULL, g_direct_equal);
+}
+
+static void vcpu_haddr(unsigned int cpu_index, qemu_plugin_meminfo_t meminfo,
+                       uint64_t vaddr, void *haddr, void *udata)
+{
+    uint64_t page = (uint64_t) haddr & page_mask;
+    PageCounters *count;
+
+    g_mutex_lock(&lock);
+    count = (PageCounters *) g_hash_table_lookup(pages, GUINT_TO_POINTER(page));
+
+    if (!count) {
+        count = g_new0(PageCounters, 1);
+        count->page_address = page;
+        g_hash_table_insert(pages, GUINT_TO_POINTER(page), (gpointer) count);
+    }
+    if (qemu_plugin_mem_is_store(meminfo)) {
+        count->writes++;
+        count->cpu_write |= (1 << cpu_index);
+    } else {
+        count->reads++;
+        count->cpu_read |= (1 << cpu_index);
+    }
+
+    g_mutex_unlock(&lock);
+}
+
+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);
+        qemu_plugin_register_vcpu_mem_haddr_cb(insn, vcpu_haddr,
+                                               QEMU_PLUGIN_CB_NO_REGS,
+                                               rw, NULL);
+    }
+}
+
+static GOptionEntry entries[] =
+{
+    { "pagesize", 'r', 0, G_OPTION_ARG_INT, &page_size, "Page Size", "N" },
+    { "inline", 'i', 0, G_OPTION_ARG_NONE, &do_inline, "Inline counters", NULL },
+    { NULL }
+};
+
+QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, int argc,
+                                           char **argv)
+{
+    GOptionContext *context;
+    GError *error;
+
+    context = g_option_context_new ("- hot pages tool");
+    g_option_context_add_main_entries (context, entries, NULL);
+    if (!g_option_context_parse (context, &argc, &argv, &error))
+    {
+        dprintf(stdout_fd, "option parsing failed: %s\n", error->message);
+        return -1;
+    }
+
+    /* to be used when in the exit hook */
+    stdout_fd = dup(STDOUT_FILENO);
+    assert(stdout_fd);
+
+    plugin_init();
+
+    qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans);
+    qemu_plugin_register_atexit_cb(id, plugin_exit, NULL);
+    return 0;
+}
-- 
2.20.1



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

* [Qemu-devel] [PATCH v3 47/50] accel/stubs: reduce headers from tcg-stub
  2019-06-14 17:11 [Qemu-devel] [PATCH v3 00/50] tcg plugin support Alex Bennée
                   ` (45 preceding siblings ...)
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 46/50] tests/plugin: add hotpages plugin to breakdown memory access patterns Alex Bennée
@ 2019-06-14 17:11 ` Alex Bennée
  2019-06-17 23:22   ` Richard Henderson
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 48/50] include/exec: wrap cpu_ldst.h in CONFIG_TCG Alex Bennée
                   ` (6 subsequent siblings)
  53 siblings, 1 reply; 117+ messages in thread
From: Alex Bennée @ 2019-06-14 17:11 UTC (permalink / raw)
  To: qemu-devel; +Cc: Paolo Bonzini, Alex Bennée, Richard Henderson

We don't need much for these. However I do wonder why these aren't
just null inlines in exec-all.h

Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
---
 accel/stubs/tcg-stub.c | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/accel/stubs/tcg-stub.c b/accel/stubs/tcg-stub.c
index 76ae461749..431ec7d7e6 100644
--- a/accel/stubs/tcg-stub.c
+++ b/accel/stubs/tcg-stub.c
@@ -11,10 +11,7 @@
  */
 
 #include "qemu/osdep.h"
-#include "qemu-common.h"
 #include "cpu.h"
-#include "tcg/tcg.h"
-#include "exec/cpu-common.h"
 #include "exec/exec-all.h"
 
 void tb_flush(CPUState *cpu)
-- 
2.20.1



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

* [Qemu-devel] [PATCH v3 48/50] include/exec: wrap cpu_ldst.h in CONFIG_TCG
  2019-06-14 17:11 [Qemu-devel] [PATCH v3 00/50] tcg plugin support Alex Bennée
                   ` (46 preceding siblings ...)
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 47/50] accel/stubs: reduce headers from tcg-stub Alex Bennée
@ 2019-06-14 17:11 ` Alex Bennée
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 49/50] include/exec/cpu-defs.h: fix typo Alex Bennée
                   ` (5 subsequent siblings)
  53 siblings, 0 replies; 117+ messages in thread
From: Alex Bennée @ 2019-06-14 17:11 UTC (permalink / raw)
  To: qemu-devel; +Cc: Paolo Bonzini, Alex Bennée, Richard Henderson

This gets around a build problem with --disable-tcg.

Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
---
 include/exec/exec-all.h | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h
index 90045e77c1..fa4d5b9533 100644
--- a/include/exec/exec-all.h
+++ b/include/exec/exec-all.h
@@ -21,7 +21,9 @@
 #define EXEC_ALL_H
 
 #include "exec/tb-context.h"
+#ifdef CONFIG_TCG
 #include "exec/cpu_ldst.h"
+#endif
 #include "sysemu/cpus.h"
 
 /* allow to see translation results - the slowdown should be negligible, so we leave it */
-- 
2.20.1



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

* [Qemu-devel] [PATCH  v3 49/50] include/exec/cpu-defs.h: fix typo
  2019-06-14 17:11 [Qemu-devel] [PATCH v3 00/50] tcg plugin support Alex Bennée
                   ` (47 preceding siblings ...)
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 48/50] include/exec: wrap cpu_ldst.h in CONFIG_TCG Alex Bennée
@ 2019-06-14 17:11 ` Alex Bennée
  2019-06-17 23:23   ` Richard Henderson
  2019-06-14 17:12 ` [Qemu-devel] [PATCH v3 50/50] TODO: API changes to make? Alex Bennée
                   ` (4 subsequent siblings)
  53 siblings, 1 reply; 117+ messages in thread
From: Alex Bennée @ 2019-06-14 17:11 UTC (permalink / raw)
  To: qemu-devel; +Cc: Paolo Bonzini, Alex Bennée, Richard Henderson

Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
---
 include/exec/cpu-defs.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/include/exec/cpu-defs.h b/include/exec/cpu-defs.h
index 354788385b..b529c2013e 100644
--- a/include/exec/cpu-defs.h
+++ b/include/exec/cpu-defs.h
@@ -241,7 +241,7 @@ typedef struct CPUTLB { } CPUTLB;
 #endif  /* !CONFIG_USER_ONLY && CONFIG_TCG */
 
 /*
- * This structure must be placed in ArchCPU immedately
+ * This structure must be placed in ArchCPU immediately
  * before CPUArchState, as a field named "neg".
  */
 typedef struct CPUNegativeOffsetState {
-- 
2.20.1



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

* [Qemu-devel] [PATCH  v3 50/50] TODO: API changes to make?
  2019-06-14 17:11 [Qemu-devel] [PATCH v3 00/50] tcg plugin support Alex Bennée
                   ` (48 preceding siblings ...)
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 49/50] include/exec/cpu-defs.h: fix typo Alex Bennée
@ 2019-06-14 17:12 ` Alex Bennée
  2019-06-14 17:41 ` [Qemu-devel] [PATCH v3 00/50] tcg plugin support Aleksandar Markovic
                   ` (3 subsequent siblings)
  53 siblings, 0 replies; 117+ messages in thread
From: Alex Bennée @ 2019-06-14 17:12 UTC (permalink / raw)
  To: qemu-devel; +Cc: Alex Bennée

---
 include/qemu/qemu-plugin.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/include/qemu/qemu-plugin.h b/include/qemu/qemu-plugin.h
index 21f056d4ab..655640fe54 100644
--- a/include/qemu/qemu-plugin.h
+++ b/include/qemu/qemu-plugin.h
@@ -169,6 +169,7 @@ enum qemu_plugin_mem_rw {
  * callbacks to be triggered when the block or individual instruction
  * executes.
  */
+/* ??? - should we expose vcpu_index here? */
 typedef void (*qemu_plugin_vcpu_tb_trans_cb_t)(qemu_plugin_id_t id,
                                                unsigned int vcpu_index,
                                                struct qemu_plugin_tb *tb);
-- 
2.20.1



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

* Re: [Qemu-devel] [PATCH v3 00/50] tcg plugin support
  2019-06-14 17:11 [Qemu-devel] [PATCH v3 00/50] tcg plugin support Alex Bennée
                   ` (49 preceding siblings ...)
  2019-06-14 17:12 ` [Qemu-devel] [PATCH v3 50/50] TODO: API changes to make? Alex Bennée
@ 2019-06-14 17:41 ` Aleksandar Markovic
  2019-06-14 18:39   ` Alex Bennée
  2019-06-14 19:47 ` no-reply
                   ` (2 subsequent siblings)
  53 siblings, 1 reply; 117+ messages in thread
From: Aleksandar Markovic @ 2019-06-14 17:41 UTC (permalink / raw)
  To: Alex Bennée; +Cc: QEMU Developers

On Fri, Jun 14, 2019 at 7:22 PM Alex Bennée <alex.bennee@linaro.org> wrote:
>
> Hi,
>
> This is v3 of the proposed plugins API for QEMU. As Emilio is busy
> having finished his time at Columbia I have volunteered to take the
> patch series forward. Emilio's RFC v2 was posted last year:
>
>   Subject: [RFC v2 00/38] Plugin support
>   Date: Sun,  9 Dec 2018 14:37:11 -0500
>   Message-Id: <20181209193749.12277-1-cota@braap.org>
>
> The idea is to present a plugin interface that allows for interesting
> experiments to be written while not exposing the internal details of
> how the TCG works to the wider world.
>
> Changes from last year
>
>   - re-basing and fixing up conflicts
>   - moved to plugins/ and split files up (core/api/loader)
>   - added a design document (docs/devel/plugins.rst)
>   - replaced auto-growing structures with glib GArray and friends
>   - expanded API to include access to disassembly
>   - wrote some more example plugins
>   - dropped the RFC tag ;-)
>
> There are a few things left to sort out. Currently the haddr
> calculation relies on the softmmu TLB code for each architecture to
> dump it in a cpu_env location. Currently this is only done on x86 and
> we would require some tweaking to each backend. I think it would
> probably be better to just have a wrapper helper which could do the
> lookup of the (hot) TLB in C to calculate the address before calling
> the helpers.
>

Great to see this series!

Is there any particular conceptual reason why not all targets are included
(i.e. mips, s390, etc.)? Or it was just, let's say, lack of your time resources
to complete everything?

Yours,
Aleksandar

> I also ended up adding a bunch of miscellaneous fixes at the end while
> I was re-spinning. If they get some review I'll try and get the
> non-controversial stuff up-streamed first.
>
> The area that needs the most review is the single pass TCG op
> insertion code. When plugins are enabled each instruction gets dummy
> ops inserted and then at the end we swap those out for the actual
> calls or remove them from the stream. Hopefully this is not too much
> of an abuse of TCG mechanisms.
>
> I'm aiming to get this in for 4.1 but I guess that will depend on how
> the review and iteration goes.
>
> Alex Bennée (12):
>   docs/devel: add plugins.rst design document
>   configure: add --enable-plugins (MOVE TO END)
>   plugin: add implementation of the api
>   tests/tcg: enable plugin testing
>   tests/plugin: add a hotblocks plugin
>   plugin: add qemu_plugin_insn_disas helper
>   tests/plugin: add instruction execution breakdown
>   tests/plugin: add hotpages plugin to breakdown memory access patterns
>   accel/stubs: reduce headers from tcg-stub
>   include/exec: wrap cpu_ldst.h in CONFIG_TCG
>   include/exec/cpu-defs.h: fix typo
>   TODO: API changes to make?
>
> Emilio G. Cota (36):
>   trace: expand mem_info:size_shift to 3 bits
>   tcg/README: fix typo s/afterwise/afterwards/
>   cpu: introduce cpu_in_exclusive_work_context()
>   translate-all: use cpu_in_exclusive_work_context() in tb_flush
>   plugin: add user-facing API
>   plugin: add core code
>   queue: add QTAILQ_REMOVE_SEVERAL
>   cputlb: document get_page_addr_code
>   cputlb: introduce get_page_addr_code_hostp
>   plugin-gen: add module for TCG-related code
>   tcg: add tcg_gen_st_ptr
>   tcg: add MO_HADDR to TCGMemOp
>   atomic_template: fix indentation in GEN_ATOMIC_HELPER
>   atomic_template: add inline trace/plugin helpers
>   cpu_ldst_useronly_template: remove redundant #ifndef CODE_ACCESS
>   tcg: let plugins instrument memory accesses
>   translate-all: notify plugin code of tb_flush
>   *-user: notify plugin of exit
>   *-user: plugin syscalls
>   cpu: hook plugin vcpu events
>   plugin-gen: add plugin_insn_append
>   translator: add translator_ld{ub,sw,uw,l,q}
>   target/arm: call qemu_plugin_insn_append
>   target/ppc: fetch code with translator_ld
>   target/sh4: fetch code with translator_ld
>   target/i386: fetch code with translator_ld
>   target/hppa: fetch code with translator_ld
>   target/m68k: fetch code with translator_ld
>   target/alpha: fetch code with translator_ld
>   target/riscv: fetch code with translator_ld
>   target/sparc: fetch code with translator_ld
>   target/xtensa: fetch code with translator_ld
>   target/openrisc: fetch code with translator_ld
>   translator: inject instrumentation from plugins
>   plugin: add API symbols to qemu-plugins.symbols
>   tests/plugin: add sample plugins
>
> Lluís Vilanova (2):
>   vl: support -plugin option
>   linux-user: support -plugin option
>
>  Makefile                                  |   11 +-
>  Makefile.target                           |    2 +
>  accel/stubs/tcg-stub.c                    |    3 -
>  accel/tcg/Makefile.objs                   |    1 +
>  accel/tcg/atomic_template.h               |  117 ++-
>  accel/tcg/cpu-exec.c                      |    3 +
>  accel/tcg/cputlb.c                        |   56 +-
>  accel/tcg/plugin-gen.c                    | 1079 +++++++++++++++++++++
>  accel/tcg/plugin-helpers.h                |    6 +
>  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 +
>  disas.c                                   |  103 ++
>  docs/devel/index.rst                      |    1 +
>  docs/devel/plugins.rst                    |   99 ++
>  exec.c                                    |    2 +
>  include/disas/disas.h                     |    2 +
>  include/exec/cpu-defs.h                   |   11 +-
>  include/exec/cpu_ldst.h                   |    9 +
>  include/exec/cpu_ldst_template.h          |   40 +-
>  include/exec/cpu_ldst_useronly_template.h |   36 +-
>  include/exec/exec-all.h                   |   64 +-
>  include/exec/helper-gen.h                 |    1 +
>  include/exec/helper-proto.h               |    1 +
>  include/exec/helper-tcg.h                 |    1 +
>  include/exec/plugin-gen.h                 |   71 ++
>  include/exec/translator.h                 |   30 +-
>  include/qemu/plugin.h                     |  261 +++++
>  include/qemu/qemu-plugin.h                |  349 +++++++
>  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 +
>  plugins/.gitignore                        |    2 +
>  plugins/Makefile.objs                     |   21 +
>  plugins/api.c                             |  274 ++++++
>  plugins/core.c                            |  524 ++++++++++
>  plugins/loader.c                          |  353 +++++++
>  plugins/plugin.h                          |   95 ++
>  plugins/qemu-plugins.symbols              |   36 +
>  qemu-options.hx                           |   17 +
>  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                    |    4 +-
>  target/sparc/translate.c                  |    2 +-
>  target/xtensa/translate.c                 |    4 +-
>  tcg/README                                |    2 +-
>  tcg/i386/tcg-target.inc.c                 |    8 +
>  tcg/tcg-op.c                              |   40 +-
>  tcg/tcg-op.h                              |   16 +
>  tcg/tcg-opc.h                             |    3 +
>  tcg/tcg.c                                 |   22 +
>  tcg/tcg.h                                 |   28 +
>  tests/Makefile.include                    |   10 +-
>  tests/plugin/Makefile                     |   31 +
>  tests/plugin/bb.c                         |   66 ++
>  tests/plugin/empty.c                      |   30 +
>  tests/plugin/hotblocks.c                  |  145 +++
>  tests/plugin/hotpages.c                   |  152 +++
>  tests/plugin/howvec.c                     |  297 ++++++
>  tests/plugin/insn.c                       |   63 ++
>  tests/plugin/mem.c                        |   93 ++
>  tests/tcg/Makefile                        |   34 +
>  tests/tcg/arm/Makefile.softmmu-target     |    1 +
>  trace-events                              |    2 +-
>  vl.c                                      |   11 +
>  78 files changed, 4855 insertions(+), 122 deletions(-)
>  create mode 100644 accel/tcg/plugin-gen.c
>  create mode 100644 accel/tcg/plugin-helpers.h
>  create mode 100644 docs/devel/plugins.rst
>  create mode 100644 include/exec/plugin-gen.h
>  create mode 100644 include/qemu/plugin.h
>  create mode 100644 include/qemu/qemu-plugin.h
>  create mode 100644 plugins/.gitignore
>  create mode 100644 plugins/Makefile.objs
>  create mode 100644 plugins/api.c
>  create mode 100644 plugins/core.c
>  create mode 100644 plugins/loader.c
>  create mode 100644 plugins/plugin.h
>  create mode 100644 plugins/qemu-plugins.symbols
>  create mode 100644 tests/plugin/Makefile
>  create mode 100644 tests/plugin/bb.c
>  create mode 100644 tests/plugin/empty.c
>  create mode 100644 tests/plugin/hotblocks.c
>  create mode 100644 tests/plugin/hotpages.c
>  create mode 100644 tests/plugin/howvec.c
>  create mode 100644 tests/plugin/insn.c
>  create mode 100644 tests/plugin/mem.c
>
> --
> 2.20.1
>
>


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

* Re: [Qemu-devel] [PATCH v3 00/50] tcg plugin support
  2019-06-14 17:41 ` [Qemu-devel] [PATCH v3 00/50] tcg plugin support Aleksandar Markovic
@ 2019-06-14 18:39   ` Alex Bennée
  0 siblings, 0 replies; 117+ messages in thread
From: Alex Bennée @ 2019-06-14 18:39 UTC (permalink / raw)
  To: Aleksandar Markovic; +Cc: QEMU Developers


Aleksandar Markovic <aleksandar.m.mail@gmail.com> writes:

> On Fri, Jun 14, 2019 at 7:22 PM Alex Bennée <alex.bennee@linaro.org> wrote:
>>
>> Hi,
>>
>> This is v3 of the proposed plugins API for QEMU. As Emilio is busy
>> having finished his time at Columbia I have volunteered to take the
>> patch series forward. Emilio's RFC v2 was posted last year:
>>
>>   Subject: [RFC v2 00/38] Plugin support
>>   Date: Sun,  9 Dec 2018 14:37:11 -0500
>>   Message-Id: <20181209193749.12277-1-cota@braap.org>
>>
>> The idea is to present a plugin interface that allows for interesting
>> experiments to be written while not exposing the internal details of
>> how the TCG works to the wider world.
>>
>> Changes from last year
>>
>>   - re-basing and fixing up conflicts
>>   - moved to plugins/ and split files up (core/api/loader)
>>   - added a design document (docs/devel/plugins.rst)
>>   - replaced auto-growing structures with glib GArray and friends
>>   - expanded API to include access to disassembly
>>   - wrote some more example plugins
>>   - dropped the RFC tag ;-)
>>
>> There are a few things left to sort out. Currently the haddr
>> calculation relies on the softmmu TLB code for each architecture to
>> dump it in a cpu_env location. Currently this is only done on x86 and
>> we would require some tweaking to each backend. I think it would
>> probably be better to just have a wrapper helper which could do the
>> lookup of the (hot) TLB in C to calculate the address before calling
>> the helpers.
>>
>
> Great to see this series!
>
> Is there any particular conceptual reason why not all targets are included
> (i.e. mips, s390, etc.)? Or it was just, let's say, lack of your time resources
> to complete everything?

There are two things.

Firstly this only supports guests that have been converted to the common
translator_loop code. This is to avoid having hooks in all the various
guest front-ends. I think we are slowly moving to having the common
translator_loop as mandatory and once that's done the guest gets plugins
"for free".

The only other change needed for each guest is to use the common
translator_ld function to read instructions. This again avoids us having
to have too many tendrils into the guests. arm's handling of endian
switching being the exception in this case.

AFAICT both mips and s390 are using the common loop stuff so it should
just be a case of adding the relevant translator_ld calls. The only
minor wrinkle would be if they do anything fancy like SH4's gUSA
sequence.

>
> Yours,
> Aleksandar
>
>> I also ended up adding a bunch of miscellaneous fixes at the end while
>> I was re-spinning. If they get some review I'll try and get the
>> non-controversial stuff up-streamed first.
>>
>> The area that needs the most review is the single pass TCG op
>> insertion code. When plugins are enabled each instruction gets dummy
>> ops inserted and then at the end we swap those out for the actual
>> calls or remove them from the stream. Hopefully this is not too much
>> of an abuse of TCG mechanisms.
>>
>> I'm aiming to get this in for 4.1 but I guess that will depend on how
>> the review and iteration goes.
>>
>> Alex Bennée (12):
>>   docs/devel: add plugins.rst design document
>>   configure: add --enable-plugins (MOVE TO END)
>>   plugin: add implementation of the api
>>   tests/tcg: enable plugin testing
>>   tests/plugin: add a hotblocks plugin
>>   plugin: add qemu_plugin_insn_disas helper
>>   tests/plugin: add instruction execution breakdown
>>   tests/plugin: add hotpages plugin to breakdown memory access patterns
>>   accel/stubs: reduce headers from tcg-stub
>>   include/exec: wrap cpu_ldst.h in CONFIG_TCG
>>   include/exec/cpu-defs.h: fix typo
>>   TODO: API changes to make?
>>
>> Emilio G. Cota (36):
>>   trace: expand mem_info:size_shift to 3 bits
>>   tcg/README: fix typo s/afterwise/afterwards/
>>   cpu: introduce cpu_in_exclusive_work_context()
>>   translate-all: use cpu_in_exclusive_work_context() in tb_flush
>>   plugin: add user-facing API
>>   plugin: add core code
>>   queue: add QTAILQ_REMOVE_SEVERAL
>>   cputlb: document get_page_addr_code
>>   cputlb: introduce get_page_addr_code_hostp
>>   plugin-gen: add module for TCG-related code
>>   tcg: add tcg_gen_st_ptr
>>   tcg: add MO_HADDR to TCGMemOp
>>   atomic_template: fix indentation in GEN_ATOMIC_HELPER
>>   atomic_template: add inline trace/plugin helpers
>>   cpu_ldst_useronly_template: remove redundant #ifndef CODE_ACCESS
>>   tcg: let plugins instrument memory accesses
>>   translate-all: notify plugin code of tb_flush
>>   *-user: notify plugin of exit
>>   *-user: plugin syscalls
>>   cpu: hook plugin vcpu events
>>   plugin-gen: add plugin_insn_append
>>   translator: add translator_ld{ub,sw,uw,l,q}
>>   target/arm: call qemu_plugin_insn_append
>>   target/ppc: fetch code with translator_ld
>>   target/sh4: fetch code with translator_ld
>>   target/i386: fetch code with translator_ld
>>   target/hppa: fetch code with translator_ld
>>   target/m68k: fetch code with translator_ld
>>   target/alpha: fetch code with translator_ld
>>   target/riscv: fetch code with translator_ld
>>   target/sparc: fetch code with translator_ld
>>   target/xtensa: fetch code with translator_ld
>>   target/openrisc: fetch code with translator_ld
>>   translator: inject instrumentation from plugins
>>   plugin: add API symbols to qemu-plugins.symbols
>>   tests/plugin: add sample plugins
>>
>> Lluís Vilanova (2):
>>   vl: support -plugin option
>>   linux-user: support -plugin option
>>
>>  Makefile                                  |   11 +-
>>  Makefile.target                           |    2 +
>>  accel/stubs/tcg-stub.c                    |    3 -
>>  accel/tcg/Makefile.objs                   |    1 +
>>  accel/tcg/atomic_template.h               |  117 ++-
>>  accel/tcg/cpu-exec.c                      |    3 +
>>  accel/tcg/cputlb.c                        |   56 +-
>>  accel/tcg/plugin-gen.c                    | 1079 +++++++++++++++++++++
>>  accel/tcg/plugin-helpers.h                |    6 +
>>  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 +
>>  disas.c                                   |  103 ++
>>  docs/devel/index.rst                      |    1 +
>>  docs/devel/plugins.rst                    |   99 ++
>>  exec.c                                    |    2 +
>>  include/disas/disas.h                     |    2 +
>>  include/exec/cpu-defs.h                   |   11 +-
>>  include/exec/cpu_ldst.h                   |    9 +
>>  include/exec/cpu_ldst_template.h          |   40 +-
>>  include/exec/cpu_ldst_useronly_template.h |   36 +-
>>  include/exec/exec-all.h                   |   64 +-
>>  include/exec/helper-gen.h                 |    1 +
>>  include/exec/helper-proto.h               |    1 +
>>  include/exec/helper-tcg.h                 |    1 +
>>  include/exec/plugin-gen.h                 |   71 ++
>>  include/exec/translator.h                 |   30 +-
>>  include/qemu/plugin.h                     |  261 +++++
>>  include/qemu/qemu-plugin.h                |  349 +++++++
>>  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 +
>>  plugins/.gitignore                        |    2 +
>>  plugins/Makefile.objs                     |   21 +
>>  plugins/api.c                             |  274 ++++++
>>  plugins/core.c                            |  524 ++++++++++
>>  plugins/loader.c                          |  353 +++++++
>>  plugins/plugin.h                          |   95 ++
>>  plugins/qemu-plugins.symbols              |   36 +
>>  qemu-options.hx                           |   17 +
>>  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                    |    4 +-
>>  target/sparc/translate.c                  |    2 +-
>>  target/xtensa/translate.c                 |    4 +-
>>  tcg/README                                |    2 +-
>>  tcg/i386/tcg-target.inc.c                 |    8 +
>>  tcg/tcg-op.c                              |   40 +-
>>  tcg/tcg-op.h                              |   16 +
>>  tcg/tcg-opc.h                             |    3 +
>>  tcg/tcg.c                                 |   22 +
>>  tcg/tcg.h                                 |   28 +
>>  tests/Makefile.include                    |   10 +-
>>  tests/plugin/Makefile                     |   31 +
>>  tests/plugin/bb.c                         |   66 ++
>>  tests/plugin/empty.c                      |   30 +
>>  tests/plugin/hotblocks.c                  |  145 +++
>>  tests/plugin/hotpages.c                   |  152 +++
>>  tests/plugin/howvec.c                     |  297 ++++++
>>  tests/plugin/insn.c                       |   63 ++
>>  tests/plugin/mem.c                        |   93 ++
>>  tests/tcg/Makefile                        |   34 +
>>  tests/tcg/arm/Makefile.softmmu-target     |    1 +
>>  trace-events                              |    2 +-
>>  vl.c                                      |   11 +
>>  78 files changed, 4855 insertions(+), 122 deletions(-)
>>  create mode 100644 accel/tcg/plugin-gen.c
>>  create mode 100644 accel/tcg/plugin-helpers.h
>>  create mode 100644 docs/devel/plugins.rst
>>  create mode 100644 include/exec/plugin-gen.h
>>  create mode 100644 include/qemu/plugin.h
>>  create mode 100644 include/qemu/qemu-plugin.h
>>  create mode 100644 plugins/.gitignore
>>  create mode 100644 plugins/Makefile.objs
>>  create mode 100644 plugins/api.c
>>  create mode 100644 plugins/core.c
>>  create mode 100644 plugins/loader.c
>>  create mode 100644 plugins/plugin.h
>>  create mode 100644 plugins/qemu-plugins.symbols
>>  create mode 100644 tests/plugin/Makefile
>>  create mode 100644 tests/plugin/bb.c
>>  create mode 100644 tests/plugin/empty.c
>>  create mode 100644 tests/plugin/hotblocks.c
>>  create mode 100644 tests/plugin/hotpages.c
>>  create mode 100644 tests/plugin/howvec.c
>>  create mode 100644 tests/plugin/insn.c
>>  create mode 100644 tests/plugin/mem.c
>>
>> --
>> 2.20.1
>>
>>


--
Alex Bennée


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

* Re: [Qemu-devel] [PATCH  v3 00/50] tcg plugin support
  2019-06-14 17:11 [Qemu-devel] [PATCH v3 00/50] tcg plugin support Alex Bennée
                   ` (50 preceding siblings ...)
  2019-06-14 17:41 ` [Qemu-devel] [PATCH v3 00/50] tcg plugin support Aleksandar Markovic
@ 2019-06-14 19:47 ` no-reply
  2019-06-14 19:48 ` no-reply
  2019-06-20 13:53 ` Pranith Kumar
  53 siblings, 0 replies; 117+ messages in thread
From: no-reply @ 2019-06-14 19:47 UTC (permalink / raw)
  To: alex.bennee; +Cc: alex.bennee, qemu-devel

Patchew URL: https://patchew.org/QEMU/20190614171200.21078-1-alex.bennee@linaro.org/



Hi,

This series failed the asan build test. Please find the testing commands and
their output below. If you have Docker installed, you can probably reproduce it
locally.

=== TEST SCRIPT BEGIN ===
#!/bin/bash
make docker-image-fedora V=1 NETWORK=1
time make docker-test-debug@fedora TARGET_LIST=x86_64-softmmu J=14 NETWORK=1
=== TEST SCRIPT END ===

  CC      x86_64-softmmu/balloon.o
  CC      x86_64-softmmu/ioport.o
  CC      x86_64-softmmu/numa.o
/tmp/qemu-test/src/disas.c:502:5: error: use of undeclared identifier 'csh'
    csh handle;
    ^
/tmp/qemu-test/src/disas.c:503:5: error: use of undeclared identifier 'cs_insn'
    cs_insn *insn;
    ^
/tmp/qemu-test/src/disas.c:503:14: error: use of undeclared identifier 'insn'; did you mean 'info'?
    cs_insn *insn;
             ^~~~
             info
/tmp/qemu-test/src/disas.c:499:53: note: 'info' declared here
static bool plugin_cap_disas_insn(disassemble_info *info, uint64_t pc, size_t size)
                                                    ^
/tmp/qemu-test/src/disas.c:508:9: error: implicit declaration of function 'cap_disas_start' is invalid in C99 [-Werror,-Wimplicit-function-declaration]
    if (cap_disas_start(info, &handle) != CS_ERR_OK) {
        ^
/tmp/qemu-test/src/disas.c:508:9: error: this function declaration is not a prototype [-Werror,-Wstrict-prototypes]
/tmp/qemu-test/src/disas.c:508:32: error: use of undeclared identifier 'handle'
    if (cap_disas_start(info, &handle) != CS_ERR_OK) {
                               ^
/tmp/qemu-test/src/disas.c:508:43: error: use of undeclared identifier 'CS_ERR_OK'
    if (cap_disas_start(info, &handle) != CS_ERR_OK) {
                                          ^
/tmp/qemu-test/src/disas.c:511:5: error: use of undeclared identifier 'insn'
    insn = cap_insn;
    ^
/tmp/qemu-test/src/disas.c:511:12: error: use of undeclared identifier 'cap_insn'
    insn = cap_insn;
           ^
/tmp/qemu-test/src/disas.c:517:13: error: implicit declaration of function 'cs_disasm' is invalid in C99 [-Werror,-Wimplicit-function-declaration]
    count = cs_disasm(handle, cbuf, size, 0, 1, &insn);
            ^
/tmp/qemu-test/src/disas.c:517:13: error: this function declaration is not a prototype [-Werror,-Wstrict-prototypes]
/tmp/qemu-test/src/disas.c:517:23: error: use of undeclared identifier 'handle'
    count = cs_disasm(handle, cbuf, size, 0, 1, &insn);
                      ^
/tmp/qemu-test/src/disas.c:517:50: error: use of undeclared identifier 'insn'
    count = cs_disasm(handle, cbuf, size, 0, 1, &insn);
                                                 ^
/tmp/qemu-test/src/disas.c:520:37: error: use of undeclared identifier 'insn'
        g_string_printf(s, "%s %s", insn->mnemonic, insn->op_str);
                                    ^
/tmp/qemu-test/src/disas.c:520:53: error: use of undeclared identifier 'insn'
        g_string_printf(s, "%s %s", insn->mnemonic, insn->op_str);
                                                    ^
/tmp/qemu-test/src/disas.c:525:5: error: implicit declaration of function 'cs_close' is invalid in C99 [-Werror,-Wimplicit-function-declaration]
    cs_close(&handle);
    ^
/tmp/qemu-test/src/disas.c:525:5: error: this function declaration is not a prototype [-Werror,-Wstrict-prototypes]
/tmp/qemu-test/src/disas.c:525:15: error: use of undeclared identifier 'handle'
    cs_close(&handle);
              ^
18 errors generated.


The full log is available at
http://patchew.org/logs/20190614171200.21078-1-alex.bennee@linaro.org/testing.asan/?type=message.
---
Email generated automatically by Patchew [https://patchew.org/].
Please send your feedback to patchew-devel@redhat.com

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

* Re: [Qemu-devel] [PATCH  v3 00/50] tcg plugin support
  2019-06-14 17:11 [Qemu-devel] [PATCH v3 00/50] tcg plugin support Alex Bennée
                   ` (51 preceding siblings ...)
  2019-06-14 19:47 ` no-reply
@ 2019-06-14 19:48 ` no-reply
  2019-06-20 13:53 ` Pranith Kumar
  53 siblings, 0 replies; 117+ messages in thread
From: no-reply @ 2019-06-14 19:48 UTC (permalink / raw)
  To: alex.bennee; +Cc: alex.bennee, qemu-devel

Patchew URL: https://patchew.org/QEMU/20190614171200.21078-1-alex.bennee@linaro.org/



Hi,

This series seems to have some coding style problems. See output below for
more information:

Message-id: 20190614171200.21078-1-alex.bennee@linaro.org
Type: series
Subject: [Qemu-devel] [PATCH  v3 00/50] tcg plugin support

=== TEST SCRIPT BEGIN ===
#!/bin/bash
git rev-parse base > /dev/null || exit 0
git config --local diff.renamelimit 0
git config --local diff.renames True
git config --local diff.algorithm histogram
./scripts/checkpatch.pl --mailback base..
=== TEST SCRIPT END ===

Updating 3c8cf5a9c21ff8782164d1def7f44bd888713384
Switched to a new branch 'test'
049048f TODO: API changes to make?
ed124e9 include/exec/cpu-defs.h: fix typo
f5a1e5b include/exec: wrap cpu_ldst.h in CONFIG_TCG
9ba123b accel/stubs: reduce headers from tcg-stub
df9793f tests/plugin: add hotpages plugin to breakdown memory access patterns
d1f355e tests/plugin: add instruction execution breakdown
6d4683c plugin: add qemu_plugin_insn_disas helper
e3145c6 tests/plugin: add a hotblocks plugin
473cace tests/tcg: enable plugin testing
c99d2ca tests/plugin: add sample plugins
f92b641 linux-user: support -plugin option
f81dc94 vl: support -plugin option
11d158f plugin: add API symbols to qemu-plugins.symbols
d0af116 translator: inject instrumentation from plugins
075f779 target/openrisc: fetch code with translator_ld
6426594 target/xtensa: fetch code with translator_ld
38fa0bd target/sparc: fetch code with translator_ld
96a7c87 target/riscv: fetch code with translator_ld
2bbeaea target/alpha: fetch code with translator_ld
2a3f91f target/m68k: fetch code with translator_ld
86b0923 target/hppa: fetch code with translator_ld
206cd1e target/i386: fetch code with translator_ld
c7ffbb8 target/sh4: fetch code with translator_ld
23bae27 target/ppc: fetch code with translator_ld
04e9634 target/arm: call qemu_plugin_insn_append
90c2eaa translator: add translator_ld{ub, sw, uw, l, q}
9681e1f plugin-gen: add plugin_insn_append
6c03f40 cpu: hook plugin vcpu events
aa70581 *-user: plugin syscalls
d9de19e *-user: notify plugin of exit
2a222c5 translate-all: notify plugin code of tb_flush
fa91333 tcg: let plugins instrument memory accesses
3644ead cpu_ldst_useronly_template: remove redundant #ifndef CODE_ACCESS
ab9dbec atomic_template: add inline trace/plugin helpers
e4aef56 atomic_template: fix indentation in GEN_ATOMIC_HELPER
ee3eba8 tcg: add MO_HADDR to TCGMemOp
3760a15 tcg: add tcg_gen_st_ptr
a4af748 plugin-gen: add module for TCG-related code
7f9020a cputlb: introduce get_page_addr_code_hostp
9a46037 cputlb: document get_page_addr_code
6d8e505 queue: add QTAILQ_REMOVE_SEVERAL
a3f7fb1 plugin: add implementation of the api
1bd3802 plugin: add core code
eb7a5d5 plugin: add user-facing API
33d885f configure: add --enable-plugins (MOVE TO END)
3fd3aeb docs/devel: add plugins.rst design document
dd4b2c0 translate-all: use cpu_in_exclusive_work_context() in tb_flush
6ef20c7 cpu: introduce cpu_in_exclusive_work_context()
8995c5a tcg/README: fix typo s/afterwise/afterwards/
b13029e trace: expand mem_info:size_shift to 3 bits

=== OUTPUT BEGIN ===
1/50 Checking commit b13029ec3c61 (trace: expand mem_info:size_shift to 3 bits)
2/50 Checking commit 8995c5a5814a (tcg/README: fix typo s/afterwise/afterwards/)
3/50 Checking commit 6ef20c7ac84b (cpu: introduce cpu_in_exclusive_work_context())
4/50 Checking commit dd4b2c0b5d58 (translate-all: use cpu_in_exclusive_work_context() in tb_flush)
5/50 Checking commit 3fd3aeb92699 (docs/devel: add plugins.rst design document)
WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
#23: 
new file mode 100644

total: 0 errors, 1 warnings, 103 lines checked

Patch 5/50 has style problems, please review.  If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.
6/50 Checking commit 33d885f24794 (configure: add --enable-plugins (MOVE TO END))
7/50 Checking commit eb7a5d579131 (plugin: add user-facing API)
WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
#15: 
new file mode 100644

WARNING: architecture specific defines should be avoided
#41: FILE: include/qemu/qemu-plugin.h:22:
+#if defined _WIN32 || defined __CYGWIN__

WARNING: architecture specific defines should be avoided
#49: FILE: include/qemu/qemu-plugin.h:30:
+  #if __GNUC__ >= 4

total: 0 errors, 3 warnings, 339 lines checked

Patch 7/50 has style problems, please review.  If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.
8/50 Checking commit 1bd3802f7e55 (plugin: add core code)
WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
#56: 
new file mode 100644

ERROR: "foo * bar" should be "foo *bar"
#197: FILE: include/qemu/plugin.h:137:
+static inline struct qemu_plugin_insn * qemu_plugin_insn_alloc(void)

WARNING: Block comments use a leading /* on a separate line
#371: FILE: plugins/core.c:1:
+/* plugin.c - QEMU Plugin interface

ERROR: named QLIST_HEAD should be typedefed separately
#407: FILE: plugins/core.c:37:
+QLIST_HEAD(qemu_plugin_cb_head, qemu_plugin_cb);

WARNING: line over 80 characters
#626: FILE: plugins/core.c:256:
+        cbs = g_array_sized_new(false, false, sizeof(struct qemu_plugin_dyn_cb), 1);

WARNING: Block comments use a leading /* on a separate line
#942: FILE: plugins/loader.c:42:
+        { /* end of list */ }

ERROR: externs should be avoided in .c files
#948: FILE: plugins/loader.c:48:
+extern struct qemu_plugin_state plugin;

total: 3 errors, 4 warnings, 1294 lines checked

Patch 8/50 has style problems, please review.  If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.

9/50 Checking commit a3f7fb1c3afd (plugin: add implementation of the api)
WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
#21: 
new file mode 100644

total: 0 errors, 1 warnings, 271 lines checked

Patch 9/50 has style problems, please review.  If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.
10/50 Checking commit 6d8e50569c13 (queue: add QTAILQ_REMOVE_SEVERAL)
WARNING: Block comments use a leading /* on a separate line
#30: FILE: include/qemu/queue.h:433:
+    } while (/*CONSTCOND*/0)

total: 0 errors, 1 warnings, 16 lines checked

Patch 10/50 has style problems, please review.  If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.
11/50 Checking commit 9a46037bfb3b (cputlb: document get_page_addr_code)
12/50 Checking commit 7f9020af50d0 (cputlb: introduce get_page_addr_code_hostp)
13/50 Checking commit a4af74838a09 (plugin-gen: add module for TCG-related code)
WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
#48: 
new file mode 100644

total: 0 errors, 1 warnings, 1273 lines checked

Patch 13/50 has style problems, please review.  If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.
14/50 Checking commit 3760a1583150 (tcg: add tcg_gen_st_ptr)
15/50 Checking commit ee3eba82d0e6 (tcg: add MO_HADDR to TCGMemOp)
16/50 Checking commit e4aef56e6d7b (atomic_template: fix indentation in GEN_ATOMIC_HELPER)
17/50 Checking commit ab9dbecd0947 (atomic_template: add inline trace/plugin helpers)
18/50 Checking commit 3644ead62d44 (cpu_ldst_useronly_template: remove redundant #ifndef CODE_ACCESS)
19/50 Checking commit fa91333b879c (tcg: let plugins instrument memory accesses)
ERROR: line over 90 characters
#224: FILE: accel/tcg/plugin-gen.c:98:
+    tcg_gen_ld_i32(cpu_index, cpu_env, -offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index));

total: 1 errors, 0 warnings, 542 lines checked

Patch 19/50 has style problems, please review.  If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.

20/50 Checking commit 2a222c5a56d6 (translate-all: notify plugin code of tb_flush)
21/50 Checking commit d9de19e4841b (*-user: notify plugin of exit)
22/50 Checking commit aa7058138134 (*-user: plugin syscalls)
23/50 Checking commit 6c03f4039c79 (cpu: hook plugin vcpu events)
24/50 Checking commit 9681e1f77508 (plugin-gen: add plugin_insn_append)
25/50 Checking commit 90c2eaa2247d (translator: add translator_ld{ub, sw, uw, l, q})
WARNING: Block comments use a leading /* on a separate line
#49: FILE: include/exec/translator.h:166:
+GEN_TRANSLATOR_LD(translator_ldub, ldub, uint8_t, /* no swap needed */)

total: 0 errors, 1 warnings, 40 lines checked

Patch 25/50 has style problems, please review.  If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.
26/50 Checking commit 04e963494144 (target/arm: call qemu_plugin_insn_append)
27/50 Checking commit 23bae27d37b4 (target/ppc: fetch code with translator_ld)
28/50 Checking commit c7ffbb8780fb (target/sh4: fetch code with translator_ld)
29/50 Checking commit 206cd1e53cc9 (target/i386: fetch code with translator_ld)
30/50 Checking commit 86b0923359a8 (target/hppa: fetch code with translator_ld)
31/50 Checking commit 2a3f91fd39e9 (target/m68k: fetch code with translator_ld)
32/50 Checking commit 2bbeaea354dc (target/alpha: fetch code with translator_ld)
33/50 Checking commit 96a7c87ae19f (target/riscv: fetch code with translator_ld)
34/50 Checking commit 38fa0bd596fd (target/sparc: fetch code with translator_ld)
35/50 Checking commit 642659499636 (target/xtensa: fetch code with translator_ld)
36/50 Checking commit 075f7795fe2c (target/openrisc: fetch code with translator_ld)
37/50 Checking commit d0af1163d422 (translator: inject instrumentation from plugins)
38/50 Checking commit 11d158f19449 (plugin: add API symbols to qemu-plugins.symbols)
WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
#120: 
new file mode 100644

total: 0 errors, 1 warnings, 148 lines checked

Patch 38/50 has style problems, please review.  If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.
39/50 Checking commit f81dc947d361 (vl: support -plugin option)
40/50 Checking commit f92b641790b2 (linux-user: support -plugin option)
41/50 Checking commit c99d2cab03ae (tests/plugin: add sample plugins)
WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
#34: 
new file mode 100644

total: 0 errors, 1 warnings, 296 lines checked

Patch 41/50 has style problems, please review.  If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.
42/50 Checking commit 473cace55471 (tests/tcg: enable plugin testing)
43/50 Checking commit e3145c6f3945 (tests/plugin: add a hotblocks plugin)
WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
#32: 
new file mode 100644

ERROR: line over 90 characters
#97: FILE: tests/plugin/hotblocks.c:61:
+        g_string_append_printf(report, "  pc: %#" PRIx64 " (%d block%s, %ld insns, %" PRId64" total hits)\n",

WARNING: line over 80 characters
#99: FILE: tests/plugin/hotblocks.c:63:
+                               rec->trans_count, rec->trans_count < 2 ? "" : "s",

WARNING: line over 80 characters
#121: FILE: tests/plugin/hotblocks.c:85:
+    cnt = (ExecCount *) g_hash_table_lookup(hotblocks, (gconstpointer) cheap_hash);

WARNING: line over 80 characters
#142: FILE: tests/plugin/hotblocks.c:106:
+    cnt = (ExecCount *) g_hash_table_lookup(hotblocks, (gconstpointer) cheap_hash);

total: 1 errors, 4 warnings, 152 lines checked

Patch 43/50 has style problems, please review.  If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.

44/50 Checking commit 6d4683cf10dc (plugin: add qemu_plugin_insn_disas helper)
ERROR: space prohibited between function name and open parenthesis '('
#36: FILE: disas.c:493:
+static void plugin_print_address (bfd_vma addr, struct disassemble_info *info)

WARNING: line over 80 characters
#42: FILE: disas.c:499:
+static bool plugin_cap_disas_insn(disassemble_info *info, uint64_t pc, size_t size)

ERROR: "foo * bar" should be "foo *bar"
#72: FILE: disas.c:529:
+char * plugin_disas(CPUState *cpu, uint64_t addr, size_t size)

ERROR: braces {} are necessary for all arms of this statement
#115: FILE: disas.c:572:
+        if (count < 0)
[...]

ERROR: "foo * bar" should be "foo *bar"
#135: FILE: include/disas/disas.h:16:
+char * plugin_disas(CPUState *cpu, uint64_t addr, size_t size);

ERROR: "foo * bar" should be "foo *bar"
#154: FILE: include/qemu/qemu-plugin.h:322:
+char * qemu_plugin_insn_disas(const struct qemu_plugin_insn *insn);

total: 5 errors, 1 warnings, 158 lines checked

Patch 44/50 has style problems, please review.  If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.

45/50 Checking commit d1f355eb9db4 (tests/plugin: add instruction execution breakdown)
WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
#26: 
new file mode 100644

ERROR: space required after that ',' (ctx:VxV)
#99: FILE: tests/plugin/howvec.c:69:
+    { "  Add/Sub (imm,tags)","asit",   0x1f800000, 0x11800000, COUNT_CLASS},
                             ^

ERROR: space required after that ',' (ctx:VxV)
#122: FILE: tests/plugin/howvec.c:92:
+    { "  AdvSimd ldstmult++","advlsmp",0xbfb00000, 0x0c800000, COUNT_CLASS},
                             ^

ERROR: space required after that ',' (ctx:VxV)
#122: FILE: tests/plugin/howvec.c:92:
+    { "  AdvSimd ldstmult++","advlsmp",0xbfb00000, 0x0c800000, COUNT_CLASS},
                                       ^

ERROR: space required after that ',' (ctx:VxV)
#124: FILE: tests/plugin/howvec.c:94:
+    { "  AdvSimd ldst++",    "advlssp",0xbf800000, 0x0d800000, COUNT_CLASS},
                                       ^

ERROR: space required after that ',' (ctx:VxV)
#128: FILE: tests/plugin/howvec.c:98:
+    { "  ldst noalloc pair", "ldstnap",0x3b800000, 0x28000000, COUNT_CLASS},
                                       ^

ERROR: space required after that ',' (ctx:VxV)
#132: FILE: tests/plugin/howvec.c:102:
+    { "  ldst reg (reg off)","ldstro", 0x3b200b00, 0x38200800, COUNT_CLASS},
                             ^

ERROR: space required after that ',' (ctx:VxV)
#161: FILE: tests/plugin/howvec.c:131:
+                g_string_append_printf(report,"Class: %-24s\t(%ld hits)\n",
                                              ^

WARNING: line over 80 characters
#162: FILE: tests/plugin/howvec.c:132:
+                                       insn_classes[i].class, insn_classes[i].count);

WARNING: line over 80 characters
#166: FILE: tests/plugin/howvec.c:136:
+            g_string_append_printf(report,"Class: %-24s\tcounted individually\n",

ERROR: space required after that ',' (ctx:VxV)
#166: FILE: tests/plugin/howvec.c:136:
+            g_string_append_printf(report,"Class: %-24s\tcounted individually\n",
                                          ^

ERROR: space required after that ',' (ctx:VxV)
#170: FILE: tests/plugin/howvec.c:140:
+            g_string_append_printf(report,"Class: %-24s\tnot counted\n",
                                          ^

ERROR: space required after that ',' (ctx:VxV)
#182: FILE: tests/plugin/howvec.c:152:
+        g_string_append_printf(report,"Individual Instructions:\n");
                                      ^

WARNING: line over 80 characters
#188: FILE: tests/plugin/howvec.c:158:
+            g_string_append_printf(report, "Instr: %-24s\t(%ld hits)\t(op=%#08x/%s)\n",

WARNING: line over 80 characters
#192: FILE: tests/plugin/howvec.c:162:
+                                   rec->class ? rec->class->class : "un-categorised");

ERROR: "foo * bar" should be "foo *bar"
#212: FILE: tests/plugin/howvec.c:182:
+static uint64_t * find_counter(struct qemu_plugin_insn *insn)

WARNING: line over 80 characters
#243: FILE: tests/plugin/howvec.c:213:
+        icount = (InsnExecCount *) g_hash_table_lookup(insns, GUINT_TO_POINTER(opcode));

ERROR: line over 90 characters
#252: FILE: tests/plugin/howvec.c:222:
+                dprintf(stdout_fd, "adding instrumentation for %s (%#08x @ %#20lx from %s)\n",

WARNING: line over 80 characters
#253: FILE: tests/plugin/howvec.c:223:
+                        icount->insn, opcode, qemu_plugin_insn_vaddr(insn), class->class);

WARNING: line over 80 characters
#255: FILE: tests/plugin/howvec.c:225:
+            g_hash_table_insert(insns, GUINT_TO_POINTER(opcode), (gpointer) icount);

total: 12 errors, 8 warnings, 304 lines checked

Patch 45/50 has style problems, please review.  If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.

46/50 Checking commit df9793f9866c (tests/plugin: add hotpages plugin to breakdown memory access patterns)
WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
#25: 
new file mode 100644

ERROR: space required after that ',' (ctx:VxV)
#88: FILE: tests/plugin/hotpages.c:59:
+        g_string_append_printf(report,"Individual Instructions:\n");
                                      ^

ERROR: line over 90 characters
#94: FILE: tests/plugin/hotpages.c:65:
+            g_string_append_printf(report, "Page: %"PRIx64" cpus:%#04x/%#04x %"PRId64"/%"PRId64"\n",

ERROR: that open brace { should be on the previous line
#152: FILE: tests/plugin/hotpages.c:123:
+static GOptionEntry entries[] =
+{

WARNING: line over 80 characters
#154: FILE: tests/plugin/hotpages.c:125:
+    { "inline", 'i', 0, G_OPTION_ARG_NONE, &do_inline, "Inline counters", NULL },

ERROR: space prohibited between function name and open parenthesis '('
#164: FILE: tests/plugin/hotpages.c:135:
+    context = g_option_context_new ("- hot pages tool");

ERROR: space prohibited between function name and open parenthesis '('
#165: FILE: tests/plugin/hotpages.c:136:
+    g_option_context_add_main_entries (context, entries, NULL);

ERROR: that open brace { should be on the previous line
#166: FILE: tests/plugin/hotpages.c:137:
+    if (!g_option_context_parse (context, &argc, &argv, &error))
+    {

ERROR: space prohibited between function name and open parenthesis '('
#166: FILE: tests/plugin/hotpages.c:137:
+    if (!g_option_context_parse (context, &argc, &argv, &error))

total: 7 errors, 2 warnings, 159 lines checked

Patch 46/50 has style problems, please review.  If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.

47/50 Checking commit 9ba123bb95e6 (accel/stubs: reduce headers from tcg-stub)
48/50 Checking commit f5a1e5b54136 (include/exec: wrap cpu_ldst.h in CONFIG_TCG)
49/50 Checking commit ed124e922b01 (include/exec/cpu-defs.h: fix typo)
50/50 Checking commit 049048fdb87a (TODO: API changes to make?)
ERROR: Missing Signed-off-by: line(s)

total: 1 errors, 0 warnings, 7 lines checked

Patch 50/50 has style problems, please review.  If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.

=== OUTPUT END ===

Test command exited with code: 1


The full log is available at
http://patchew.org/logs/20190614171200.21078-1-alex.bennee@linaro.org/testing.checkpatch/?type=message.
---
Email generated automatically by Patchew [https://patchew.org/].
Please send your feedback to patchew-devel@redhat.com

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

* Re: [Qemu-devel] [PATCH v3 01/50] trace: expand mem_info:size_shift to 3 bits
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 01/50] trace: expand mem_info:size_shift to 3 bits Alex Bennée
@ 2019-06-17  2:12   ` Richard Henderson
  2019-06-17  8:22     ` Alex Bennée
  0 siblings, 1 reply; 117+ messages in thread
From: Richard Henderson @ 2019-06-17  2:12 UTC (permalink / raw)
  To: Alex Bennée, qemu-devel; +Cc: Emilio G. Cota, Stefan Hajnoczi

On 6/14/19 10:11 AM, Alex Bennée wrote:
> From: "Emilio G. Cota" <cota@braap.org>
> 
> This will allow us to trace 16B-long memory accesses.
> 
> Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
> 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 844ee58dd9..037169aab3 100644
> --- a/trace-events
> +++ b/trace-events
> @@ -159,7 +159,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 */
> 

Well, 128B-long memory accesses.  But SVE supports 256B memory accesses
already.  So why not add one more bit now.


r~


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

* Re: [Qemu-devel] [PATCH v3 02/50] tcg/README: fix typo s/afterwise/afterwards/
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 02/50] tcg/README: fix typo s/afterwise/afterwards/ Alex Bennée
@ 2019-06-17  2:13   ` Richard Henderson
  0 siblings, 0 replies; 117+ messages in thread
From: Richard Henderson @ 2019-06-17  2:13 UTC (permalink / raw)
  To: Alex Bennée, qemu-devel; +Cc: Emilio G. Cota, Richard Henderson

On 6/14/19 10:11 AM, Alex Bennée wrote:
> From: "Emilio G. Cota" <cota@braap.org>
> 
> 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(-)

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>


r~


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

* Re: [Qemu-devel] [PATCH v3 03/50] cpu: introduce cpu_in_exclusive_work_context()
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 03/50] cpu: introduce cpu_in_exclusive_work_context() Alex Bennée
@ 2019-06-17  2:15   ` Richard Henderson
  2019-06-20  9:50     ` Alex Bennée
  0 siblings, 1 reply; 117+ messages in thread
From: Richard Henderson @ 2019-06-17  2:15 UTC (permalink / raw)
  To: Alex Bennée, qemu-devel; +Cc: Emilio G. Cota

On 6/14/19 10:11 AM, Alex Bennée wrote:
>              start_exclusive();
> +            cpu->in_exclusive_work_context = true;
>              wi->func(cpu, wi->data);
> +            cpu->in_exclusive_work_context = false;
>              end_exclusive();

Is there a reason not to put those into start/end_exclusive?
And if not, what does in_exclusive_work_context mean?


r~


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

* Re: [Qemu-devel] [PATCH v3 01/50] trace: expand mem_info:size_shift to 3 bits
  2019-06-17  2:12   ` Richard Henderson
@ 2019-06-17  8:22     ` Alex Bennée
  2019-06-17 23:29       ` Richard Henderson
  0 siblings, 1 reply; 117+ messages in thread
From: Alex Bennée @ 2019-06-17  8:22 UTC (permalink / raw)
  To: Richard Henderson; +Cc: Emilio G. Cota, qemu-devel, Stefan Hajnoczi


Richard Henderson <richard.henderson@linaro.org> writes:

> On 6/14/19 10:11 AM, Alex Bennée wrote:
>> From: "Emilio G. Cota" <cota@braap.org>
>>
>> This will allow us to trace 16B-long memory accesses.
>>
>> Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
>> 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 844ee58dd9..037169aab3 100644
>> --- a/trace-events
>> +++ b/trace-events
>> @@ -159,7 +159,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 */
>>
>
> Well, 128B-long memory accesses.  But SVE supports 256B memory accesses
> already.  So why not add one more bit now.

Good point.

Do we have any architectures that do load/stores that are not power of
2? I guess the SVE non-faulting accesses are treated as a series of elem
size accesses.

>
>
> r~


--
Alex Bennée


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

* Re: [Qemu-devel] [PATCH v3 14/50] tcg: add tcg_gen_st_ptr
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 14/50] tcg: add tcg_gen_st_ptr Alex Bennée
@ 2019-06-17 20:19   ` Richard Henderson
  0 siblings, 0 replies; 117+ messages in thread
From: Richard Henderson @ 2019-06-17 20:19 UTC (permalink / raw)
  To: Alex Bennée, qemu-devel; +Cc: Emilio G. Cota, Richard Henderson

On 6/14/19 10:11 AM, Alex Bennée wrote:
> From: "Emilio G. Cota" <cota@braap.org>
> 
> Will gain a user soon.
> 
> Signed-off-by: Emilio G. Cota <cota@braap.org>
> ---
>  tcg/tcg-op.h | 5 +++++
>  1 file changed, 5 insertions(+)

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>


r~


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

* Re: [Qemu-devel] [PATCH v3 15/50] tcg: add MO_HADDR to TCGMemOp
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 15/50] tcg: add MO_HADDR to TCGMemOp Alex Bennée
@ 2019-06-17 20:43   ` Richard Henderson
  0 siblings, 0 replies; 117+ messages in thread
From: Richard Henderson @ 2019-06-17 20:43 UTC (permalink / raw)
  To: Alex Bennée, qemu-devel; +Cc: Emilio G. Cota, Richard Henderson

On 6/14/19 10:11 AM, Alex Bennée wrote:
> +    /*
> +     * 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,

FYI, Alex and I talked about recomputing the host address within the helper.

For at least a few of the hosts, we currently never compute the full host
address into a single register -- we use reg+reg addressing when possible.
It's only a couple of instructions to re-compute, given that we know that the
tlb lookup succeeded, and importantly they are all out of line and not bloating
the inline code further.


r~


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

* Re: [Qemu-devel] [PATCH v3 16/50] atomic_template: fix indentation in GEN_ATOMIC_HELPER
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 16/50] atomic_template: fix indentation in GEN_ATOMIC_HELPER Alex Bennée
@ 2019-06-17 20:43   ` Richard Henderson
  0 siblings, 0 replies; 117+ messages in thread
From: Richard Henderson @ 2019-06-17 20:43 UTC (permalink / raw)
  To: Alex Bennée, qemu-devel
  Cc: Paolo Bonzini, Emilio G. Cota, Richard Henderson

On 6/14/19 10:11 AM, Alex Bennée wrote:
> From: "Emilio G. Cota" <cota@braap.org>
> 
> 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(-)

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>


r~


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

* Re: [Qemu-devel] [PATCH v3 17/50] atomic_template: add inline trace/plugin helpers
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 17/50] atomic_template: add inline trace/plugin helpers Alex Bennée
@ 2019-06-17 20:47   ` Richard Henderson
  0 siblings, 0 replies; 117+ messages in thread
From: Richard Henderson @ 2019-06-17 20:47 UTC (permalink / raw)
  To: Alex Bennée, qemu-devel
  Cc: Paolo Bonzini, Emilio G. Cota, Richard Henderson

On 6/14/19 10:11 AM, Alex Bennée wrote:
> +#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_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_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_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 */
>  

All of this should just go into atomic_common.inc.c.


r~


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

* Re: [Qemu-devel] [PATCH v3 18/50] cpu_ldst_useronly_template: remove redundant #ifndef CODE_ACCESS
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 18/50] cpu_ldst_useronly_template: remove redundant #ifndef CODE_ACCESS Alex Bennée
@ 2019-06-17 20:47   ` Richard Henderson
  0 siblings, 0 replies; 117+ messages in thread
From: Richard Henderson @ 2019-06-17 20:47 UTC (permalink / raw)
  To: Alex Bennée, qemu-devel
  Cc: Paolo Bonzini, Emilio G. Cota, Richard Henderson

On 6/14/19 10:11 AM, Alex Bennée wrote:
> From: "Emilio G. Cota" <cota@braap.org>
> 
> This function is already under #ifndef CODE_ACCESS.
> 
> Signed-off-by: Emilio G. Cota <cota@braap.org>
> ---
>  include/exec/cpu_ldst_useronly_template.h | 2 --
>  1 file changed, 2 deletions(-)

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>


r~


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

* Re: [Qemu-devel] [PATCH v3 19/50] tcg: let plugins instrument memory accesses
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 19/50] tcg: let plugins instrument memory accesses Alex Bennée
@ 2019-06-17 20:51   ` Richard Henderson
  2019-06-28 15:30   ` Aaron Lindsay OS via Qemu-devel
  1 sibling, 0 replies; 117+ messages in thread
From: Richard Henderson @ 2019-06-17 20:51 UTC (permalink / raw)
  To: Alex Bennée, qemu-devel
  Cc: Paolo Bonzini, Emilio G. Cota, Richard Henderson

On 6/14/19 10:11 AM, Alex Bennée wrote:
> +static inline void set_hostaddr(CPUArchState *env, TCGMemOp mo, void *haddr)
> +{
> +#ifdef CONFIG_PLUGIN
> +    if (mo & MO_HADDR) {
> +        env_tlb(env)->c.hostaddr = haddr;
> +    }
> +#endif
> +}
> +

Even if we weren't talking about recomputing this in the helper, would an
unconditional store be cheaper?


r~


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

* Re: [Qemu-devel] [PATCH v3 20/50] translate-all: notify plugin code of tb_flush
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 20/50] translate-all: notify plugin code of tb_flush Alex Bennée
@ 2019-06-17 20:54   ` Richard Henderson
  0 siblings, 0 replies; 117+ messages in thread
From: Richard Henderson @ 2019-06-17 20:54 UTC (permalink / raw)
  To: Alex Bennée, qemu-devel
  Cc: Paolo Bonzini, Emilio G. Cota, Richard Henderson

On 6/14/19 10:11 AM, Alex Bennée wrote:
> From: "Emilio G. Cota" <cota@braap.org>
> 
> 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(+)

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>


r~


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

* Re: [Qemu-devel] [PATCH v3 21/50] *-user: notify plugin of exit
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 21/50] *-user: notify plugin of exit Alex Bennée
@ 2019-06-17 20:54   ` Richard Henderson
  0 siblings, 0 replies; 117+ messages in thread
From: Richard Henderson @ 2019-06-17 20:54 UTC (permalink / raw)
  To: Alex Bennée, qemu-devel; +Cc: Riku Voipio, Emilio G. Cota, Laurent Vivier

On 6/14/19 10:11 AM, Alex Bennée wrote:
> From: "Emilio G. Cota" <cota@braap.org>
> 
> 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(+)

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>


r~


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

* Re: [Qemu-devel] [PATCH v3 22/50] *-user: plugin syscalls
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 22/50] *-user: plugin syscalls Alex Bennée
@ 2019-06-17 20:56   ` Richard Henderson
  2019-06-19  3:35   ` Pranith Kumar
  1 sibling, 0 replies; 117+ messages in thread
From: Richard Henderson @ 2019-06-17 20:56 UTC (permalink / raw)
  To: Alex Bennée, qemu-devel; +Cc: Riku Voipio, Emilio G. Cota, Laurent Vivier

On 6/14/19 10:11 AM, Alex Bennée wrote:
> From: "Emilio G. Cota" <cota@braap.org>
> 
> Signed-off-by: Emilio G. Cota <cota@braap.org>
> ---
>  bsd-user/syscall.c   | 9 +++++++++
>  linux-user/syscall.c | 3 +++
>  2 files changed, 12 insertions(+)

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>


r~


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

* Re: [Qemu-devel] [PATCH v3 23/50] cpu: hook plugin vcpu events
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 23/50] cpu: hook plugin vcpu events Alex Bennée
@ 2019-06-17 21:00   ` Richard Henderson
  0 siblings, 0 replies; 117+ messages in thread
From: Richard Henderson @ 2019-06-17 21:00 UTC (permalink / raw)
  To: Alex Bennée, qemu-devel
  Cc: Paolo Bonzini, Emilio G. Cota, Richard Henderson

On 6/14/19 10:11 AM, Alex Bennée wrote:
>  static void qemu_wait_io_event(CPUState *cpu)
>  {
> +    bool slept = false;
> +
>      while (cpu_thread_is_idle(cpu)) {
> +        if (!slept) {
> +            slept = true;
> +            qemu_plugin_vcpu_idle_cb(cpu);
> +        }
>          qemu_cond_wait(cpu->halt_cond, &qemu_global_mutex);
>      }
> +    if (slept) {
> +        qemu_plugin_vcpu_resume_cb(cpu);
> +    }

Maybe better without the variable.

	if (cpu_thread_is_idle(cpu)) {
	    qemu_plugin_vcpu_idle_cb(cpu);
	    do {
	        qemu_cond_wait(cpu->halt_cond, &qemu_global_mutex);
	    } while (cpu_thread_is_idle(cpu);
	    qemu_plugin_vcpu_resume_cb(cpu);
	}

Otherwise,
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>


r~


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

* Re: [Qemu-devel] [PATCH v3 24/50] plugin-gen: add plugin_insn_append
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 24/50] plugin-gen: add plugin_insn_append Alex Bennée
@ 2019-06-17 21:03   ` Richard Henderson
  0 siblings, 0 replies; 117+ messages in thread
From: Richard Henderson @ 2019-06-17 21:03 UTC (permalink / raw)
  To: Alex Bennée, qemu-devel
  Cc: Paolo Bonzini, Emilio G. Cota, Richard Henderson

On 6/14/19 10:11 AM, Alex Bennée wrote:
> From: "Emilio G. Cota" <cota@braap.org>
> 
> 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>
> 
> ---
> v3
>   - use g_byte_array
> ---
>  accel/tcg/plugin-gen.c    | 10 +++++++++-
>  include/exec/plugin-gen.h | 23 ++++++++++++++---------
>  2 files changed, 23 insertions(+), 10 deletions(-)
> 
> diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c
> index 9d9ec29765..758fc5d099 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,
> diff --git a/include/exec/plugin-gen.h b/include/exec/plugin-gen.h
> index 449ea16034..316638c736 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,
> -};

Why is this movement in here, and can it be folded back?
It doesn't seem to be used from ...


> -
>  struct DisasContextBase;
>  
>  #ifdef CONFIG_PLUGIN
> @@ -36,6 +27,17 @@ 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;
> +    }
> +
> +    insn->data = g_byte_array_append(insn->data, from, size);
> +}
> +
>  #else /* !CONFIG_PLUGIN */
>  
>  static inline
> @@ -60,6 +62,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)
> +{ }
> +

... here.


r~


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

* Re: [Qemu-devel] [PATCH v3 25/50] translator: add translator_ld{ub, sw, uw, l, q}
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 25/50] translator: add translator_ld{ub, sw, uw, l, q} Alex Bennée
@ 2019-06-17 21:06   ` Richard Henderson
  2019-07-30 12:41     ` Alex Bennée
  0 siblings, 1 reply; 117+ messages in thread
From: Richard Henderson @ 2019-06-17 21:06 UTC (permalink / raw)
  To: Alex Bennée, qemu-devel; +Cc: Emilio G. Cota

On 6/14/19 10:11 AM, Alex Bennée wrote:
> +#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);                                         \
> +        }                                                               \

This feels like we should have done this at a different level.  We already have
lower-level functions that read from memory with the proper endianness.

It seems that we don't have them for *_code, but that could be fixed.  Or,
indeed, bypassed, since these could be the new official interface, deprecating
the *_code functions.


r~


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

* Re: [Qemu-devel] [PATCH v3 26/50] target/arm: call qemu_plugin_insn_append
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 26/50] target/arm: call qemu_plugin_insn_append Alex Bennée
@ 2019-06-17 22:28   ` Richard Henderson
  0 siblings, 0 replies; 117+ messages in thread
From: Richard Henderson @ 2019-06-17 22:28 UTC (permalink / raw)
  To: Alex Bennée, qemu-devel
  Cc: Peter Maydell, Emilio G. Cota, open list:ARM TCG CPUs

On 6/14/19 10:11 AM, Alex Bennée wrote:
> From: "Emilio G. Cota" <cota@braap.org>
> 
> 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.

We should in fact change that caller.

Unfortunately, the SVC immediate copied into env->exception.syndrome is
truncated to 16 bits, which means that the aa32 0x123456 won't match.

However, it would be easy enough to create new EXCP_SWI_SEMIHOST, generate it
within translate*.c when the svc immediate matches.  Everywhere except
check_for_semihosting(), we'd treat the two exceptions the same.

The BKPT instruction is only used for semihosting by thumb (and maybe only
v7m?).  The exception syndrome does contain the entire 8-bit immediate, however
for consistency it might be convenient to create an EXCP_BKPT_SEMIHOST so that
all of the checks are always done at translation time.


r~


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

* Re: [Qemu-devel] [PATCH v3 27/50] target/ppc: fetch code with translator_ld
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 27/50] target/ppc: fetch code with translator_ld Alex Bennée
@ 2019-06-17 22:30   ` Richard Henderson
  2019-06-19  9:39   ` David Gibson
  1 sibling, 0 replies; 117+ messages in thread
From: Richard Henderson @ 2019-06-17 22:30 UTC (permalink / raw)
  To: Alex Bennée, qemu-devel
  Cc: Emilio G. Cota, open list:PowerPC TCG CPUs, David Gibson

On 6/14/19 10:11 AM, Alex Bennée wrote:
> -    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));
> +

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>


r~


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

* Re: [Qemu-devel] [PATCH v3 28/50] target/sh4: fetch code with translator_ld
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 28/50] target/sh4: " Alex Bennée
@ 2019-06-17 22:33   ` Richard Henderson
  0 siblings, 0 replies; 117+ messages in thread
From: Richard Henderson @ 2019-06-17 22:33 UTC (permalink / raw)
  To: Alex Bennée, qemu-devel; +Cc: Emilio G. Cota, Aurelien Jarno

On 6/14/19 10:11 AM, Alex Bennée wrote:
> From: "Emilio G. Cota" <cota@braap.org>
> 
> There is a small wrinkle with the gUSA instruction. The translator
> effectively treats a (known) gUSA sequence as a single instruction.
> For the purposes of the plugin we end up with a long multi-instruction
> qemu_plugin_insn.
> 
> If the known sequence isn't detected we shall never run this
> translation anyway.
> 
> Signed-off-by: Emilio G. Cota <cota@braap.org>
> Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
> ---
>  target/sh4/translate.c | 4 ++--
>  1 file changed, 2 insertions(+), 2 deletions(-)

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>


r~



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

* Re: [Qemu-devel] [PATCH v3 29/50] target/i386: fetch code with translator_ld
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 29/50] target/i386: " Alex Bennée
@ 2019-06-17 22:33   ` Richard Henderson
  0 siblings, 0 replies; 117+ messages in thread
From: Richard Henderson @ 2019-06-17 22:33 UTC (permalink / raw)
  To: Alex Bennée, qemu-devel
  Cc: Paolo Bonzini, Emilio G. Cota, Eduardo Habkost, Richard Henderson

On 6/14/19 10:11 AM, Alex Bennée wrote:
> From: "Emilio G. Cota" <cota@braap.org>
> 
> Signed-off-by: Emilio G. Cota <cota@braap.org>
> ---
>  target/i386/translate.c | 10 +++++-----
>  1 file changed, 5 insertions(+), 5 deletions(-)

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>


r~



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

* Re: [Qemu-devel] [PATCH v3 30/50] target/hppa: fetch code with translator_ld
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 30/50] target/hppa: " Alex Bennée
@ 2019-06-17 22:34   ` Richard Henderson
  0 siblings, 0 replies; 117+ messages in thread
From: Richard Henderson @ 2019-06-17 22:34 UTC (permalink / raw)
  To: Alex Bennée, qemu-devel; +Cc: Emilio G. Cota, Richard Henderson

On 6/14/19 10:11 AM, Alex Bennée wrote:
> From: "Emilio G. Cota" <cota@braap.org>
> 
> Signed-off-by: Emilio G. Cota <cota@braap.org>
> ---
>  target/hppa/translate.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>


r~



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

* Re: [Qemu-devel] [PATCH v3 31/50] target/m68k: fetch code with translator_ld
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 31/50] target/m68k: " Alex Bennée
@ 2019-06-17 22:35   ` Richard Henderson
  0 siblings, 0 replies; 117+ messages in thread
From: Richard Henderson @ 2019-06-17 22:35 UTC (permalink / raw)
  To: Alex Bennée, qemu-devel; +Cc: Emilio G. Cota, Laurent Vivier

On 6/14/19 10:11 AM, Alex Bennée wrote:
> From: "Emilio G. Cota" <cota@braap.org>
> 
> Signed-off-by: Emilio G. Cota <cota@braap.org>
> ---
>  target/m68k/translate.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>


r~



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

* Re: [Qemu-devel] [PATCH v3 32/50] target/alpha: fetch code with translator_ld
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 32/50] target/alpha: " Alex Bennée
@ 2019-06-17 22:35   ` Richard Henderson
  0 siblings, 0 replies; 117+ messages in thread
From: Richard Henderson @ 2019-06-17 22:35 UTC (permalink / raw)
  To: Alex Bennée, qemu-devel; +Cc: Emilio G. Cota, Richard Henderson

On 6/14/19 10:11 AM, Alex Bennée wrote:
> From: "Emilio G. Cota" <cota@braap.org>
> 
> Signed-off-by: Emilio G. Cota <cota@braap.org>
> ---
>  target/alpha/translate.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>


r~



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

* Re: [Qemu-devel] [PATCH v3 33/50] target/riscv: fetch code with translator_ld
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 33/50] target/riscv: " Alex Bennée
@ 2019-06-17 22:38   ` Richard Henderson
  2019-06-19 10:49     ` Palmer Dabbelt
  0 siblings, 1 reply; 117+ messages in thread
From: Richard Henderson @ 2019-06-17 22:38 UTC (permalink / raw)
  To: Alex Bennée, qemu-devel
  Cc: open list:RISC-V TCG CPUs, Sagar Karandikar, Bastian Koppelmann,
	Palmer Dabbelt, Emilio G. Cota, Alistair Francis

On 6/14/19 10:11 AM, Alex Bennée wrote:
> +++ b/target/riscv/translate.c
> @@ -793,7 +793,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);

I'll note for the riscv folks that this is an existing bug, reading too much in
the case of an RVC instruction.  This could well matter for the last 2-byte
instruction at the end of a page.

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>


r~


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

* Re: [Qemu-devel] [PATCH v3 34/50] target/sparc: fetch code with translator_ld
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 34/50] target/sparc: " Alex Bennée
@ 2019-06-17 22:39   ` Richard Henderson
  0 siblings, 0 replies; 117+ messages in thread
From: Richard Henderson @ 2019-06-17 22:39 UTC (permalink / raw)
  To: Alex Bennée, qemu-devel
  Cc: Emilio G. Cota, Mark Cave-Ayland, Artyom Tarasenko

On 6/14/19 10:11 AM, Alex Bennée wrote:
> From: "Emilio G. Cota" <cota@braap.org>
> 
> Signed-off-by: Emilio G. Cota <cota@braap.org>
> ---
>  target/sparc/translate.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>


r~



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

* Re: [Qemu-devel] [PATCH v3 35/50] target/xtensa: fetch code with translator_ld
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 35/50] target/xtensa: " Alex Bennée
@ 2019-06-17 22:41   ` Richard Henderson
  0 siblings, 0 replies; 117+ messages in thread
From: Richard Henderson @ 2019-06-17 22:41 UTC (permalink / raw)
  To: Alex Bennée, qemu-devel; +Cc: Max Filippov, Emilio G. Cota

On 6/14/19 10:11 AM, Alex Bennée wrote:
> From: "Emilio G. Cota" <cota@braap.org>
> 
> Signed-off-by: Emilio G. Cota <cota@braap.org>
> ---
>  target/xtensa/translate.c | 4 ++--
>  1 file changed, 2 insertions(+), 2 deletions(-)

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>


r~



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

* Re: [Qemu-devel] [PATCH v3 36/50] target/openrisc: fetch code with translator_ld
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 36/50] target/openrisc: " Alex Bennée
@ 2019-06-17 22:41   ` Richard Henderson
  0 siblings, 0 replies; 117+ messages in thread
From: Richard Henderson @ 2019-06-17 22:41 UTC (permalink / raw)
  To: Alex Bennée, qemu-devel; +Cc: Stafford Horne, Emilio G. Cota

On 6/14/19 10:11 AM, Alex Bennée wrote:
> From: "Emilio G. Cota" <cota@braap.org>
> 
> Signed-off-by: Emilio G. Cota <cota@braap.org>
> ---
>  target/openrisc/translate.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>


r~



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

* Re: [Qemu-devel] [PATCH v3 37/50] translator: inject instrumentation from plugins
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 37/50] translator: inject instrumentation from plugins Alex Bennée
@ 2019-06-17 22:44   ` Richard Henderson
  2019-06-20 16:51     ` Alex Bennée
  2019-07-01 16:01     ` Alex Bennée
  0 siblings, 2 replies; 117+ messages in thread
From: Richard Henderson @ 2019-06-17 22:44 UTC (permalink / raw)
  To: Alex Bennée, qemu-devel
  Cc: Paolo Bonzini, Emilio G. Cota, Richard Henderson

On 6/14/19 10:11 AM, Alex Bennée wrote:
> @@ -95,6 +103,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) {

This will of course not be reachable if db->is_jmp == DISAS_NORETURN.
Do we want to not bother calling the plugin for this case?


r~


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

* Re: [Qemu-devel] [PATCH v3 39/50] vl: support -plugin option
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 39/50] vl: support -plugin option Alex Bennée
@ 2019-06-17 22:53   ` Richard Henderson
  0 siblings, 0 replies; 117+ messages in thread
From: Richard Henderson @ 2019-06-17 22:53 UTC (permalink / raw)
  To: Alex Bennée, qemu-devel
  Cc: Paolo Bonzini, Emilio G . Cota, Lluís Vilanova

On 6/14/19 10:11 AM, Alex Bennée wrote:
> 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>
> ---
>  qemu-options.hx | 17 +++++++++++++++++
>  vl.c            | 11 +++++++++++
>  2 files changed, 28 insertions(+)

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>


r~


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

* Re: [Qemu-devel] [PATCH v3 40/50] linux-user: support -plugin option
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 40/50] linux-user: " Alex Bennée
@ 2019-06-17 22:54   ` Richard Henderson
  0 siblings, 0 replies; 117+ messages in thread
From: Richard Henderson @ 2019-06-17 22:54 UTC (permalink / raw)
  To: Alex Bennée, qemu-devel
  Cc: Laurent Vivier, Riku Voipio, Emilio G . Cota, Lluís Vilanova

On 6/14/19 10:11 AM, Alex Bennée wrote:
> 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(+)

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>


r~



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

* Re: [Qemu-devel] [PATCH v3 44/50] plugin: add qemu_plugin_insn_disas helper
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 44/50] plugin: add qemu_plugin_insn_disas helper Alex Bennée
@ 2019-06-17 23:09   ` Richard Henderson
  0 siblings, 0 replies; 117+ messages in thread
From: Richard Henderson @ 2019-06-17 23:09 UTC (permalink / raw)
  To: Alex Bennée, qemu-devel

On 6/14/19 10:11 AM, Alex Bennée wrote:
> Give the plugins access to the QEMU dissasembler so they don't have to
> re-invent the wheel.
> 
> Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
> ---
>  disas.c                      | 103 +++++++++++++++++++++++++++++++++++
>  include/disas/disas.h        |   2 +
>  include/qemu/qemu-plugin.h   |   9 +++
>  plugins/api.c                |   7 +++
>  plugins/qemu-plugins.symbols |   1 +
>  5 files changed, 122 insertions(+)

There are a couple of checkpatch errors, otherwise
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>


r~



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

* Re: [Qemu-devel] [PATCH v3 47/50] accel/stubs: reduce headers from tcg-stub
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 47/50] accel/stubs: reduce headers from tcg-stub Alex Bennée
@ 2019-06-17 23:22   ` Richard Henderson
  0 siblings, 0 replies; 117+ messages in thread
From: Richard Henderson @ 2019-06-17 23:22 UTC (permalink / raw)
  To: Alex Bennée, qemu-devel; +Cc: Paolo Bonzini, Richard Henderson

On 6/14/19 10:11 AM, Alex Bennée wrote:
> We don't need much for these. However I do wonder why these aren't
> just null inlines in exec-all.h
> 
> Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
> ---
>  accel/stubs/tcg-stub.c | 3 ---
>  1 file changed, 3 deletions(-)

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>


r~



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

* Re: [Qemu-devel] [PATCH v3 49/50] include/exec/cpu-defs.h: fix typo
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 49/50] include/exec/cpu-defs.h: fix typo Alex Bennée
@ 2019-06-17 23:23   ` Richard Henderson
  0 siblings, 0 replies; 117+ messages in thread
From: Richard Henderson @ 2019-06-17 23:23 UTC (permalink / raw)
  To: Alex Bennée, qemu-devel; +Cc: Paolo Bonzini, Richard Henderson

On 6/14/19 10:11 AM, Alex Bennée wrote:
> - * This structure must be placed in ArchCPU immedately
> + * This structure must be placed in ArchCPU immediately

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>


r~



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

* Re: [Qemu-devel] [PATCH v3 01/50] trace: expand mem_info:size_shift to 3 bits
  2019-06-17  8:22     ` Alex Bennée
@ 2019-06-17 23:29       ` Richard Henderson
  0 siblings, 0 replies; 117+ messages in thread
From: Richard Henderson @ 2019-06-17 23:29 UTC (permalink / raw)
  To: Alex Bennée; +Cc: Emilio G. Cota, qemu-devel, Stefan Hajnoczi

On 6/17/19 1:22 AM, Alex Bennée wrote:
> 
> Richard Henderson <richard.henderson@linaro.org> writes:
> 
>> On 6/14/19 10:11 AM, Alex Bennée wrote:
>>> From: "Emilio G. Cota" <cota@braap.org>
>>>
>>> This will allow us to trace 16B-long memory accesses.
>>>
>>> Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
>>> 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 844ee58dd9..037169aab3 100644
>>> --- a/trace-events
>>> +++ b/trace-events
>>> @@ -159,7 +159,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 */
>>>
>>
>> Well, 128B-long memory accesses.  But SVE supports 256B memory accesses
>> already.  So why not add one more bit now.
> 
> Good point.
> 
> Do we have any architectures that do load/stores that are not power of
> 2? I guess the SVE non-faulting accesses are treated as a series of elem
> size accesses.

Yes, non-faults are in addition predicated, so each element is considered
individually.

Even the SVE non-predicated load/stores can technically be considered a
sequence of byte operations.  Which, I suppose could be helpful, because SVE
can otherwise be configured to do non-power-of-2 operations.


r~


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

* Re: [Qemu-devel] [PATCH v3 05/50] docs/devel: add plugins.rst design document
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 05/50] docs/devel: add plugins.rst design document Alex Bennée
@ 2019-06-19  3:34   ` Pranith Kumar
  2019-06-20 13:38     ` Alex Bennée
  0 siblings, 1 reply; 117+ messages in thread
From: Pranith Kumar @ 2019-06-19  3:34 UTC (permalink / raw)
  To: Alex Bennée; +Cc: qemu-devel

Hi,

On Fri, Jun 14, 2019 at 10:21 AM Alex Bennée <alex.bennee@linaro.org> wrote:
>
> This is mostly extracted from Emilio's more verbose commit comments
> with some additional verbiage from me.
>
> Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
> ---
>  docs/devel/index.rst   |  1 +
>  docs/devel/plugins.rst | 99 ++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 100 insertions(+)
>  create mode 100644 docs/devel/plugins.rst
>
> diff --git a/docs/devel/index.rst b/docs/devel/index.rst
> index 2a4ddf40ad..7e6d20c970 100644
> --- a/docs/devel/index.rst
> +++ b/docs/devel/index.rst
> @@ -21,3 +21,4 @@ Contents:
>     testing
>     decodetree
>     secure-coding-practices
> +   plugins
> diff --git a/docs/devel/plugins.rst b/docs/devel/plugins.rst
> new file mode 100644
> index 0000000000..b0c30375ef
> --- /dev/null
> +++ b/docs/devel/plugins.rst
> @@ -0,0 +1,99 @@
> +..
> +   Copyright (C) 2017, Emilio G. Cota <cota@braap.org>
> +   Copyright (c) 2019, Linaro Limited
> +   Written by Emilio Cota and Alex Bennée
> +
> +================
> +QEMU TCG Plugins
> +================
> +
> +QEMU TCG plugins provide a way for users to run experiments taking
> +advantage of the total system control emulation can have over a guest.
> +It provides a mechanism for plugins to subscribe to events during
> +translation and execution and optionally callback into the plugin
> +during these events.
> +
> +API Stability
> +=============
> +
> +This is a new feature for QEMU and it does allow people to develop
> +out-of-tree plugins than can be dynamically linked into a running QEMU

s/than/that/

> +process. However the project reserves the right to change or break the
> +API should it need to do so.
> +
> +Exposure of QEMU internals
> +--------------------------
> +
> +The plugin architecture actively avoids leaking implementation details
> +about how QEMU's translation works to the plugins. While there are
> +conceptions such as translation time and translation blocks the
> +details are opaque to plugins. The plugin is able to query select
> +details of instructions and system configuration only through the
> +exported *qemu_plugin* functions. The types used to describe
> +instructions and events are opaque to the plugins themselves.
> +
> +Usage
> +=====
> +
> +The QEMU binary needs to be compiled for plugin support:
> +
> +::
> +    configure --enable-plugins
> +
> +Once built a program can be run with multiple plugins loaded each with
> +their own arguments:
> +
> +::
> +    $QEMU $OTHER_QEMU_ARGS \
> +      -plugin tests/plugin/libhowvec.so,arg=inline,arg=hint \
> +      -plugin tests/plugin/libhotblocks.so

I think this might be a good place to describe what these arguments are.

> +
> +Plugin Life cycle
> +=================
> +
> +First the plugin is loaded and the public qemu_plugin_install function
> +is called. The plugin with then register callbacks for various plugin

s/with/will/

> +events. Generally at least the atexit_cb is registered so the plugin
> +can dump its information at the end of a run.

Is that a hard requirement?

> +
> +When a registered event occurs the plugin callback is called. The

I would prefer 'callback is invoked'.

> +callbacks may provide additional information. In the case of a
> +translation event the plugin has an option to enumerate the
> +instructions in a block of instructions and optionally register
> +callbacks to some or all instructions when they are executed.
> +
> +There is also a facility to add an inline event where code to
> +increment a counter can be directly inlined with the translation.
> +Currently only a simple increment is supported. This is not atomic so
> +the plugin must either keep it's counters separated and indexed by CPU
> +or use a callback which can ensure atomicity.
> +
> +Finally when QEMU exits all the registered atexit callbacks are called

Add period at end of sentence and preferably "s/called/invoked/"

> +
> +Internals
> +=========
> +
> +Locking
> +-------
> +
> +We have to ensure we cannot deadlock, particularly under MTTCG. For
> +this we acquire a lock when called from plugin code. We also keep the
> +list of callbacks under RCU 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.
> +
> +As a result 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.
> +
> +We support the uninstallation of a plugin at any time (e.g. from plugin
> +callbacks). This means some callbacks might still be called after the uninstall
> +function returns. The plugin isn't completely uninstalled until the
> +safe work has executed while all vCPUs are quiescent.

Isn't this when the atexit callback is invoked? Might add that to make
it clearer.


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

* Re: [Qemu-devel] [PATCH v3 07/50] plugin: add user-facing API
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 07/50] plugin: add user-facing API Alex Bennée
@ 2019-06-19  3:34   ` Pranith Kumar
  2019-06-19 11:32     ` Alex Bennée
  0 siblings, 1 reply; 117+ messages in thread
From: Pranith Kumar @ 2019-06-19  3:34 UTC (permalink / raw)
  To: Alex Bennée; +Cc: Emilio G. Cota, qemu-devel

On Fri, Jun 14, 2019 at 10:24 AM Alex Bennée <alex.bennee@linaro.org> wrote:
>
> From: "Emilio G. Cota" <cota@braap.org>
>
> Add the API first to ease review.
>
> Signed-off-by: Emilio G. Cota <cota@braap.org>
> Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
>
> ---
> v3
>   - merge in changes to plugin install/reset/uninstall
>   - split api file
> ---
>  include/qemu/qemu-plugin.h | 339 +++++++++++++++++++++++++++++++++++++
>  1 file changed, 339 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..0db1ef9714
> --- /dev/null
> +++ b/include/qemu/qemu-plugin.h
> @@ -0,0 +1,339 @@
> +/*
> + * Copyright (C) 2017, Emilio G. Cota <cota@braap.org>
> + * Copyright (C) 2019, Linaro
> + *
> + * License: GNU GPL, version 2 or later.
> + *   See the COPYING file in the top-level directory.
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +#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: Calling qemu_plugin_uninstall() from this function is a bug. To raise
> + * an error during install, return !0.
> + *
> + * Note: @argv remains valid throughout the lifetime of the loaded plugin.
> + */
> +QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, int argc,
> +                                           char **argv);
> +
> +/*
> + * Prototypes for the various callback styles we will be registering
> + * in the following functions.
> + */
> +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_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 receives callbacks until @cb is called.
> + *
> + * Note: Calling this function from qemu_plugin_install() is a bug.
> + */
> +void qemu_plugin_uninstall(qemu_plugin_id_t id, qemu_plugin_simple_cb_t cb);
> +
> +/**
> + * qemu_plugin_reset() - Reset a plugin
> + * @id: this plugin's opaque ID
> + * @cb: callback to be called once the plugin has been reset
> + *
> + * Unregisters all callbacks for the plugin given by @id.
> + *
> + * Do NOT assume that the plugin has been reset once this function returns.
> + * Plugins are reset asynchronously, and therefore the given plugin receives
> + * callbacks until @cb is called.
> + */
> +void qemu_plugin_reset(qemu_plugin_id_t id, qemu_plugin_simple_cb_t cb);
> +
> +/**
> + * 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);
> +
> +/**
> + * qemu_plugin_register_vcpu_idle_cb() - register a vCPU idle callback
> + * @id: plugin ID
> + * @cb: callback function
> + *
> + * The @cb function is called every time a vCPU idles.
> + */
> +void qemu_plugin_register_vcpu_idle_cb(qemu_plugin_id_t id,
> +                                       qemu_plugin_vcpu_simple_cb_t cb);
> +
> +/**
> + * qemu_plugin_register_vcpu_resume_cb() - register a vCPU resume callback
> + * @id: plugin ID
> + * @cb: callback function
> + *
> + * The @cb function is called every time a vCPU resumes execution.
> + */
> +void qemu_plugin_register_vcpu_resume_cb(qemu_plugin_id_t id,
> +                                         qemu_plugin_vcpu_simple_cb_t cb);
> +
> +/*
> + * Opaque types that the plugin is given during the translation and
> + * instrumentation phase.
> + */
> +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,
> +};

Why is this structure different from qemu_plugin_cb_flags? I think
both of them could use a similar structure. Both of them can have
(_NO, _R, _W, _RW) I think.

> +
> +/**
> + * qemu_plugin_register_vcpu_tb_trans_cb() - register a translate cb
> + * @id: plugin ID
> + * @cb: callback function
> + *
> + * The @cb function is called every time a translation occurs. The @cb
> + * function is passed an opaque qemu_plugin_type which is can query

s/is/it/


> + * for additional information including the list of translated
> + * instructions. At this point the plugin can register further
> + * callbacks to be triggered when the block or individual instruction
> + * executes.
> + */
> +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);
> +
> +/**
> + * qemu_plugin_register_vcpu_tb_trans_exec_cb() - register execution callback
> + * @tb: the opaque qemu_plugin_tb handle for the translation
> + * @cb: callback function
> + * @flags: does the plugin read or write the CPU's registers?
> + * @userdata: any plugin data to pass to the @cb?
> + *
> + * The @cb function is called every time a translated unit executes.
> + */
> +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,
> +};
> +
> +/**
> + * qemu_plugin_register_vcpu_tb_trans_exec_inline() - execution inline op
> + * @tb: the opaque qemu_plugin_tb handle for the translation
> + * @op: the type of qemu_plugin_op (e.g. ADD_U64)
> + * @ptr: the target memory location for the op
> + * @imm: the op data (e.g. 1)
> + *
> + * Insert an inline op to every time a translated unit executes.
> + * Useful if you just want to increment a single counter somewhere in
> + * memory.
> + */
> +void qemu_plugin_register_vcpu_tb_exec_inline(struct qemu_plugin_tb *tb,
> +                                              enum qemu_plugin_op op,
> +                                              void *ptr, uint64_t imm);
> +
> +/**
> + * qemu_plugin_register_vcpu_insn_exec_cb() - register insn execution cb
> + * @insn: the opaque qemu_plugin_insn handle for an instruction
> + * @cb: callback function
> + * @flags: does the plugin read or write the CPU's registers?
> + * @userdata: any plugin data to pass to the @cb?
> + *
> + * The @cb function is called every time an instruction is executed
> + */
> +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);
> +
> +/**
> + * qemu_plugin_register_vcpu_insn_exec_inline() - insn execution inline op
> + * @insn: the opaque qemu_plugin_insn handle for an instruction
> + * @cb: callback function
> + * @op: the type of qemu_plugin_op (e.g. ADD_U64)
> + * @ptr: the target memory location for the op
> + * @imm: the op data (e.g. 1)
> + *
> + * Insert an inline op to every time an instruction executes. Useful
> + * if you just want to increment a single counter somewhere in memory.
> + */
> +void qemu_plugin_register_vcpu_insn_exec_inline(struct qemu_plugin_insn *insn,
> +                                                enum qemu_plugin_op op,
> +                                                void *ptr, uint64_t imm);
> +
> +/*
> + * Helpers to query information about the instructions in a block
> + */
> +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);
> +
> +/*
> + * Memory Instrumentation
> + */
> +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);
> +
> +
> +/**
> + * 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.20.1
>
>


--
Pranith


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

* Re: [Qemu-devel] [PATCH v3 22/50] *-user: plugin syscalls
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 22/50] *-user: plugin syscalls Alex Bennée
  2019-06-17 20:56   ` Richard Henderson
@ 2019-06-19  3:35   ` Pranith Kumar
  2019-07-01 14:20     ` Alex Bennée
  1 sibling, 1 reply; 117+ messages in thread
From: Pranith Kumar @ 2019-06-19  3:35 UTC (permalink / raw)
  To: Alex Bennée; +Cc: Riku Voipio, Emilio G. Cota, qemu-devel, Laurent Vivier

Minor nits.

On Fri, Jun 14, 2019 at 11:41 AM Alex Bennée <alex.bennee@linaro.org> wrote:
>
> From: "Emilio G. Cota" <cota@braap.org>
>
> 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 84a983a9a1..50e47d217c 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);

Looking at the previous line, seems like you can avoid splitting this
line into 2. Keeps it more consistent that way.

>      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);

ditto.

>      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);

ditto.

>      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 b187c1281d..7f3cfdee84 100644
> --- a/linux-user/syscall.c
> +++ b/linux-user/syscall.c
> @@ -11724,6 +11724,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);

This I am not sure.


>
>      if (unlikely(do_strace)) {
>          print_syscall(num, arg1, arg2, arg3, arg4, arg5, arg6);
> @@ -11736,5 +11738,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.20.1
>
>


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

* Re: [Qemu-devel] [PATCH v3 27/50] target/ppc: fetch code with translator_ld
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 27/50] target/ppc: fetch code with translator_ld Alex Bennée
  2019-06-17 22:30   ` Richard Henderson
@ 2019-06-19  9:39   ` David Gibson
  1 sibling, 0 replies; 117+ messages in thread
From: David Gibson @ 2019-06-19  9:39 UTC (permalink / raw)
  To: Alex Bennée; +Cc: Emilio G. Cota, open list:PowerPC TCG CPUs, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 1452 bytes --]

On Fri, Jun 14, 2019 at 06:11:37PM +0100, Alex Bennée wrote:
> From: "Emilio G. Cota" <cota@braap.org>
> 
> Signed-off-by: Emilio G. Cota <cota@braap.org>

Acked-by: David Gibson <david@gibson.dropbear.id.au>

> ---
>  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 4a5de28036..a27b5659f4 100644
> --- a/target/ppc/translate.c
> +++ b/target/ppc/translate.c
> @@ -7864,11 +7864,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),

-- 
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

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

* Re: [Qemu-devel] [PATCH v3 33/50] target/riscv: fetch code with translator_ld
  2019-06-17 22:38   ` Richard Henderson
@ 2019-06-19 10:49     ` Palmer Dabbelt
  2019-09-27 21:47       ` Alistair Francis
  0 siblings, 1 reply; 117+ messages in thread
From: Palmer Dabbelt @ 2019-06-19 10:49 UTC (permalink / raw)
  To: richard.henderson
  Cc: qemu-riscv, sagark, Bastian Koppelmann, qemu-devel, cota,
	Alistair Francis, alex.bennee

On Mon, 17 Jun 2019 15:38:45 PDT (-0700), richard.henderson@linaro.org wrote:
> On 6/14/19 10:11 AM, Alex Bennée wrote:
>> +++ b/target/riscv/translate.c
>> @@ -793,7 +793,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);
>
> I'll note for the riscv folks that this is an existing bug, reading too much in
> the case of an RVC instruction.  This could well matter for the last 2-byte
> instruction at the end of a page.
>
> Reviewed-by: Richard Henderson <richard.henderson@linaro.org>

Thanks for pointing this out.  I'm checking the ISA semantics with Andrew to
make sure I've got it right, as there's some implicit wording in the document
that doesn't quite do what I'd expect it to.


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

* Re: [Qemu-devel] [PATCH v3 07/50] plugin: add user-facing API
  2019-06-19  3:34   ` Pranith Kumar
@ 2019-06-19 11:32     ` Alex Bennée
  0 siblings, 0 replies; 117+ messages in thread
From: Alex Bennée @ 2019-06-19 11:32 UTC (permalink / raw)
  To: Pranith Kumar; +Cc: Emilio G. Cota, qemu-devel


Pranith Kumar <pranith.foss@gmail.com> writes:

> On Fri, Jun 14, 2019 at 10:24 AM Alex Bennée <alex.bennee@linaro.org> wrote:
>>
>> From: "Emilio G. Cota" <cota@braap.org>
>>
>> Add the API first to ease review.
>>
>> Signed-off-by: Emilio G. Cota <cota@braap.org>
>> Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
>>
>> ---
>> v3
>>   - merge in changes to plugin install/reset/uninstall
>>   - split api file
>> ---
>>  include/qemu/qemu-plugin.h | 339 +++++++++++++++++++++++++++++++++++++
>>  1 file changed, 339 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..0db1ef9714
>> --- /dev/null
>> +++ b/include/qemu/qemu-plugin.h
>> @@ -0,0 +1,339 @@
>> +/*
>> + * Copyright (C) 2017, Emilio G. Cota <cota@braap.org>
>> + * Copyright (C) 2019, Linaro
>> + *
>> + * License: GNU GPL, version 2 or later.
>> + *   See the COPYING file in the top-level directory.
>> + *
>> + * SPDX-License-Identifier: GPL-2.0-or-later
>> + */
>> +#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: Calling qemu_plugin_uninstall() from this function is a bug. To raise
>> + * an error during install, return !0.
>> + *
>> + * Note: @argv remains valid throughout the lifetime of the loaded plugin.
>> + */
>> +QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, int argc,
>> +                                           char **argv);
>> +
>> +/*
>> + * Prototypes for the various callback styles we will be registering
>> + * in the following functions.
>> + */
>> +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_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 receives callbacks until @cb is called.
>> + *
>> + * Note: Calling this function from qemu_plugin_install() is a bug.
>> + */
>> +void qemu_plugin_uninstall(qemu_plugin_id_t id, qemu_plugin_simple_cb_t cb);
>> +
>> +/**
>> + * qemu_plugin_reset() - Reset a plugin
>> + * @id: this plugin's opaque ID
>> + * @cb: callback to be called once the plugin has been reset
>> + *
>> + * Unregisters all callbacks for the plugin given by @id.
>> + *
>> + * Do NOT assume that the plugin has been reset once this function returns.
>> + * Plugins are reset asynchronously, and therefore the given plugin receives
>> + * callbacks until @cb is called.
>> + */
>> +void qemu_plugin_reset(qemu_plugin_id_t id, qemu_plugin_simple_cb_t cb);
>> +
>> +/**
>> + * 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);
>> +
>> +/**
>> + * qemu_plugin_register_vcpu_idle_cb() - register a vCPU idle callback
>> + * @id: plugin ID
>> + * @cb: callback function
>> + *
>> + * The @cb function is called every time a vCPU idles.
>> + */
>> +void qemu_plugin_register_vcpu_idle_cb(qemu_plugin_id_t id,
>> +                                       qemu_plugin_vcpu_simple_cb_t cb);
>> +
>> +/**
>> + * qemu_plugin_register_vcpu_resume_cb() - register a vCPU resume callback
>> + * @id: plugin ID
>> + * @cb: callback function
>> + *
>> + * The @cb function is called every time a vCPU resumes execution.
>> + */
>> +void qemu_plugin_register_vcpu_resume_cb(qemu_plugin_id_t id,
>> +                                         qemu_plugin_vcpu_simple_cb_t cb);
>> +
>> +/*
>> + * Opaque types that the plugin is given during the translation and
>> + * instrumentation phase.
>> + */
>> +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,
>> +};
>
> Why is this structure different from qemu_plugin_cb_flags? I think
> both of them could use a similar structure. Both of them can have
> (_NO, _R, _W, _RW) I think.

They do refer to semantically different things. The plugin CB flags are
important for ensuring registers are synced or not before the callback.
The memory callbacks flags control where the callbacks are placed with
respect to the access.

>
>> +
>> +/**
>> + * qemu_plugin_register_vcpu_tb_trans_cb() - register a translate cb
>> + * @id: plugin ID
>> + * @cb: callback function
>> + *
>> + * The @cb function is called every time a translation occurs. The @cb
>> + * function is passed an opaque qemu_plugin_type which is can query
>
> s/is/it/

Fixed.

--
Alex Bennée


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

* Re: [Qemu-devel] [PATCH v3 08/50] plugin: add core code
       [not found]   ` <CADYwmhGiU_1GrBrR_tzBx+Lw+Hs3=Hi3AoPxRwkEj2pv9awqUg@mail.gmail.com>
@ 2019-06-19 11:46     ` Alex Bennée
  0 siblings, 0 replies; 117+ messages in thread
From: Alex Bennée @ 2019-06-19 11:46 UTC (permalink / raw)
  To: Pranith Kumar; +Cc: Emilio G. Cota, qemu-devel


Pranith Kumar <bobby.prani@gmail.com> writes:

> On Fri, Jun 14, 2019 at 10:30 AM Alex Bennée <alex.bennee@linaro.org> wrote:
>>
>> From: "Emilio G. Cota" <cota@braap.org>
>>
>> Signed-off-by: Emilio G. Cota <cota@braap.org>
>> [AJB: moved directory and merged various fixes]
>> Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
>>
>> ---
>> v3
>>   - moved to plugins/
>>   - merged
>>     plugin: remove uninstall_cb_t and just use simple_cb_t
>>     plugin: handle uninstall when !current_cpu
>>     plugin: introduce qemu_plugin_reset
>>   - split into core/loader
>>   - use GArray/GByteArray/GPtrArray instead of homegrown funcs
>> ---
>>  Makefile              |  10 +-
>>  Makefile.target       |   2 +
>>  include/qemu/plugin.h | 261 +++++++++++++++++++++
>>  include/qom/cpu.h     |   6 +
>>  plugins/Makefile.objs |   6 +
>>  plugins/core.c        | 524 ++++++++++++++++++++++++++++++++++++++++++
>>  plugins/loader.c      | 353 ++++++++++++++++++++++++++++
>>  plugins/plugin.h      |  95 ++++++++
>>  8 files changed, 1256 insertions(+), 1 deletion(-)
>>  create mode 100644 include/qemu/plugin.h
>>  create mode 100644 plugins/Makefile.objs
>>  create mode 100644 plugins/core.c
>>  create mode 100644 plugins/loader.c
>>  create mode 100644 plugins/plugin.h
>>
>> diff --git a/Makefile b/Makefile
>> index 8e2fc6624c..37d396ab88 100644
>> --- a/Makefile
>> +++ b/Makefile
>> @@ -827,8 +827,13 @@ endif
>>
>>  ICON_SIZES=16x16 24x24 32x32 48x48 64x64 128x128 256x256 512x512
>>
>> -install: all $(if $(BUILD_DOCS),install-doc) install-datadir install-localstatedir \
>> +install-includedir:
>> +       $(INSTALL_DIR) "$(DESTDIR)$(includedir)"
>> +
>> +install: all $(if $(BUILD_DOCS),install-doc) \
>> +       install-datadir install-localstatedir install-includedir \
>>         $(if $(INSTALL_BLOBS),$(edk2-decompressed))
>> +
>>  ifneq ($(TOOLS),)
>>         $(call install-prog,$(subst qemu-ga,qemu-ga$(EXESUF),$(TOOLS)),$(DESTDIR)$(bindir))
>>  endif
>> @@ -888,6 +893,9 @@ endif
>>                 "$(DESTDIR)/$(qemu_desktopdir)/qemu.desktop"
>>  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 ecd856e3a3..d492e24da2 100644
>> --- a/Makefile.target
>> +++ b/Makefile.target
>> @@ -118,6 +118,8 @@ obj-y += target/$(TARGET_BASE_ARCH)/
>>  obj-y += disas.o
>>  obj-$(call notempty,$(TARGET_XML_FILES)) += gdbstub-xml.o
>>
>> +obj-$(CONFIG_PLUGIN) += plugins/
>> +
>>  #########################################################
>>  # Linux user emulator target
>>
>> diff --git a/include/qemu/plugin.h b/include/qemu/plugin.h
>> new file mode 100644
>> index 0000000000..93e39f44d2
>> --- /dev/null
>> +++ b/include/qemu/plugin.h
>> @@ -0,0 +1,261 @@
>> +/*
>> + * 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;
>> +typedef QTAILQ_HEAD(, qemu_plugin_desc) QemuPluginList;
>> +
>> +#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, QemuPluginList *head);
>> +int qemu_plugin_load_list(QemuPluginList *head);
>> +#else /* !CONFIG_PLUGIN */
>> +static inline void qemu_plugin_add_opts(void)
>> +{ }
>> +
>> +static inline void qemu_plugin_opt_parse(const char *optarg,
>> +                                         QemuPluginList *head)
>> +{
>> +    error_report("plugin interface not enabled in this build");
>> +    exit(1);
>> +}
>> +
>> +static inline int qemu_plugin_load_list(QemuPluginList *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,
>> +};
>
> All the events but for MAX are descriptive. Care to add a comment for
> the last one?

Ok.

>
>> +
>> +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,
>> +};
>
> This might be better to have the types as powers of 2. It will allow
> us to define a plugin by comibining multiple types using OR.

To what end? It's not like you can have multiple prototypes to the call back.

>
>
>> +
>> +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_insn {
>> +    GByteArray *data;
>> +    uint64_t vaddr;
>> +    void *haddr;
>> +    GArray *cbs[PLUGIN_N_CB_TYPES][PLUGIN_N_CB_SUBTYPES];
>> +    bool calls_helpers;
>> +    bool mem_helper;
>> +};
>> +
>> +/*
>> + * qemu_plugin_insn allocate and cleanup functions. We don't expect to
>> + * cleanup many of these structures. They are reused for each fresh
>> + * translation.
>> + */
>> +
>> +static inline void qemu_plugin_insn_cleanup_fn(gpointer data)
>> +{
>> +    struct qemu_plugin_insn *insn = (struct qemu_plugin_insn *) data;
>> +    g_byte_array_free(insn->data, true);
>> +}
>> +
>> +static inline struct qemu_plugin_insn * qemu_plugin_insn_alloc(void)
>> +{
>> +    int i, j;
>> +    struct qemu_plugin_insn *insn = g_new0(struct qemu_plugin_insn, 1);
>> +    insn->data = g_byte_array_sized_new(4);
>> +
>> +    for (i = 0; i < PLUGIN_N_CB_TYPES; i++) {
>> +        for (j = 0; j < PLUGIN_N_CB_SUBTYPES; j++) {
>> +            insn->cbs[i][j] = g_array_new(false, false,
>> +                                          sizeof(struct qemu_plugin_dyn_cb));
>> +        }
>> +    }
>> +    return insn;
>> +}
>> +
>> +struct qemu_plugin_tb {
>> +    GPtrArray *insns;
>> +    size_t n;
>> +    uint64_t vaddr;
>> +    uint64_t vaddr2;
>> +    void *haddr1;
>> +    void *haddr2;
>> +    GArray *cbs[PLUGIN_N_CB_SUBTYPES];
>> +};
>> +
>> +/**
>> + * qemu_plugin_tb_insn_get(): get next plugin record for translation.
>> + *
>> + */
>> +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->insns->len)) {
>> +        struct qemu_plugin_insn *new_insn = qemu_plugin_insn_alloc();
>> +        g_ptr_array_add(tb->insns, new_insn);
>> +    }
>> +    insn = g_ptr_array_index(tb->insns, tb->n++);
>> +    g_byte_array_set_size(insn->data, 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++) {
>> +            g_array_set_size(insn->cbs[i][j], 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(GArray *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(GArray *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 08481ad304..27b67a4e49 100644
>> --- a/include/qom/cpu.h
>> +++ b/include/qom/cpu.h
>> @@ -29,6 +29,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);
>> @@ -345,6 +346,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.
>> @@ -430,6 +432,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);
>> +
>> +    GArray *plugin_mem_cbs;
>> +
>>      /* TODO Move common fields from CPUArchState here. */
>>      int cpu_index;
>>      int cluster_index;
>> diff --git a/plugins/Makefile.objs b/plugins/Makefile.objs
>> new file mode 100644
>> index 0000000000..58940335bc
>> --- /dev/null
>> +++ b/plugins/Makefile.objs
>> @@ -0,0 +1,6 @@
>> +#
>> +# Plugin Support
>> +#
>> +
>> +obj-y += loader.o
>> +obj-y += core.o
>> diff --git a/plugins/core.c b/plugins/core.c
>> new file mode 100644
>> index 0000000000..8c46373773
>> --- /dev/null
>> +++ b/plugins/core.c
>> @@ -0,0 +1,524 @@
>> +/* 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 */
>> +#include "plugin.h"
>> +
>> +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_state plugin;
>> +
>> +struct qemu_plugin_ctx *plugin_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);
>> +    }
>> +}
>> +
>> +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);
>> +    }
>> +}
>> +
>> +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 = plugin_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);
>> +}
>> +
>> +void plugin_register_cb(qemu_plugin_id_t id, enum qemu_plugin_event ev,
>> +                        void *func)
>> +{
>> +    do_plugin_register_cb(id, ev, func, NULL);
>> +}
>> +
>> +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_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 = plugin_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);
>> +}
>> +
>> +/* Allocate and return a callback record */
>> +static struct qemu_plugin_dyn_cb *plugin_get_dyn_cb(GArray **arr)
>> +{
>> +    GArray *cbs = *arr;
>> +
>> +    if (!cbs) {
>> +        cbs = g_array_sized_new(false, false, sizeof(struct qemu_plugin_dyn_cb), 1);
>> +        *arr = cbs;
>> +    }
>> +
>> +    g_array_set_size(cbs, cbs->len + 1);
>> +    return &g_array_index(cbs, struct qemu_plugin_dyn_cb, cbs->len - 1);
>> +}
>> +
>> +void plugin_register_inline_op(GArray **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;
>> +}
>> +
>> +inline void
>> +plugin_register_dyn_cb__udata(GArray **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 plugin_register_vcpu_mem_cb(GArray **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_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_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_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_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);
>> +}
>> +
>> +static bool free_dyn_cb_arr(void *p, uint32_t h, void *userp)
>> +{
>> +    g_array_free((GArray *) p, true);
>> +    return true;
>> +}
>> +
>> +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);
>> +}
>> +
>> +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)
>> +{
>> +    GArray *arr = cpu->plugin_mem_cbs;
>> +    size_t i;
>> +
>> +    if (arr == NULL) {
>> +        return;
>> +    }
>> +    for (i = 0; i < arr->len; i++) {
>> +        struct qemu_plugin_dyn_cb *cb =
>> +            &g_array_index(arr, struct qemu_plugin_dyn_cb, 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();
>> +        }
>> +    }
>> +}
>> +
>> +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 bool plugin_dyn_cb_arr_cmp(const void *ap, const void *bp)
>> +{
>> +    return ap == bp;
>> +}
>> +
>> +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);
>> +}
>> diff --git a/plugins/loader.c b/plugins/loader.c
>> new file mode 100644
>> index 0000000000..bdd875868f
>> --- /dev/null
>> +++ b/plugins/loader.c
>> @@ -0,0 +1,353 @@
>> +#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/qht.h"
>> +#include "qemu/bitmap.h"
>> +#include "qemu/xxhash.h"
>> +#include "qemu/plugin.h"
>> +#include "qom/cpu.h"
>> +#include <dlfcn.h>
>> +#include "cpu.h"
>> +#include "exec/exec-all.h"
>> +
>> +#include "plugin.h"
>> +
>> +/*
>> + * 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 {
>> +    QemuPluginList *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 **);
>> +
>> +extern struct qemu_plugin_state plugin;
>> +
>> +void qemu_plugin_add_dyn_cb_arr(GArray *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 struct qemu_plugin_desc *plugin_find_desc(QemuPluginList *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, QemuPluginList *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);
>> +    ctx->installing = true;
>> +    rc = install(ctx->id, desc->argc, desc->argv);
>> +    ctx->installing = false;
>> +    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 yet call it.
>> +         */
>> +        /* if (!ctx->uninstalling) { */
>> +        /*     qemu_plugin_uninstall(ctx->id, NULL); */
>> +        /* } */
>> +    }
>> +
>> +    qemu_rec_mutex_unlock(&plugin.lock);
>> +    return rc;
>> +
>> + 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(QemuPluginList *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;
>> +}
>> +
>> +struct qemu_plugin_reset_data {
>> +    struct qemu_plugin_ctx *ctx;
>> +    qemu_plugin_simple_cb_t cb;
>> +    bool reset;
>> +};
>> +
>> +static void plugin_reset_destroy__locked(struct qemu_plugin_reset_data *data)
>> +{
>> +    struct qemu_plugin_ctx *ctx = data->ctx;
>> +    enum qemu_plugin_event ev;
>> +    bool success;
>> +
>> +    /*
>> +     * After updating the subscription lists there is no need to wait for an RCU
>> +     * grace period to elapse, because right now we either are in a "safe async"
>> +     * work environment (i.e. all vCPUs are asleep), or no vCPUs have yet been
>> +     * created.
>> +     */
>> +    for (ev = 0; ev < QEMU_PLUGIN_EV_MAX; ev++) {
>> +        plugin_unregister_cb__locked(ctx, ev);
>> +    }
>> +
>> +    if (data->reset) {
>> +        g_assert(ctx->resetting);
>> +        if (data->cb) {
>> +            data->cb(ctx->id);
>> +        }
>> +        ctx->resetting = false;
>> +        g_free(data);
>> +        return;
>> +    }
>> +
>> +    g_assert(ctx->uninstalling);
>> +    /* we cannot dlclose if we are going to return to plugin code */
>> +    if (ctx->installing) {
>> +        error_report("Calling qemu_plugin_uninstall from the install function "
>> +                     "is a bug. Instead, return !0 from the install function.");
>> +        abort();
>> +    }
>> +
>> +    success = g_hash_table_remove(plugin.id_ht, &ctx->id);
>> +    g_assert(success);
>> +    QTAILQ_REMOVE(&plugin.ctxs, ctx, entry);
>> +    if (data->cb) {
>> +        data->cb(ctx->id);
>> +    }
>> +    if (dlclose(ctx->handle)) {
>> +        warn_report("%s: %s", __func__, dlerror());
>> +    }
>> +    plugin_desc_free(ctx->desc);
>> +    qemu_vfree(ctx);
>> +    g_free(data);
>> +}
>> +
>> +static void plugin_reset_destroy(struct qemu_plugin_reset_data *data)
>> +{
>> +    qemu_rec_mutex_lock(&plugin.lock);
>> +    plugin_reset_destroy__locked(data);
>> +    qemu_rec_mutex_lock(&plugin.lock);
>> +}
>> +
>> +static void plugin_flush_destroy(CPUState *cpu, run_on_cpu_data arg)
>> +{
>> +    struct qemu_plugin_reset_data *data = arg.host_ptr;
>> +
>> +    g_assert(cpu_in_exclusive_work_context(cpu));
>> +    tb_flush(cpu);
>> +    plugin_reset_destroy(data);
>> +}
>> +
>> +void plugin_reset_uninstall(qemu_plugin_id_t id,
>> +                            qemu_plugin_simple_cb_t cb,
>> +                            bool reset)
>> +{
>> +    struct qemu_plugin_reset_data *data;
>> +    struct qemu_plugin_ctx *ctx;
>> +
>> +    qemu_rec_mutex_lock(&plugin.lock);
>> +    ctx = plugin_id_to_ctx_locked(id);
>> +    if (ctx->uninstalling || (reset && ctx->resetting)) {
>> +        qemu_rec_mutex_unlock(&plugin.lock);
>> +        return;
>> +    }
>> +    ctx->resetting = reset;
>> +    ctx->uninstalling = !reset;
>> +    qemu_rec_mutex_unlock(&plugin.lock);
>> +
>> +    data = g_new(struct qemu_plugin_reset_data, 1);
>> +    data->ctx = ctx;
>> +    data->cb = cb;
>> +    data->reset = reset;
>> +    /*
>> +     * Only flush the code cache if the vCPUs have been created. If so,
>> +     * current_cpu must be non-NULL.
>> +     */
>> +    if (current_cpu) {
>> +        async_safe_run_on_cpu(current_cpu, plugin_flush_destroy,
>> +                              RUN_ON_CPU_HOST_PTR(data));
>> +    } else {
>> +        /*
>> +         * If current_cpu isn't set, then we don't have yet any vCPU threads
>> +         * and we therefore can remove the callbacks synchronously.
>> +         */
>> +        plugin_reset_destroy(data);
>> +    }
>> +}
>> diff --git a/plugins/plugin.h b/plugins/plugin.h
>> new file mode 100644
>> index 0000000000..e89f9ef0ea
>> --- /dev/null
>> +++ b/plugins/plugin.h
>> @@ -0,0 +1,95 @@
>> +/*
>> + * Plugin Shared Internal Functions
>> + *
>> + * Copyright (C) 2019, Linaro
>> + *
>> + * License: GNU GPL, version 2 or later.
>> + *   See the COPYING file in the top-level directory.
>> + *
>> + * SPDX-License-Identifier: GPL-2.0-or-later
>> + */
>> +
>> +#ifndef _PLUGIN_INTERNAL_H_
>> +#define _PLUGIN_INTERNAL_H_
>> +
>> +/* 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;
>> +};
>> +
>> +
>> +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;
>> +    /*
>> +     * keep a reference to @desc until uninstall, so that plugins do not have
>> +     * to strdup plugin args.
>> +     */
>> +    struct qemu_plugin_desc *desc;
>> +    bool installing;
>> +    bool uninstalling;
>> +    bool resetting;
>> +};
>> +
>> +struct qemu_plugin_ctx *plugin_id_to_ctx_locked(qemu_plugin_id_t id);
>> +
>> +void plugin_register_inline_op(GArray **arr,
>> +                               enum qemu_plugin_mem_rw rw,
>> +                               enum qemu_plugin_op op, void *ptr,
>> +                               uint64_t imm);
>> +
>> +void plugin_reset_uninstall(qemu_plugin_id_t id,
>> +                            qemu_plugin_simple_cb_t cb,
>> +                            bool reset);
>> +
>> +void plugin_register_cb(qemu_plugin_id_t id, enum qemu_plugin_event ev,
>> +                        void *func);
>> +
>> +void plugin_unregister_cb__locked(struct qemu_plugin_ctx *ctx,
>> +                                  enum qemu_plugin_event ev);
>> +
>> +void
>> +plugin_register_cb_udata(qemu_plugin_id_t id, enum qemu_plugin_event ev,
>> +                         void *func, void *udata);
>> +
>> +void
>> +plugin_register_dyn_cb__udata(GArray **arr,
>> +                              qemu_plugin_vcpu_udata_cb_t cb,
>> +                              enum qemu_plugin_cb_flags flags, void *udata);
>> +
>> +
>> +void plugin_register_vcpu_mem_cb(GArray **arr,
>> +                                 void *cb,
>> +                                 enum qemu_plugin_cb_flags flags,
>> +                                 enum qemu_plugin_mem_rw rw,
>> +                                 void *udata, bool haddr);
>> +
>> +void exec_inline_op(struct qemu_plugin_dyn_cb *cb);
>> +
>> +#endif /* _PLUGIN_INTERNAL_H_ */
>> --
>> 2.20.1
>>
>>


--
Alex Bennée


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

* Re: [Qemu-devel] [PATCH v3 03/50] cpu: introduce cpu_in_exclusive_work_context()
  2019-06-17  2:15   ` Richard Henderson
@ 2019-06-20  9:50     ` Alex Bennée
  0 siblings, 0 replies; 117+ messages in thread
From: Alex Bennée @ 2019-06-20  9:50 UTC (permalink / raw)
  To: Richard Henderson; +Cc: Emilio G. Cota, qemu-devel


Richard Henderson <richard.henderson@linaro.org> writes:

> On 6/14/19 10:11 AM, Alex Bennée wrote:
>>              start_exclusive();
>> +            cpu->in_exclusive_work_context = true;
>>              wi->func(cpu, wi->data);
>> +            cpu->in_exclusive_work_context = false;
>>              end_exclusive();
>
> Is there a reason not to put those into start/end_exclusive?

Not particularly... it can use current_cpu to find the cpu and set the
flag.

> And if not, what does in_exclusive_work_context mean?

Currently the check implies it's only for:

 exclusive work context, which has previously been queued via async_safe_run_on_cpu()

which avoids jumping through hoops if another async_safe tasks still
wants to flush the TB. However keeping it with start/end exclusive means
we could also clean up the code in:

  void cpu_exec_step_atomic(CPUState *cpu)
  {
    ..
    /* volatile because we modify it between setjmp and longjmp */
    volatile bool in_exclusive_region = false;
    ..
    if (sigsetjmp(cpu->jmp_env, 0) == 0) {
        start_exclusive();
        ..
    } else {
        ..
    }

    if (in_exclusive_region) {
        ..
        end_exclusive();

but the volatile makes me nervous. Is it only a risk that local
variable accesses might get optimised away?

>
>
> r~


--
Alex Bennée


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

* Re: [Qemu-devel] [PATCH v3 05/50] docs/devel: add plugins.rst design document
  2019-06-19  3:34   ` Pranith Kumar
@ 2019-06-20 13:38     ` Alex Bennée
  0 siblings, 0 replies; 117+ messages in thread
From: Alex Bennée @ 2019-06-20 13:38 UTC (permalink / raw)
  To: Pranith Kumar; +Cc: qemu-devel


Pranith Kumar <bobby.prani@gmail.com> writes:

<snip>
>> +
>> +Plugin Life cycle
>> +=================
>> +
>> +First the plugin is loaded and the public qemu_plugin_install function
>> +is called. The plugin with then register callbacks for various plugin
>
> s/with/will/
>
>> +events. Generally at least the atexit_cb is registered so the plugin
>> +can dump its information at the end of a run.
>
> Is that a hard requirement?

Not really although for a lot of plugins it is a natural point to dump
the results of the experiment.

<snip>
>> +
>> +As a result 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.
>> +
>> +We support the uninstallation of a plugin at any time (e.g. from plugin
>> +callbacks). This means some callbacks might still be called after the uninstall
>> +function returns. The plugin isn't completely uninstalled until the
>> +safe work has executed while all vCPUs are quiescent.
>
> Isn't this when the atexit callback is invoked? Might add that to make
> it clearer.

No we can uninstall at any time, I've amended to:

  We support the uninstallation of a plugin at any time (e.g. from
  plugin callbacks). This allows plugins to remove themselves if they no
  longer want to instrument the code. This operation is asynchronous
  which means callbacks may still occur after the uninstall operation is
  requested. The plugin isn't completely uninstalled until the safe work
  has executed while all vCPUs are quiescent.

--
Alex Bennée


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

* Re: [Qemu-devel] [PATCH v3 00/50] tcg plugin support
  2019-06-14 17:11 [Qemu-devel] [PATCH v3 00/50] tcg plugin support Alex Bennée
                   ` (52 preceding siblings ...)
  2019-06-14 19:48 ` no-reply
@ 2019-06-20 13:53 ` Pranith Kumar
  2019-06-21  8:21   ` Alex Bennée
  53 siblings, 1 reply; 117+ messages in thread
From: Pranith Kumar @ 2019-06-20 13:53 UTC (permalink / raw)
  To: Alex Bennée; +Cc: Emilio G. Cota, qemu-devel, rth

Hi Alex/Emilio,

I am really happy to see the progress you made on the plugin feature. Looking
forward to seeing it merged soon! Please CC me on future versions of the
patchset. I am happy to help review and contribute to this effort.

I have a few general comments from experience writing a very similar system
(qsim) below.

On Fri, Jun 14, 2019 at 10:23 AM Alex Bennée <alex.bennee@linaro.org> wrote:
>
> Hi,
>
> This is v3 of the proposed plugins API for QEMU. As Emilio is busy
> having finished his time at Columbia I have volunteered to take the
> patch series forward. Emilio's RFC v2 was posted last year:
>
>   Subject: [RFC v2 00/38] Plugin support
>   Date: Sun,  9 Dec 2018 14:37:11 -0500
>   Message-Id: <20181209193749.12277-1-cota@braap.org>
>

* Register and memory read/write API

  It would be great to have register and memory read/write API i.e., ability
  to read/write to registers/memory from within the callback. This gives the
  plugin ability to do system introspection. (Not sure if the current patchset
  implements this already).

* Register callbacks

  A callback needs to be invoked whenever a specified registers is read or
  written to.

* Where do new plugins live in the tree?

  The current source files in plugins (api, core etc.,) I think are better if
  moved to tcg/plugins/.  The various plugins we write would then live in the
  plugins/ folder instead of the current tests/plugin/ folder.

* Timer interrupts

  What I observed is that the system execution is affected by what you do in
  the callbacks because of timer interrupts. For example, if you spend time in
  the memory callback doing a bit of processing and writing to a file, you
  will see more timer interrupt instructions. One solution to this would be to
  use 'icount', but it does not work that well. I think we need to do
  something similar to what gdb does in debug mode. How would you handle MTTCG
  guests in that case?

  Another approach would be to offload callback generation to a separate
  plugin thread. The main thread will copy data required by a callback and
  invoke the callback asynchronously (std::async in C++ if you are familiar).

* Starting and stopping callback generation

  It would be great if we have a mechanism to dynamically start/stop callbacks
  when a sequence of code (magic instruction) is executed. This would be
  useful to annotate region-of-interest (ROI) in benchmarks to
generate callbacks.

  Also, the return value from a callback can be used to decide further course
  of action. For example, if our plugin needs 10000 callbacks, it can indicate
  to stop generating further callbacks in the return value of the callback
  once it got the necessary callbacks.

* State saving API

  An API to save the state of the VM from the plugin code.

Let me know your thoughts and any other ideas you might have.

Thanks,
--
Pranith


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

* Re: [Qemu-devel] [PATCH v3 37/50] translator: inject instrumentation from plugins
  2019-06-17 22:44   ` Richard Henderson
@ 2019-06-20 16:51     ` Alex Bennée
  2019-07-01 16:01     ` Alex Bennée
  1 sibling, 0 replies; 117+ messages in thread
From: Alex Bennée @ 2019-06-20 16:51 UTC (permalink / raw)
  To: Richard Henderson
  Cc: Paolo Bonzini, Emilio G. Cota, qemu-devel, Richard Henderson


Richard Henderson <richard.henderson@linaro.org> writes:

> On 6/14/19 10:11 AM, Alex Bennée wrote:
>> @@ -95,6 +103,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) {
>
> This will of course not be reachable if db->is_jmp == DISAS_NORETURN.
> Do we want to not bother calling the plugin for this case?

Hmm good point. Are you just suggesting:

  if (plugin_enabled && db->is_jmp != DISAS_NORETURN)

to be explicit?

>
>
> r~


--
Alex Bennée


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

* Re: [Qemu-devel] [PATCH v3 00/50] tcg plugin support
  2019-06-20 13:53 ` Pranith Kumar
@ 2019-06-21  8:21   ` Alex Bennée
  2019-06-21 17:36     ` Pranith Kumar
  0 siblings, 1 reply; 117+ messages in thread
From: Alex Bennée @ 2019-06-21  8:21 UTC (permalink / raw)
  To: Pranith Kumar; +Cc: Emilio G. Cota, qemu-devel, rth


Pranith Kumar <bobby.prani@gmail.com> writes:

> Hi Alex/Emilio,
>
> I am really happy to see the progress you made on the plugin feature. Looking
> forward to seeing it merged soon! Please CC me on future versions of the
> patchset. I am happy to help review and contribute to this effort.

Will do.

>
> I have a few general comments from experience writing a very similar system
> (qsim) below.
>
> On Fri, Jun 14, 2019 at 10:23 AM Alex Bennée <alex.bennee@linaro.org> wrote:
>>
>> Hi,
>>
>> This is v3 of the proposed plugins API for QEMU. As Emilio is busy
>> having finished his time at Columbia I have volunteered to take the
>> patch series forward. Emilio's RFC v2 was posted last year:
>>
>>   Subject: [RFC v2 00/38] Plugin support
>>   Date: Sun,  9 Dec 2018 14:37:11 -0500
>>   Message-Id: <20181209193749.12277-1-cota@braap.org>
>>
>
> * Register and memory read/write API
>
>   It would be great to have register and memory read/write API i.e., ability
>   to read/write to registers/memory from within the callback. This gives the
>   plugin ability to do system introspection. (Not sure if the current patchset
>   implements this already).

Not currently. The trick is to have something flexible enough without
exposing internals. I guess we could consider the gdb register
enumeration or maybe hook into stuff declared as
tcg_global_mem_new_i[64|32]. That won't get every "register" but it
should certainly cover the main general purpose ones. Things like SVE
and AdvSIMD vector registers wouldn't be seen though.

> * Register callbacks
>
>   A callback needs to be invoked whenever a specified registers is read or
>   written to.

Again tricky as not every register read/write is obvious from TCG -
vector registers tweaked from helpers would be a good example.

>
> * Where do new plugins live in the tree?
>
>   The current source files in plugins (api, core etc.,) I think are better if
>   moved to tcg/plugins/.  The various plugins we write would then live in the
>   plugins/ folder instead of the current tests/plugin/ folder.

The example plugins are really just toys for experimenting with the API
- I don't see too much problem with them being in tests. However the
howvec plugin is very guest architecture specific so we could consider a
bit more of a hierarchy. Maybe these should all live in tests/tcg?

>
> * Timer interrupts
>
>   What I observed is that the system execution is affected by what you do in
>   the callbacks because of timer interrupts. For example, if you spend time in
>   the memory callback doing a bit of processing and writing to a file, you
>   will see more timer interrupt instructions. One solution to this would be to
>   use 'icount', but it does not work that well. I think we need to do
>   something similar to what gdb does in debug mode. How would you handle MTTCG
>   guests in that case?

icount is going to be the best you can get for deterministic time -
other efforts to pause/restart virtual time going in and out of plugins
are just going to add a lot of overhead.

Remember QEMU doesn't even try to be a cycle accurate emulation so
expecting to get reasonable timing information out of these plugins is a
stretch. Maybe I should make that clearer in the design document?

The gdb behaviour is just a massive hack. When single-stepping in GDB we
prevent timer IRQs from being delivered - they have still fired and are
pending and will execute as soon as you hit continue.

>   Another approach would be to offload callback generation to a separate
>   plugin thread. The main thread will copy data required by a callback and
>   invoke the callback asynchronously (std::async in C++ if you are
>   familiar).

This would complicate things - the current iteration I'm working on
drops the haddr cb in favour of dynamically resolving the vaddr in the
callback. But that approach is only valid during the callback before
something else potentially pollutes the TLB.

>
> * Starting and stopping callback generation
>
>   It would be great if we have a mechanism to dynamically start/stop callbacks
>   when a sequence of code (magic instruction) is executed. This would be
>   useful to annotate region-of-interest (ROI) in benchmarks to
> generate callbacks.

Well we have that now. At each TB generation event the callback is free to register
as many or few callbacks as it likes dynamically.

>
>   Also, the return value from a callback can be used to decide further course
>   of action. For example, if our plugin needs 10000 callbacks, it can indicate
>   to stop generating further callbacks in the return value of the callback
>   once it got the necessary callbacks.

This can be done internally - a plugin can unregister itself once it has
collected everything it wanted. If it wants to hang around you can
qemu_plugin_reset to reset all callbacks.

Do you want something finer grained?

> * State saving API
>
>   An API to save the state of the VM from the plugin code.

This would be tricky for -smp > 1 because other threads will have run on
while you are making your decision. It would certainly be useful for
analyzing failed states though.

>
> Let me know your thoughts and any other ideas you might have.
>
> Thanks,


--
Alex Bennée


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

* Re: [Qemu-devel] [PATCH v3 00/50] tcg plugin support
  2019-06-21  8:21   ` Alex Bennée
@ 2019-06-21 17:36     ` Pranith Kumar
  2019-07-01 16:51       ` Alex Bennée
  0 siblings, 1 reply; 117+ messages in thread
From: Pranith Kumar @ 2019-06-21 17:36 UTC (permalink / raw)
  To: Alex Bennée; +Cc: Emilio G. Cota, qemu-devel, Richard Henderson

On Fri, Jun 21, 2019 at 1:21 AM Alex Bennée <alex.bennee@linaro.org> wrote:

> > * Register and memory read/write API
> >
> >   It would be great to have register and memory read/write API i.e., ability
> >   to read/write to registers/memory from within the callback. This gives the
> >   plugin ability to do system introspection. (Not sure if the current patchset
> >   implements this already).
>
> Not currently. The trick is to have something flexible enough without
> exposing internals. I guess we could consider the gdb register
> enumeration or maybe hook into stuff declared as
> tcg_global_mem_new_i[64|32]. That won't get every "register" but it
> should certainly cover the main general purpose ones. Things like SVE
> and AdvSIMD vector registers wouldn't be seen though.

I guess general registers could be a good starting point. We can then
implement arch specific register access APIs.

>
> > * Register callbacks
> >
> >   A callback needs to be invoked whenever a specified registers is read or
> >   written to.
>
> Again tricky as not every register read/write is obvious from TCG -
> vector registers tweaked from helpers would be a good example.
>
> >
> > * Where do new plugins live in the tree?
> >
> >   The current source files in plugins (api, core etc.,) I think are better if
> >   moved to tcg/plugins/.  The various plugins we write would then live in the
> >   plugins/ folder instead of the current tests/plugin/ folder.
>
> The example plugins are really just toys for experimenting with the API
> - I don't see too much problem with them being in tests. However the
> howvec plugin is very guest architecture specific so we could consider a
> bit more of a hierarchy. Maybe these should all live in tests/tcg?
>

So where do you want 'real' plugins to live in the tree? It would be
good to think about the structure for those.

> >
> > * Timer interrupts
> >
> >   What I observed is that the system execution is affected by what you do in
> >   the callbacks because of timer interrupts. For example, if you spend time in
> >   the memory callback doing a bit of processing and writing to a file, you
> >   will see more timer interrupt instructions. One solution to this would be to
> >   use 'icount', but it does not work that well. I think we need to do
> >   something similar to what gdb does in debug mode. How would you handle MTTCG
> >   guests in that case?
>
> icount is going to be the best you can get for deterministic time -
> other efforts to pause/restart virtual time going in and out of plugins
> are just going to add a lot of overhead.

I wonder why using icount is not working in this case. Are there any
timers that fire non-deterministically even when icount is used?

>
> Remember QEMU doesn't even try to be a cycle accurate emulation so
> expecting to get reasonable timing information out of these plugins is a
> stretch. Maybe I should make that clearer in the design document?

It is less about being cycle accurate and more about being
deterministic. For example, when tracing using plugins+callbacks, you
will see a lot more interrupt code in the trace than when if you
execute without tracing. How do we get them to be more similar?

Another idea would be to provide an API for the plugin to generate the
timer interrupt. This allows the plugin to generate regular interrupts
irrespective of what is being done in the callbacks.

>
> The gdb behaviour is just a massive hack. When single-stepping in GDB we
> prevent timer IRQs from being delivered - they have still fired and are
> pending and will execute as soon as you hit continue.
>
> >   Another approach would be to offload callback generation to a separate
> >   plugin thread. The main thread will copy data required by a callback and
> >   invoke the callback asynchronously (std::async in C++ if you are
> >   familiar).
>
> This would complicate things - the current iteration I'm working on
> drops the haddr cb in favour of dynamically resolving the vaddr in the
> callback. But that approach is only valid during the callback before
> something else potentially pollutes the TLB.
>

> >
> > * Starting and stopping callback generation
> >
> >   It would be great if we have a mechanism to dynamically start/stop callbacks
> >   when a sequence of code (magic instruction) is executed. This would be
> >   useful to annotate region-of-interest (ROI) in benchmarks to
> > generate callbacks.
>
> Well we have that now. At each TB generation event the callback is free to register
> as many or few callbacks as it likes dynamically.

But how does the plugin know that the TB being generated is the first
TB in the ROI?
Similarly the plugin needs to know the then end of ROI has been reached.

Also, please note that there can be multiple ROIs. It would be good to
know if we can assign ids to each ROI for the plugin.

Thanks,
-- 
Pranith


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

* Re: [Qemu-devel] [PATCH v3 19/50] tcg: let plugins instrument memory accesses
  2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 19/50] tcg: let plugins instrument memory accesses Alex Bennée
  2019-06-17 20:51   ` Richard Henderson
@ 2019-06-28 15:30   ` Aaron Lindsay OS via Qemu-devel
  2019-06-28 17:11     ` Alex Bennée
  1 sibling, 1 reply; 117+ messages in thread
From: Aaron Lindsay OS via Qemu-devel @ 2019-06-28 15:30 UTC (permalink / raw)
  To: Alex Bennée
  Cc: Paolo Bonzini, Emilio G. Cota, qemu-devel, Richard Henderson

On Jun 14 18:11, Alex Bennée wrote:
> From: "Emilio G. Cota" <cota@braap.org>
> 
> 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.

How much more difficult would it be to get the true physical address (on
the guest)?

This is important enough to me that I would be willing to help if
pointed in the right direction.

-Aaron


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

* Re: [Qemu-devel] [PATCH v3 19/50] tcg: let plugins instrument memory accesses
  2019-06-28 15:30   ` Aaron Lindsay OS via Qemu-devel
@ 2019-06-28 17:11     ` Alex Bennée
  2019-06-28 17:58       ` Aaron Lindsay OS via Qemu-devel
  0 siblings, 1 reply; 117+ messages in thread
From: Alex Bennée @ 2019-06-28 17:11 UTC (permalink / raw)
  To: Aaron Lindsay OS
  Cc: Paolo Bonzini, Emilio G. Cota, qemu-devel, Richard Henderson


Aaron Lindsay OS <aaron@os.amperecomputing.com> writes:

> On Jun 14 18:11, Alex Bennée wrote:
>> From: "Emilio G. Cota" <cota@braap.org>
>>
>> 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.
>
> How much more difficult would it be to get the true physical address (on
> the guest)?

Previously there was a helper that converted host address (i.e. where
QEMU actually stores that value) back to the physical address (ram
offset + ram base). However the code for calculating all of this is
pretty invasive and requires tweaks to all the softmmu TCG backends as
well as hooks into a slew of memory functions.

I'm re-working this now so we just have the one memory callback and we
provide a helper function that can provide an opaque hwaddr struct which
can then be queried. The catch is you can only call this helper during a
memory callback. I'm not sure if having this restriction violates our
aim of not leaking implementation details to the plugin but it makes the
code simpler.

Internally what the helper does is simply re-query the SoftMMU TLB. As
the TLBs are per-CPU nothing else can have touched the TLB and the cache
should be hot so the cost of lookup should be minor. We could also
potentially expand the helpers so if you are interested in only IO
accesses we can do the full resolution and figure out what device we
just accessed.

> This is important enough to me that I would be willing to help if
> pointed in the right direction.

Well I'll certainly CC on the next series (hopefully posted Monday,
softfreeze starts Tuesday). I'll welcome any testing and review. Also if
you can tell us more about your use case that will help.

>
> -Aaron


--
Alex Bennée


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

* Re: [Qemu-devel] [PATCH v3 19/50] tcg: let plugins instrument memory accesses
  2019-06-28 17:11     ` Alex Bennée
@ 2019-06-28 17:58       ` Aaron Lindsay OS via Qemu-devel
  2019-06-28 20:52         ` Alex Bennée
  0 siblings, 1 reply; 117+ messages in thread
From: Aaron Lindsay OS via Qemu-devel @ 2019-06-28 17:58 UTC (permalink / raw)
  To: Alex Bennée
  Cc: Paolo Bonzini, Emilio G. Cota, qemu-devel, Richard Henderson

On Jun 28 18:11, Alex Bennée wrote:
> Aaron Lindsay OS <aaron@os.amperecomputing.com> writes:
> > On Jun 14 18:11, Alex Bennée wrote:
> >> From: "Emilio G. Cota" <cota@braap.org>
> >>
> >> 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.
> >
> > How much more difficult would it be to get the true physical address (on
> > the guest)?
> 
> Previously there was a helper that converted host address (i.e. where
> QEMU actually stores that value) back to the physical address (ram
> offset + ram base). However the code for calculating all of this is
> pretty invasive and requires tweaks to all the softmmu TCG backends as
> well as hooks into a slew of memory functions.
> 
> I'm re-working this now so we just have the one memory callback and we
> provide a helper function that can provide an opaque hwaddr struct which
> can then be queried.

To make sure I understand - you're implying that one such query will
return the PA from the guest's perspective, right?

> The catch is you can only call this helper during a
> memory callback.

Does this mean it will be difficult to get the physical address for the
bytes containing the instruction encoding itself?

> I'm not sure if having this restriction violates our
> aim of not leaking implementation details to the plugin but it makes the
> code simpler.

Assuming that the purpose of "not leaking implementation details" is to
allow the same plugin interface to work with other backend
implementations in the future, isn't this probably fine? It may add an
unnecessary limitation for another backend driving the same plugin
interface, but I don't think it likely changes the structure of the
interface itself. And that seems like the sort of restriction that could
easily be dropped in the future while remaining backwards-compatible.

> Internally what the helper does is simply re-query the SoftMMU TLB. As
> the TLBs are per-CPU nothing else can have touched the TLB and the cache
> should be hot so the cost of lookup should be minor. We could also
> potentially expand the helpers so if you are interested in only IO
> accesses we can do the full resolution and figure out what device we
> just accessed.

Oh, so you're already working on doing just what I asked about?

> > This is important enough to me that I would be willing to help if
> > pointed in the right direction.
> 
> Well I'll certainly CC on the next series (hopefully posted Monday,
> softfreeze starts Tuesday). I'll welcome any testing and review. Also if
> you can tell us more about your use case that will help.

Awesome, thanks!

In terms of our use case - we use QEMU to drive studies to help us
design the next generation of processors. As you can imagine, having the
right physical addresses is important for some aspects of that. We're
currently using a version of Pavel Dovgalyuk's earlier plugin patchset
with some of our own patches/fixes on top, but it would obviously make
our lives easier to work together to get this sort of infrastructure
upstream!

-Aaron


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

* Re: [Qemu-devel] [PATCH v3 19/50] tcg: let plugins instrument memory accesses
  2019-06-28 17:58       ` Aaron Lindsay OS via Qemu-devel
@ 2019-06-28 20:52         ` Alex Bennée
  2019-07-01 14:40           ` Aaron Lindsay OS via Qemu-devel
  0 siblings, 1 reply; 117+ messages in thread
From: Alex Bennée @ 2019-06-28 20:52 UTC (permalink / raw)
  To: Aaron Lindsay OS
  Cc: Paolo Bonzini, Emilio G. Cota, qemu-devel, Richard Henderson


Aaron Lindsay OS <aaron@os.amperecomputing.com> writes:

> On Jun 28 18:11, Alex Bennée wrote:
>> Aaron Lindsay OS <aaron@os.amperecomputing.com> writes:
>> > On Jun 14 18:11, Alex Bennée wrote:
>> >> From: "Emilio G. Cota" <cota@braap.org>
>> >>
>> >> 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.
>> >
>> > How much more difficult would it be to get the true physical address (on
>> > the guest)?
>>
>> Previously there was a helper that converted host address (i.e. where
>> QEMU actually stores that value) back to the physical address (ram
>> offset + ram base). However the code for calculating all of this is
>> pretty invasive and requires tweaks to all the softmmu TCG backends as
>> well as hooks into a slew of memory functions.
>>
>> I'm re-working this now so we just have the one memory callback and we
>> provide a helper function that can provide an opaque hwaddr struct which
>> can then be queried.
>
> To make sure I understand - you're implying that one such query will
> return the PA from the guest's perspective, right?

Yes - although it will be two queries:

  struct qemu_plugin_hwaddr *hw = qemu_plugin_get_hwaddr(info, vaddr);

This does the actual lookup and stores enough information for the
further queries.

  uint64_t pa = qemu_plugin_hwaddr_to_raddr(hw);

will return the physical address (assuming it's a RAM reference and not
some IO location).

>
>> The catch is you can only call this helper during a
>> memory callback.
>
> Does this mean it will be difficult to get the physical address for the
> bytes containing the instruction encoding itself?

Hmm good question. We track the hostaddr of the instructions as we load
them so we should be able to track that back to the guest physical
address. There isn't a helper for doing that yet though.

>
>> I'm not sure if having this restriction violates our
>> aim of not leaking implementation details to the plugin but it makes the
>> code simpler.
>
> Assuming that the purpose of "not leaking implementation details" is to
> allow the same plugin interface to work with other backend
> implementations in the future, isn't this probably fine?

Quite. We don't want plugin authors to make any assumptions about the
internals of the TCG. It's not totally opaque because there are
translation time events where we offer the plugin a chance to instrument
individual instructions (or even a "block") which obviously exposes
there is a JIT of some sort.

> It may add an
> unnecessary limitation for another backend driving the same plugin
> interface, but I don't think it likely changes the structure of the
> interface itself. And that seems like the sort of restriction that could
> easily be dropped in the future while remaining backwards-compatible.
>
>> Internally what the helper does is simply re-query the SoftMMU TLB. As
>> the TLBs are per-CPU nothing else can have touched the TLB and the cache
>> should be hot so the cost of lookup should be minor. We could also
>> potentially expand the helpers so if you are interested in only IO
>> accesses we can do the full resolution and figure out what device we
>> just accessed.
>
> Oh, so you're already working on doing just what I asked about?

Yes.

>
>> > This is important enough to me that I would be willing to help if
>> > pointed in the right direction.
>>
>> Well I'll certainly CC on the next series (hopefully posted Monday,
>> softfreeze starts Tuesday). I'll welcome any testing and review. Also if
>> you can tell us more about your use case that will help.
>
> Awesome, thanks!
>
> In terms of our use case - we use QEMU to drive studies to help us
> design the next generation of processors. As you can imagine, having the
> right physical addresses is important for some aspects of that. We're
> currently using a version of Pavel Dovgalyuk's earlier plugin patchset
> with some of our own patches/fixes on top, but it would obviously make
> our lives easier to work together to get this sort of infrastructure
> upstream!

Was this:

 Date: Tue, 05 Jun 2018 13:39:15 +0300
 Message-ID: <152819515565.30857.16834004920507717324.stgit@pasha-ThinkPad-T60>
 Subject: [Qemu-devel] [RFC PATCH v2 0/7] QEMU binary instrumentation prototype

There have certainly been a lot of attempts to getting some sort of
plugin functionality into QEMU. I make no promises this one will be the
one but we shall see!

What patches did you add on top?

>
> -Aaron


--
Alex Bennée


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

* Re: [Qemu-devel] [PATCH v3 22/50] *-user: plugin syscalls
  2019-06-19  3:35   ` Pranith Kumar
@ 2019-07-01 14:20     ` Alex Bennée
  0 siblings, 0 replies; 117+ messages in thread
From: Alex Bennée @ 2019-07-01 14:20 UTC (permalink / raw)
  To: Pranith Kumar; +Cc: Riku Voipio, Emilio G. Cota, qemu-devel, Laurent Vivier


Pranith Kumar <bobby.prani@gmail.com> writes:

> Minor nits.
>
> On Fri, Jun 14, 2019 at 11:41 AM Alex Bennée <alex.bennee@linaro.org> wrote:
>>
>> From: "Emilio G. Cota" <cota@braap.org>
>>
>> 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 84a983a9a1..50e47d217c 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);
>
> Looking at the previous line, seems like you can avoid splitting this
> line into 2. Keeps it more consistent that way.

Technically the trace line is over-long... I can make them both
consistent with each other but it would be nicer to wrap them both into
a common function.

Unfortunately this seems a little tricky for *-user as they are kept
pretty separate.

>
>>      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);
>
> ditto.
>
>>      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);
>
> ditto.
>
>>      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 b187c1281d..7f3cfdee84 100644
>> --- a/linux-user/syscall.c
>> +++ b/linux-user/syscall.c
>> @@ -11724,6 +11724,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);
>
> This I am not sure.
>
>
>>
>>      if (unlikely(do_strace)) {
>>          print_syscall(num, arg1, arg2, arg3, arg4, arg5, arg6);
>> @@ -11736,5 +11738,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.20.1
>>
>>


--
Alex Bennée


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

* Re: [Qemu-devel] [PATCH v3 19/50] tcg: let plugins instrument memory accesses
  2019-06-28 20:52         ` Alex Bennée
@ 2019-07-01 14:40           ` Aaron Lindsay OS via Qemu-devel
  2019-07-01 15:00             ` Alex Bennée
  0 siblings, 1 reply; 117+ messages in thread
From: Aaron Lindsay OS via Qemu-devel @ 2019-07-01 14:40 UTC (permalink / raw)
  To: Alex Bennée
  Cc: Paolo Bonzini, Emilio G. Cota, qemu-devel, Richard Henderson

On Jun 28 21:52, Alex Bennée wrote:
> Aaron Lindsay OS <aaron@os.amperecomputing.com> writes:
> > To make sure I understand - you're implying that one such query will
> > return the PA from the guest's perspective, right?
> 
> Yes - although it will be two queries:
> 
>   struct qemu_plugin_hwaddr *hw = qemu_plugin_get_hwaddr(info, vaddr);
> 
> This does the actual lookup and stores enough information for the
> further queries.
> 
>   uint64_t pa = qemu_plugin_hwaddr_to_raddr(hw);
> 
> will return the physical address (assuming it's a RAM reference and not
> some IO location).

Sounds good, as long as we have a good way to either prevent or cleanly
detect the failure mode for the IO accesses.

> > In terms of our use case - we use QEMU to drive studies to help us
> > design the next generation of processors. As you can imagine, having the
> > right physical addresses is important for some aspects of that. We're
> > currently using a version of Pavel Dovgalyuk's earlier plugin patchset
> > with some of our own patches/fixes on top, but it would obviously make
> > our lives easier to work together to get this sort of infrastructure
> > upstream!
> 
> Was this:
> 
>  Date: Tue, 05 Jun 2018 13:39:15 +0300
>  Message-ID: <152819515565.30857.16834004920507717324.stgit@pasha-ThinkPad-T60>
>  Subject: [Qemu-devel] [RFC PATCH v2 0/7] QEMU binary instrumentation prototype

Yes, that looks like the one.

> What patches did you add on top?

We added:
- plugin support for linux-user mode (I sent that one upstream, I think)
- memory tracing support and a VA->PA conversion helper
- a way for a plugin to request getting a callback just before QEMU
  exits to clean up any internal state
- a way for a plugin to reset any instrumentation decisions made in the
  past (essentially calls `tb_flush(cpu);` under the covers). We found
  this critical for plugins which undergo state changes during the
  course of their execution (i.e. watch for event X, then go into a more
  detailed profiling mode until you see event Y)
- instrumentation at the TB granularity (in addition to the existing
  instruction-level support)
- the ability for a plugin to trigger a checkpoint to be taken

-Aaron


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

* Re: [Qemu-devel] [PATCH v3 19/50] tcg: let plugins instrument memory accesses
  2019-07-01 14:40           ` Aaron Lindsay OS via Qemu-devel
@ 2019-07-01 15:00             ` Alex Bennée
  2019-07-02 14:07               ` Aaron Lindsay OS via Qemu-devel
  0 siblings, 1 reply; 117+ messages in thread
From: Alex Bennée @ 2019-07-01 15:00 UTC (permalink / raw)
  To: Aaron Lindsay OS
  Cc: Paolo Bonzini, Emilio G. Cota, qemu-devel, Richard Henderson


Aaron Lindsay OS <aaron@os.amperecomputing.com> writes:

> On Jun 28 21:52, Alex Bennée wrote:
>> Aaron Lindsay OS <aaron@os.amperecomputing.com> writes:
>> > To make sure I understand - you're implying that one such query will
>> > return the PA from the guest's perspective, right?
>>
>> Yes - although it will be two queries:
>>
>>   struct qemu_plugin_hwaddr *hw = qemu_plugin_get_hwaddr(info, vaddr);
>>
>> This does the actual lookup and stores enough information for the
>> further queries.
>>
>>   uint64_t pa = qemu_plugin_hwaddr_to_raddr(hw);
>>
>> will return the physical address (assuming it's a RAM reference and not
>> some IO location).
>
> Sounds good, as long as we have a good way to either prevent or cleanly
> detect the failure mode for the IO accesses.
>
>> > In terms of our use case - we use QEMU to drive studies to help us
>> > design the next generation of processors. As you can imagine, having the
>> > right physical addresses is important for some aspects of that. We're
>> > currently using a version of Pavel Dovgalyuk's earlier plugin patchset
>> > with some of our own patches/fixes on top, but it would obviously make
>> > our lives easier to work together to get this sort of infrastructure
>> > upstream!
>>
>> Was this:
>>
>>  Date: Tue, 05 Jun 2018 13:39:15 +0300
>>  Message-ID: <152819515565.30857.16834004920507717324.stgit@pasha-ThinkPad-T60>
>>  Subject: [Qemu-devel] [RFC PATCH v2 0/7] QEMU binary instrumentation prototype
>
> Yes, that looks like the one.
>
>> What patches did you add on top?
>
> We added:
> - plugin support for linux-user mode (I sent that one upstream, I think)
> - memory tracing support and a VA->PA conversion helper

check

> - a way for a plugin to request getting a callback just before QEMU
>   exits to clean up any internal state

check - qemu_plugin_register_atexit_cb

> - a way for a plugin to reset any instrumentation decisions made in the
>   past (essentially calls `tb_flush(cpu);` under the covers). We found
>   this critical for plugins which undergo state changes during the
>   course of their execution (i.e. watch for event X, then go into a more
>   detailed profiling mode until you see event Y)

check:

/**
 * qemu_plugin_reset() - Reset a plugin
 * @id: this plugin's opaque ID
 * @cb: callback to be called once the plugin has been reset
 *
 * Unregisters all callbacks for the plugin given by @id.
 *
 * Do NOT assume that the plugin has been reset once this function returns.
 * Plugins are reset asynchronously, and therefore the given plugin receives
 * callbacks until @cb is called.
 */
void qemu_plugin_reset(qemu_plugin_id_t id, qemu_plugin_simple_cb_t cb);


> - instrumentation at the TB granularity (in addition to the existing
>   instruction-level support)

check

/**
 * qemu_plugin_register_vcpu_tb_trans_cb() - register a translate cb
 * @id: plugin ID
 * @cb: callback function
 *
 * The @cb function is called every time a translation occurs. The @cb
 * function is passed an opaque qemu_plugin_type which it can query
 * for additional information including the list of translated
 * instructions. At this point the plugin can register further
 * callbacks to be triggered when the block or individual instruction
 * executes.
 */

and then you can have instruction or TB level callbacks:

/**
 * qemu_plugin_register_vcpu_tb_trans_exec_cb() - register execution callback
 * @tb: the opaque qemu_plugin_tb handle for the translation
 * @cb: callback function
 * @flags: does the plugin read or write the CPU's registers?
 * @userdata: any plugin data to pass to the @cb?
 *
 * The @cb function is called every time a translated unit executes.
 */
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);

Or the inline equivalent.


> - the ability for a plugin to trigger a checkpoint to be taken

We don't have this at the moment. Pranith also mentioned it in his
review comments. I can see its use but I suspect it won't make the
initial implementation given the broader requirements of QEMU to do
checkpointing and how to cleanly expose that to plugins.

>
> -Aaron


--
Alex Bennée


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

* Re: [Qemu-devel] [PATCH v3 37/50] translator: inject instrumentation from plugins
  2019-06-17 22:44   ` Richard Henderson
  2019-06-20 16:51     ` Alex Bennée
@ 2019-07-01 16:01     ` Alex Bennée
  1 sibling, 0 replies; 117+ messages in thread
From: Alex Bennée @ 2019-07-01 16:01 UTC (permalink / raw)
  To: Richard Henderson
  Cc: Paolo Bonzini, Emilio G. Cota, qemu-devel, Richard Henderson


Richard Henderson <richard.henderson@linaro.org> writes:

> On 6/14/19 10:11 AM, Alex Bennée wrote:
>> @@ -95,6 +103,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) {
>
> This will of course not be reachable if db->is_jmp == DISAS_NORETURN.
> Do we want to not bother calling the plugin for this case?

Swap the order and add a comment?

--
Alex Bennée


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

* Re: [Qemu-devel] [PATCH v3 00/50] tcg plugin support
  2019-06-21 17:36     ` Pranith Kumar
@ 2019-07-01 16:51       ` Alex Bennée
  0 siblings, 0 replies; 117+ messages in thread
From: Alex Bennée @ 2019-07-01 16:51 UTC (permalink / raw)
  To: Pranith Kumar; +Cc: Emilio G. Cota, qemu-devel, Richard Henderson


Pranith Kumar <bobby.prani@gmail.com> writes:

> On Fri, Jun 21, 2019 at 1:21 AM Alex Bennée <alex.bennee@linaro.org> wrote:
>
>> > * Register and memory read/write API
>> >
>> >   It would be great to have register and memory read/write API i.e., ability
>> >   to read/write to registers/memory from within the callback. This gives the
>> >   plugin ability to do system introspection. (Not sure if the current patchset
>> >   implements this already).
>>
>> Not currently. The trick is to have something flexible enough without
>> exposing internals. I guess we could consider the gdb register
>> enumeration or maybe hook into stuff declared as
>> tcg_global_mem_new_i[64|32]. That won't get every "register" but it
>> should certainly cover the main general purpose ones. Things like SVE
>> and AdvSIMD vector registers wouldn't be seen though.
>
> I guess general registers could be a good starting point. We can then
> implement arch specific register access APIs.
>
>>
>> > * Register callbacks
>> >
>> >   A callback needs to be invoked whenever a specified registers is read or
>> >   written to.
>>
>> Again tricky as not every register read/write is obvious from TCG -
>> vector registers tweaked from helpers would be a good example.
>>
>> >
>> > * Where do new plugins live in the tree?
>> >
>> >   The current source files in plugins (api, core etc.,) I think are better if
>> >   moved to tcg/plugins/.  The various plugins we write would then live in the
>> >   plugins/ folder instead of the current tests/plugin/ folder.
>>
>> The example plugins are really just toys for experimenting with the API
>> - I don't see too much problem with them being in tests. However the
>> howvec plugin is very guest architecture specific so we could consider a
>> bit more of a hierarchy. Maybe these should all live in tests/tcg?
>>
>
> So where do you want 'real' plugins to live in the tree? It would be
> good to think about the structure for those.

I don't see whats wrong with tests/plugins for this. For the upstream
point of view they are there to test and exercise the plugin code.

>
>> >
>> > * Timer interrupts
>> >
>> >   What I observed is that the system execution is affected by what you do in
>> >   the callbacks because of timer interrupts. For example, if you spend time in
>> >   the memory callback doing a bit of processing and writing to a file, you
>> >   will see more timer interrupt instructions. One solution to this would be to
>> >   use 'icount', but it does not work that well. I think we need to do
>> >   something similar to what gdb does in debug mode. How would you handle MTTCG
>> >   guests in that case?
>>
>> icount is going to be the best you can get for deterministic time -
>> other efforts to pause/restart virtual time going in and out of plugins
>> are just going to add a lot of overhead.
>
> I wonder why using icount is not working in this case. Are there any
> timers that fire non-deterministically even when icount is used?

Shouldn't be. What sort of problems are you seeing?

>> Remember QEMU doesn't even try to be a cycle accurate emulation so
>> expecting to get reasonable timing information out of these plugins is a
>> stretch. Maybe I should make that clearer in the design document?
>
> It is less about being cycle accurate and more about being
> deterministic. For example, when tracing using plugins+callbacks, you
> will see a lot more interrupt code in the trace than when if you
> execute without tracing. How do we get them to be more similar?
>
> Another idea would be to provide an API for the plugin to generate the
> timer interrupt. This allows the plugin to generate regular interrupts
> irrespective of what is being done in the callbacks.

I don't think allowing plugins to change behaviour of the code is
something we want to consider - at least for v1.

>
>>
>> The gdb behaviour is just a massive hack. When single-stepping in GDB we
>> prevent timer IRQs from being delivered - they have still fired and are
>> pending and will execute as soon as you hit continue.
>>
>> >   Another approach would be to offload callback generation to a separate
>> >   plugin thread. The main thread will copy data required by a callback and
>> >   invoke the callback asynchronously (std::async in C++ if you are
>> >   familiar).
>>
>> This would complicate things - the current iteration I'm working on
>> drops the haddr cb in favour of dynamically resolving the vaddr in the
>> callback. But that approach is only valid during the callback before
>> something else potentially pollutes the TLB.
>>
>
>> >
>> > * Starting and stopping callback generation
>> >
>> >   It would be great if we have a mechanism to dynamically start/stop callbacks
>> >   when a sequence of code (magic instruction) is executed. This would be
>> >   useful to annotate region-of-interest (ROI) in benchmarks to
>> > generate callbacks.
>>
>> Well we have that now. At each TB generation event the callback is free to register
>> as many or few callbacks as it likes dynamically.
>
> But how does the plugin know that the TB being generated is the first
> TB in the ROI?

It has to track the information - I assume by a combination of looking
at addresses or looking through the instruction patterns.

> Similarly the plugin needs to know the then end of ROI has been reached.
>
> Also, please note that there can be multiple ROIs. It would be good to
> know if we can assign ids to each ROI for the plugin.

This all sounds like stuff the plugin can do for itself. What else does
it need to know from QEMU?

--
Alex Bennée


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

* Re: [Qemu-devel] [PATCH v3 19/50] tcg: let plugins instrument memory accesses
  2019-07-01 15:00             ` Alex Bennée
@ 2019-07-02 14:07               ` Aaron Lindsay OS via Qemu-devel
  0 siblings, 0 replies; 117+ messages in thread
From: Aaron Lindsay OS via Qemu-devel @ 2019-07-02 14:07 UTC (permalink / raw)
  To: Alex Bennée
  Cc: Paolo Bonzini, Emilio G. Cota, qemu-devel, Richard Henderson

On Jul 01 16:00, Alex Bennée wrote:
> Aaron Lindsay OS <aaron@os.amperecomputing.com> writes:
> > - a way for a plugin to reset any instrumentation decisions made in the
> >   past (essentially calls `tb_flush(cpu);` under the covers). We found
> >   this critical for plugins which undergo state changes during the
> >   course of their execution (i.e. watch for event X, then go into a more
> >   detailed profiling mode until you see event Y)
> 
> check:
> 
> /**
>  * qemu_plugin_reset() - Reset a plugin
>  * @id: this plugin's opaque ID
>  * @cb: callback to be called once the plugin has been reset
>  *
>  * Unregisters all callbacks for the plugin given by @id.
>  *
>  * Do NOT assume that the plugin has been reset once this function returns.
>  * Plugins are reset asynchronously, and therefore the given plugin receives
>  * callbacks until @cb is called.
>  */
> void qemu_plugin_reset(qemu_plugin_id_t id, qemu_plugin_simple_cb_t cb);

Is this essentially synchronous for the current cpu, and only
asynchronous for any other running cpus that didn't trigger the callback
from which the call to qemu_plugin_reset() is being made? If not, could
the state resetting be made synchronous for the current cpu (even if the
callback doesn't happen until the others are complete)? This isn't
absolutely critical, but it is often nice to begin capturing precisely
when you mean to.

> > - the ability for a plugin to trigger a checkpoint to be taken
> 
> We don't have this at the moment. Pranith also mentioned it in his
> review comments. I can see its use but I suspect it won't make the
> initial implementation given the broader requirements of QEMU to do
> checkpointing and how to cleanly expose that to plugins.

Sure. Our patch works for us, but I know we're ignoring a few things
that we can externally ensure won't happen while we're attempting a
checkpoint (i.e. migration) that may have to be considered for something
upstream.

-Aaron


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

* Re: [Qemu-devel] [PATCH v3 25/50] translator: add translator_ld{ub, sw, uw, l, q}
  2019-06-17 21:06   ` Richard Henderson
@ 2019-07-30 12:41     ` Alex Bennée
  2019-07-30 13:23       ` Richard Henderson
  0 siblings, 1 reply; 117+ messages in thread
From: Alex Bennée @ 2019-07-30 12:41 UTC (permalink / raw)
  To: Richard Henderson; +Cc: Emilio G. Cota, qemu-devel


Richard Henderson <richard.henderson@linaro.org> writes:

> On 6/14/19 10:11 AM, Alex Bennée wrote:
>> +#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);                                         \
>> +        }                                                               \
>
> This feels like we should have done this at a different level.  We already have
> lower-level functions that read from memory with the proper
> endianness.

Yeah - this really only caters to the translator for guests which can
switch their mode.

> It seems that we don't have them for *_code, but that could be fixed.  Or,
> indeed, bypassed, since these could be the new official interface, deprecating
> the *_code functions.

Hmm how to properly audit that _code isn't being used elsewhere. You get
lost in a maze of macros pretty quickly :-/

So you are proposing dropping the _code helpers from cpu_ldst_*_template
and directly binding to the low level load/softmmu function from
translator.h? Do we ever need _code access that isn't part of the
translator loading instructions?

>
>
> r~


--
Alex Bennée


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

* Re: [Qemu-devel] [PATCH v3 25/50] translator: add translator_ld{ub, sw, uw, l, q}
  2019-07-30 12:41     ` Alex Bennée
@ 2019-07-30 13:23       ` Richard Henderson
  2019-07-30 14:08         ` Alex Bennée
  2019-07-30 17:04         ` Alex Bennée
  0 siblings, 2 replies; 117+ messages in thread
From: Richard Henderson @ 2019-07-30 13:23 UTC (permalink / raw)
  To: Alex Bennée; +Cc: Emilio G. Cota, qemu-devel

On 7/30/19 5:41 AM, Alex Bennée wrote:
> Do we ever need _code access that isn't part of the
> translator loading instructions?

We use it; I'm not sure that's the same as need.  ;-)

Lots of the uses that I examined should use a mechanism
like arm does for recording syndrome data in the unwind slots.

Quite possibly the only legitimate use is sparc, where it
has an alternate address space that reads with execute permission.
We could probably find a different way to accomplish that
if we removed the *_code helpers.


r~


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

* Re: [Qemu-devel] [PATCH v3 25/50] translator: add translator_ld{ub, sw, uw, l, q}
  2019-07-30 13:23       ` Richard Henderson
@ 2019-07-30 14:08         ` Alex Bennée
  2019-07-30 17:04         ` Alex Bennée
  1 sibling, 0 replies; 117+ messages in thread
From: Alex Bennée @ 2019-07-30 14:08 UTC (permalink / raw)
  To: Richard Henderson; +Cc: Emilio G. Cota, qemu-devel


Richard Henderson <richard.henderson@linaro.org> writes:

> On 7/30/19 5:41 AM, Alex Bennée wrote:
>> Do we ever need _code access that isn't part of the
>> translator loading instructions?
>
> We use it; I'm not sure that's the same as need.  ;-)

Yeah I've run into others (e.g. alpha alpha_cpu_do_unaligned_access). So
the question is do I attempt to deprecate code load in this series?

> Lots of the uses that I examined should use a mechanism
> like arm does for recording syndrome data in the unwind slots.

Yeah - hence the semihosting fixups. ATM we only touch translator_loop
guests but deprecating means having to do them all. Maybe we could
poison the build someway and ensure as each legacy translation is
converted to translator_loop we convert the _code use functions there.

> Quite possibly the only legitimate use is sparc, where it
> has an alternate address space that reads with execute permission.
> We could probably find a different way to accomplish that
> if we removed the *_code helpers.
>
>
> r~


--
Alex Bennée


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

* Re: [Qemu-devel] [PATCH v3 25/50] translator: add translator_ld{ub, sw, uw, l, q}
  2019-07-30 13:23       ` Richard Henderson
  2019-07-30 14:08         ` Alex Bennée
@ 2019-07-30 17:04         ` Alex Bennée
  1 sibling, 0 replies; 117+ messages in thread
From: Alex Bennée @ 2019-07-30 17:04 UTC (permalink / raw)
  To: Richard Henderson; +Cc: Emilio G. Cota, qemu-devel


Richard Henderson <richard.henderson@linaro.org> writes:

> On 7/30/19 5:41 AM, Alex Bennée wrote:
>> Do we ever need _code access that isn't part of the
>> translator loading instructions?
>
> We use it; I'm not sure that's the same as need.  ;-)
>
> Lots of the uses that I examined should use a mechanism
> like arm does for recording syndrome data in the unwind slots.
>
> Quite possibly the only legitimate use is sparc, where it
> has an alternate address space that reads with execute permission.
> We could probably find a different way to accomplish that
> if we removed the *_code helpers.

So far I've gone for deprecation, my current fixup patch looks like
this:

--8<---------------cut here---------------start------------->8---
fixup! translator: add translator_ld{ub,sw,uw,l,q}

4 files changed, 53 insertions(+), 7 deletions(-)
include/exec/cpu_ldst.h   | 11 +++++++++++
include/exec/translator.h | 42 +++++++++++++++++++++++++++++++++++-------
include/qemu/bswap.h      |  5 +++++
tcg/tcg.h                 |  2 ++

modified   include/exec/cpu_ldst.h
@@ -129,6 +129,11 @@ static inline void clear_helper_retaddr(void)
 #include "exec/cpu_ldst_useronly_template.h"
 #undef MEMSUFFIX

+/*
+ * Code access is deprecated in favour of translator_ld* functions
+ * (see translator.h). However there are still users that need to
+ * converted so for now these stay.
+ */
 #define MEMSUFFIX _code
 #define CODE_ACCESS
 #define DATA_SIZE 1
@@ -427,6 +432,12 @@ static inline CPUTLBEntry *tlb_entry(CPUArchState *env, uintptr_t mmu_idx,
 #undef CPU_MMU_INDEX
 #undef MEMSUFFIX

+/*
+ * Code access is deprecated in favour of translator_ld* functions
+ * (see translator.h). However there are still users that need to
+ * converted so for now these stay.
+ */
+
 #define CPU_MMU_INDEX (cpu_mmu_index(env, true))
 #define MEMSUFFIX _code
 #define SOFTMMU_CODE_ACCESS
modified   include/exec/translator.h
@@ -145,11 +145,39 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,

 void translator_loop_temp_check(DisasContextBase *db);

-#define GEN_TRANSLATOR_LD(fullname, name, type, swap_fn)                \
+/*
+ * Translator Load Functions
+ *
+ * These are intended to replace the old cpu_ld*_code functions and
+ * are mandatory for front-ends that have been migrated to the common
+ * translator_loop. These functions are only intended to be called
+ * from the translation stage and should not be called from helper
+ * functions. Those functions should be converted to encode the
+ * relevant at translation time.
+ */
+
+#ifdef CONFIG_USER_ONLY
+
+#define DO_LOAD(type, name, shift)               \
+    set_helper_retaddr(1);                       \
+    ret = name ## _p(g2h(pc));                   \
+    clear_helper_retaddr();
+
+#else
+
+#define DO_LOAD(type, name, shift)                   \
+    int mmu_idx = cpu_mmu_index(env, true);          \
+    TCGMemOpIdx oi = make_memop_idx(shift, mmu_idx); \
+    ret = helper_ret_ ## name ## _cmmu(env, pc, oi, 0);
+
+#endif
+
+#define GEN_TRANSLATOR_LD(fullname, name, type, shift, swap_fn)         \
     static inline type                                                  \
     fullname ## _swap(CPUArchState *env, abi_ptr pc, bool do_swap)      \
     {                                                                   \
-        type ret = cpu_ ## name ## _code(env, pc);                      \
+        type ret;                                                       \
+        DO_LOAD(type, name, shift)                                      \
                                                                         \
         if (do_swap) {                                                  \
             ret = swap_fn(ret);                                         \
@@ -163,11 +191,11 @@ void translator_loop_temp_check(DisasContextBase *db);
         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)
+GEN_TRANSLATOR_LD(translator_ldub, ldb, uint8_t, 1, /* no swap needed */)
+GEN_TRANSLATOR_LD(translator_ldsw, lduw, int16_t, 2, bswap16)
+GEN_TRANSLATOR_LD(translator_lduw, lduw, uint16_t, 2, bswap16)
+GEN_TRANSLATOR_LD(translator_ldl, ldl, uint32_t, 3, bswap32)
+GEN_TRANSLATOR_LD(translator_ldq, ldq, uint64_t, 4, bswap64)
 #undef GEN_TRANSLATOR_LD

 #endif  /* EXEC__TRANSLATOR_H */
modified   include/qemu/bswap.h
@@ -306,6 +306,11 @@ static inline int ldub_p(const void *ptr)
     return *(uint8_t *)ptr;
 }

+static inline int ldb_p(const void *ptr)
+{
+    return ldub_p(ptr);
+}
+
 static inline int ldsb_p(const void *ptr)
 {
     return *(int8_t *)ptr;
modified   tcg/tcg.h
@@ -1404,6 +1404,7 @@ uint64_t helper_be_ldq_cmmu(CPUArchState *env, target_ulong addr,
 # define helper_ret_stl_mmu   helper_be_stl_mmu
 # define helper_ret_stq_mmu   helper_be_stq_mmu
 # define helper_ret_ldw_cmmu  helper_be_ldw_cmmu
+# define helper_ret_lduw_cmmu helper_be_ldw_cmmu
 # define helper_ret_ldl_cmmu  helper_be_ldl_cmmu
 # define helper_ret_ldq_cmmu  helper_be_ldq_cmmu
 #else
@@ -1417,6 +1418,7 @@ uint64_t helper_be_ldq_cmmu(CPUArchState *env, target_ulong addr,
 # define helper_ret_stl_mmu   helper_le_stl_mmu
 # define helper_ret_stq_mmu   helper_le_stq_mmu
 # define helper_ret_ldw_cmmu  helper_le_ldw_cmmu
+# define helper_ret_lduw_cmmu helper_le_ldw_cmmu
 # define helper_ret_ldl_cmmu  helper_le_ldl_cmmu
 # define helper_ret_ldq_cmmu  helper_le_ldq_cmmu
 #endif
--8<---------------cut here---------------end--------------->8---

--
Alex Bennée


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

* Re: [Qemu-devel] [PATCH v3 33/50] target/riscv: fetch code with translator_ld
  2019-06-19 10:49     ` Palmer Dabbelt
@ 2019-09-27 21:47       ` Alistair Francis
  0 siblings, 0 replies; 117+ messages in thread
From: Alistair Francis @ 2019-09-27 21:47 UTC (permalink / raw)
  To: Palmer Dabbelt
  Cc: open list:RISC-V, Sagar Karandikar, Bastian Koppelmann,
	Richard Henderson, qemu-devel@nongnu.org Developers,
	Emilio G. Cota, Alistair Francis, Alex Bennée

On Wed, Jun 19, 2019 at 3:50 AM Palmer Dabbelt <palmer@sifive.com> wrote:
>
> On Mon, 17 Jun 2019 15:38:45 PDT (-0700), richard.henderson@linaro.org wrote:
> > On 6/14/19 10:11 AM, Alex Bennée wrote:
> >> +++ b/target/riscv/translate.c
> >> @@ -793,7 +793,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);
> >
> > I'll note for the riscv folks that this is an existing bug, reading too much in
> > the case of an RVC instruction.  This could well matter for the last 2-byte
> > instruction at the end of a page.
> >
> > Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
>
> Thanks for pointing this out.  I'm checking the ISA semantics with Andrew to
> make sure I've got it right, as there's some implicit wording in the document
> that doesn't quite do what I'd expect it to.

Did we figure out what to do here?

Alistair

>


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

end of thread, other threads:[~2019-09-27 21:55 UTC | newest]

Thread overview: 117+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-06-14 17:11 [Qemu-devel] [PATCH v3 00/50] tcg plugin support Alex Bennée
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 01/50] trace: expand mem_info:size_shift to 3 bits Alex Bennée
2019-06-17  2:12   ` Richard Henderson
2019-06-17  8:22     ` Alex Bennée
2019-06-17 23:29       ` Richard Henderson
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 02/50] tcg/README: fix typo s/afterwise/afterwards/ Alex Bennée
2019-06-17  2:13   ` Richard Henderson
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 03/50] cpu: introduce cpu_in_exclusive_work_context() Alex Bennée
2019-06-17  2:15   ` Richard Henderson
2019-06-20  9:50     ` Alex Bennée
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 04/50] translate-all: use cpu_in_exclusive_work_context() in tb_flush Alex Bennée
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 05/50] docs/devel: add plugins.rst design document Alex Bennée
2019-06-19  3:34   ` Pranith Kumar
2019-06-20 13:38     ` Alex Bennée
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 06/50] configure: add --enable-plugins (MOVE TO END) Alex Bennée
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 07/50] plugin: add user-facing API Alex Bennée
2019-06-19  3:34   ` Pranith Kumar
2019-06-19 11:32     ` Alex Bennée
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 08/50] plugin: add core code Alex Bennée
     [not found]   ` <CADYwmhGiU_1GrBrR_tzBx+Lw+Hs3=Hi3AoPxRwkEj2pv9awqUg@mail.gmail.com>
2019-06-19 11:46     ` Alex Bennée
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 09/50] plugin: add implementation of the api Alex Bennée
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 10/50] queue: add QTAILQ_REMOVE_SEVERAL Alex Bennée
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 11/50] cputlb: document get_page_addr_code Alex Bennée
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 12/50] cputlb: introduce get_page_addr_code_hostp Alex Bennée
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 13/50] plugin-gen: add module for TCG-related code Alex Bennée
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 14/50] tcg: add tcg_gen_st_ptr Alex Bennée
2019-06-17 20:19   ` Richard Henderson
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 15/50] tcg: add MO_HADDR to TCGMemOp Alex Bennée
2019-06-17 20:43   ` Richard Henderson
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 16/50] atomic_template: fix indentation in GEN_ATOMIC_HELPER Alex Bennée
2019-06-17 20:43   ` Richard Henderson
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 17/50] atomic_template: add inline trace/plugin helpers Alex Bennée
2019-06-17 20:47   ` Richard Henderson
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 18/50] cpu_ldst_useronly_template: remove redundant #ifndef CODE_ACCESS Alex Bennée
2019-06-17 20:47   ` Richard Henderson
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 19/50] tcg: let plugins instrument memory accesses Alex Bennée
2019-06-17 20:51   ` Richard Henderson
2019-06-28 15:30   ` Aaron Lindsay OS via Qemu-devel
2019-06-28 17:11     ` Alex Bennée
2019-06-28 17:58       ` Aaron Lindsay OS via Qemu-devel
2019-06-28 20:52         ` Alex Bennée
2019-07-01 14:40           ` Aaron Lindsay OS via Qemu-devel
2019-07-01 15:00             ` Alex Bennée
2019-07-02 14:07               ` Aaron Lindsay OS via Qemu-devel
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 20/50] translate-all: notify plugin code of tb_flush Alex Bennée
2019-06-17 20:54   ` Richard Henderson
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 21/50] *-user: notify plugin of exit Alex Bennée
2019-06-17 20:54   ` Richard Henderson
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 22/50] *-user: plugin syscalls Alex Bennée
2019-06-17 20:56   ` Richard Henderson
2019-06-19  3:35   ` Pranith Kumar
2019-07-01 14:20     ` Alex Bennée
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 23/50] cpu: hook plugin vcpu events Alex Bennée
2019-06-17 21:00   ` Richard Henderson
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 24/50] plugin-gen: add plugin_insn_append Alex Bennée
2019-06-17 21:03   ` Richard Henderson
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 25/50] translator: add translator_ld{ub, sw, uw, l, q} Alex Bennée
2019-06-17 21:06   ` Richard Henderson
2019-07-30 12:41     ` Alex Bennée
2019-07-30 13:23       ` Richard Henderson
2019-07-30 14:08         ` Alex Bennée
2019-07-30 17:04         ` Alex Bennée
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 26/50] target/arm: call qemu_plugin_insn_append Alex Bennée
2019-06-17 22:28   ` Richard Henderson
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 27/50] target/ppc: fetch code with translator_ld Alex Bennée
2019-06-17 22:30   ` Richard Henderson
2019-06-19  9:39   ` David Gibson
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 28/50] target/sh4: " Alex Bennée
2019-06-17 22:33   ` Richard Henderson
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 29/50] target/i386: " Alex Bennée
2019-06-17 22:33   ` Richard Henderson
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 30/50] target/hppa: " Alex Bennée
2019-06-17 22:34   ` Richard Henderson
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 31/50] target/m68k: " Alex Bennée
2019-06-17 22:35   ` Richard Henderson
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 32/50] target/alpha: " Alex Bennée
2019-06-17 22:35   ` Richard Henderson
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 33/50] target/riscv: " Alex Bennée
2019-06-17 22:38   ` Richard Henderson
2019-06-19 10:49     ` Palmer Dabbelt
2019-09-27 21:47       ` Alistair Francis
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 34/50] target/sparc: " Alex Bennée
2019-06-17 22:39   ` Richard Henderson
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 35/50] target/xtensa: " Alex Bennée
2019-06-17 22:41   ` Richard Henderson
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 36/50] target/openrisc: " Alex Bennée
2019-06-17 22:41   ` Richard Henderson
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 37/50] translator: inject instrumentation from plugins Alex Bennée
2019-06-17 22:44   ` Richard Henderson
2019-06-20 16:51     ` Alex Bennée
2019-07-01 16:01     ` Alex Bennée
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 38/50] plugin: add API symbols to qemu-plugins.symbols Alex Bennée
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 39/50] vl: support -plugin option Alex Bennée
2019-06-17 22:53   ` Richard Henderson
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 40/50] linux-user: " Alex Bennée
2019-06-17 22:54   ` Richard Henderson
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 41/50] tests/plugin: add sample plugins Alex Bennée
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 42/50] tests/tcg: enable plugin testing Alex Bennée
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 43/50] tests/plugin: add a hotblocks plugin Alex Bennée
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 44/50] plugin: add qemu_plugin_insn_disas helper Alex Bennée
2019-06-17 23:09   ` Richard Henderson
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 45/50] tests/plugin: add instruction execution breakdown Alex Bennée
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 46/50] tests/plugin: add hotpages plugin to breakdown memory access patterns Alex Bennée
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 47/50] accel/stubs: reduce headers from tcg-stub Alex Bennée
2019-06-17 23:22   ` Richard Henderson
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 48/50] include/exec: wrap cpu_ldst.h in CONFIG_TCG Alex Bennée
2019-06-14 17:11 ` [Qemu-devel] [PATCH v3 49/50] include/exec/cpu-defs.h: fix typo Alex Bennée
2019-06-17 23:23   ` Richard Henderson
2019-06-14 17:12 ` [Qemu-devel] [PATCH v3 50/50] TODO: API changes to make? Alex Bennée
2019-06-14 17:41 ` [Qemu-devel] [PATCH v3 00/50] tcg plugin support Aleksandar Markovic
2019-06-14 18:39   ` Alex Bennée
2019-06-14 19:47 ` no-reply
2019-06-14 19:48 ` no-reply
2019-06-20 13:53 ` Pranith Kumar
2019-06-21  8:21   ` Alex Bennée
2019-06-21 17:36     ` Pranith Kumar
2019-07-01 16:51       ` Alex Bennée

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).