All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [RFC 00/48] Plugin support
@ 2018-10-25 17:20 Emilio G. Cota
  2018-10-25 17:20 ` [Qemu-devel] [RFC 01/48] cpu: introduce run_on_cpu_no_bql Emilio G. Cota
                   ` (48 more replies)
  0 siblings, 49 replies; 132+ messages in thread
From: Emilio G. Cota @ 2018-10-25 17:20 UTC (permalink / raw)
  To: qemu-devel
  Cc: Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Alex Bennée, Stefan Hajnoczi

For those of you who need some context: "plugins" are dynamic
libraries that are loaded at run-time. These plugins can
subscribe to interesting events (e.g. instruction execution)
via an API, to then do something interesting with them. This
functionality is similar to what other instrumentation tools (e.g.
Pin and DynamoRIO) provide, although since QEMU is full-system
we have some additional features.

As an example application, I've been using this plugin implementation
for the last year or so to implement a parallel computer simulator
that uses QEMU as its execution frontend.

The key features of this plugin implementation are:

- Support for an arbitrary number of plugins

- Focus on speed. "Dynamic" callbacks are used for frequent events,
  such as memory callbacks, to call the plugin code directly, i.e.
  without going through an intermediate helper. This provides
  an average 1.33x speedup for SPEC06 over using helpers with a list
  of subscribers, and it becomes more important as more subscribers
  are added. I can share more detailed numbers if you want them.

- Instruction-granularity instrumentation. Getting callbacks
  on *all* TBs/mem accesses/instructions is not flexible. Consider
  a plugin that just wants to get callbacks on the specific memory
  accesses of a set of instructions (e.g. cmpxchg); the API
  must provide a way for the plugin to subscribe to those events
  *only*, instead of giving it all events (e.g. all mem accesses)
  for the plugin to then discard 99.9% of them.

- 2-pass translation. Once a "TB translation" callback is called,
  the plugin must know the span of the TB. We should not
  force plugins to guess where the TB will end; that is strictly
  QEMU's job, and can change any time. A TB is thus a sequence
  of instructions of whatever length the particular QEMU
  implementation decides. Thus, for each TB, a 3-step process
  is followed: (1) the plugin layer keeps a copy of the contents
  of the current TB, (2) once the TB is well-defined, its
  descriptor and contents are passed to plugins, which then
  register their desired instrumentation (e.g. "call me back
  on this particular instruction", or "call me back when
  the whole TB executes"); note that plugins can use a disassembler
  like capstone to decide what to do with each instruction; they
  can also allocate memory and then get a pointer to it passed
  back from the callbacks. And finally, (3) the target translator
  is called again to generate the final instrumented translated TB.
  This is what I called the "2-pass translation", since we go
  twice over the translation loop in translator.c. Note that the
  2-pass approach has virtually no overhead (0.40% for SPEC06int);
  translation is much cheaper than execution. But anyway, if no
  plugins have subscribed to TB translation, we only do one pass.

- Support for inlining instrumentation. This is done via an
  explicit API, i.e. we do not export TCG ops, which are internal
  to QEMU. For now, I just have support for incrementing a u64
  with an immediate, e.g. to increment a counter.

- Treating the plugins as "malicious", in that we don't export
  any pointers to key QEMU data structures (CPUState, TB).
  I implemented this after a comment from Stefan, but maybe it is
  a bit overkill.

- Other features that go beyond passively getting callbacks (I need
  these for the simulator):
  + Control of the virtual clock from plugins
  + CPU lockstep execution, where plugins decide when CPUs must
    synchronize to reduce their execution skew. This can be understood
    as a "parallel icount" mode, although plugins can decide to
    synchronize whenever they want, not whenever a certain amount of
    instructions have execution. For instance, I am using this to
    synchronize CPUs every X number of simulated cycles, thereby
    having the ability to limit skew while maintaining parallelism.
    When a CPU is idle, then we assume its "execution window" (aka
    "time slice") has expired.
  + Guest hooks. Instead of using "magic" instructions, export a
    PCI device and let plugins determine what encoding to follow.
    I'm using this to mark regions of interest in guest programs,
    so that in the simulator I start/stop recording simulation events.

- Things I haven't included here:
  + Ability to emulate devices from plugins. I'm using this to
    simulate peripherals. These are devices whose timing is important
    to overall performance (e.g. 'accelerators' to which the main
    CPU offloads computation, e.g. a JPEG encoder).

The design I'm showing here shares nothing with the tracing infrastructure.
While it is true that some features (e.g. syscall callbacks) are
identical, some others (instruction-granularity instrumentation,
2-pass translation, lockstep execution) are not. So I'm open to
discussing where we could save code (e.g. having a single trace+plugin
generator, e.g. for syscalls), as long as performance and/or the
ability to instrument aren't compromise.

Peter: I remember you asked for an API first. I am including that as
a single patch in patch 14; see also patches 40, 45 and 47.

The first 10 or so patches in the series are preliminary work,
including the support of runtime TCG helpers. I think a subset
of this could be in a proper patch series, particularly the
xxhash patches. Then I've added plugin-related patches, trying
to break this down my original 80-or-so patches into something
a little easier to review. The "core" plugin code is perhaps the last
place to look, because when it is added nothing is calling it yet.
The last patch in the series adds some example plugins just for
discussion's sake.

This series applies on top of my cpu-lock-v4 series. You can fetch
it from:
  https://github.com/cota/qemu/tree/plugin

Cheers,

		Emilio

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

* [Qemu-devel] [RFC 01/48] cpu: introduce run_on_cpu_no_bql
  2018-10-25 17:20 [Qemu-devel] [RFC 00/48] Plugin support Emilio G. Cota
@ 2018-10-25 17:20 ` Emilio G. Cota
  2018-11-14 11:30   ` Alex Bennée
  2018-10-25 17:20 ` [Qemu-devel] [RFC 02/48] trace: expand mem_info:size_shift to 3 bits Emilio G. Cota
                   ` (47 subsequent siblings)
  48 siblings, 1 reply; 132+ messages in thread
From: Emilio G. Cota @ 2018-10-25 17:20 UTC (permalink / raw)
  To: qemu-devel
  Cc: Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Alex Bennée, Stefan Hajnoczi

This allows us to queue synchronous CPU work without the BQL.

Will gain a user soon.

Signed-off-by: Emilio G. Cota <cota@braap.org>
---
 include/qom/cpu.h | 13 +++++++++++++
 cpus-common.c     | 28 ++++++++++++++++++++++------
 2 files changed, 35 insertions(+), 6 deletions(-)

diff --git a/include/qom/cpu.h b/include/qom/cpu.h
index 204bc94056..863aa2bff1 100644
--- a/include/qom/cpu.h
+++ b/include/qom/cpu.h
@@ -877,6 +877,19 @@ bool cpu_is_stopped(CPUState *cpu);
  */
 void run_on_cpu(CPUState *cpu, run_on_cpu_func func, run_on_cpu_data data);
 
+/**
+ * run_on_cpu_no_bql
+ * @cpu: The vCPU to run on.
+ * @func: The function to be executed.
+ * @data: Data to pass to the function.
+ *
+ * Schedules the function @func for execution on the vCPU @cpu.
+ * This function is run outside the BQL.
+ * See also: run_on_cpu()
+ */
+void run_on_cpu_no_bql(CPUState *cpu, run_on_cpu_func func,
+                       run_on_cpu_data data);
+
 /**
  * async_run_on_cpu:
  * @cpu: The vCPU to run on.
diff --git a/cpus-common.c b/cpus-common.c
index cffb2b71ac..b478fc8741 100644
--- a/cpus-common.c
+++ b/cpus-common.c
@@ -144,7 +144,8 @@ static void queue_work_on_cpu(CPUState *cpu, struct qemu_work_item *wi)
     cpu_mutex_unlock(cpu);
 }
 
-void run_on_cpu(CPUState *cpu, run_on_cpu_func func, run_on_cpu_data data)
+static void do_run_on_cpu(CPUState *cpu, run_on_cpu_func func,
+                          run_on_cpu_data data, bool bql)
 {
     struct qemu_work_item wi;
     bool has_bql = qemu_mutex_iothread_locked();
@@ -152,12 +153,16 @@ void run_on_cpu(CPUState *cpu, run_on_cpu_func func, run_on_cpu_data data)
     g_assert(no_cpu_mutex_locked());
 
     if (qemu_cpu_is_self(cpu)) {
-        if (has_bql) {
-            func(cpu, data);
+        if (bql) {
+            if (has_bql) {
+                func(cpu, data);
+            } else {
+                qemu_mutex_lock_iothread();
+                func(cpu, data);
+                qemu_mutex_unlock_iothread();
+            }
         } else {
-            qemu_mutex_lock_iothread();
             func(cpu, data);
-            qemu_mutex_unlock_iothread();
         }
         return;
     }
@@ -172,7 +177,7 @@ void run_on_cpu(CPUState *cpu, run_on_cpu_func func, run_on_cpu_data data)
     wi.done = false;
     wi.free = false;
     wi.exclusive = false;
-    wi.bql = true;
+    wi.bql = bql;
 
     cpu_mutex_lock(cpu);
     queue_work_on_cpu_locked(cpu, &wi);
@@ -189,6 +194,17 @@ void run_on_cpu(CPUState *cpu, run_on_cpu_func func, run_on_cpu_data data)
     }
 }
 
+void run_on_cpu(CPUState *cpu, run_on_cpu_func func, run_on_cpu_data data)
+{
+    do_run_on_cpu(cpu, func, data, true);
+}
+
+void
+run_on_cpu_no_bql(CPUState *cpu, run_on_cpu_func func, run_on_cpu_data data)
+{
+    do_run_on_cpu(cpu, func, data, false);
+}
+
 void async_run_on_cpu(CPUState *cpu, run_on_cpu_func func, run_on_cpu_data data)
 {
     struct qemu_work_item *wi;
-- 
2.17.1

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

* [Qemu-devel] [RFC 02/48] trace: expand mem_info:size_shift to 3 bits
  2018-10-25 17:20 [Qemu-devel] [RFC 00/48] Plugin support Emilio G. Cota
  2018-10-25 17:20 ` [Qemu-devel] [RFC 01/48] cpu: introduce run_on_cpu_no_bql Emilio G. Cota
@ 2018-10-25 17:20 ` Emilio G. Cota
  2018-11-14 13:03   ` Alex Bennée
  2018-10-25 17:20 ` [Qemu-devel] [RFC 03/48] tcg/README: fix typo s/afterwise/afterwards/ Emilio G. Cota
                   ` (46 subsequent siblings)
  48 siblings, 1 reply; 132+ messages in thread
From: Emilio G. Cota @ 2018-10-25 17:20 UTC (permalink / raw)
  To: qemu-devel
  Cc: Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Alex Bennée, Stefan Hajnoczi

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

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

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

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

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

* [Qemu-devel] [RFC 03/48] tcg/README: fix typo s/afterwise/afterwards/
  2018-10-25 17:20 [Qemu-devel] [RFC 00/48] Plugin support Emilio G. Cota
  2018-10-25 17:20 ` [Qemu-devel] [RFC 01/48] cpu: introduce run_on_cpu_no_bql Emilio G. Cota
  2018-10-25 17:20 ` [Qemu-devel] [RFC 02/48] trace: expand mem_info:size_shift to 3 bits Emilio G. Cota
@ 2018-10-25 17:20 ` Emilio G. Cota
  2018-11-14 13:03   ` Alex Bennée
  2018-10-25 17:20 ` [Qemu-devel] [RFC 04/48] exec: introduce qemu_xxhash{2,4,5,6,7} Emilio G. Cota
                   ` (45 subsequent siblings)
  48 siblings, 1 reply; 132+ messages in thread
From: Emilio G. Cota @ 2018-10-25 17:20 UTC (permalink / raw)
  To: qemu-devel
  Cc: Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Alex Bennée, Stefan Hajnoczi

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

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

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

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

* [Qemu-devel] [RFC 04/48] exec: introduce qemu_xxhash{2,4,5,6,7}
  2018-10-25 17:20 [Qemu-devel] [RFC 00/48] Plugin support Emilio G. Cota
                   ` (2 preceding siblings ...)
  2018-10-25 17:20 ` [Qemu-devel] [RFC 03/48] tcg/README: fix typo s/afterwise/afterwards/ Emilio G. Cota
@ 2018-10-25 17:20 ` Emilio G. Cota
  2018-11-14 13:04   ` [Qemu-devel] [RFC 04/48] exec: introduce qemu_xxhash{2, 4, 5, 6, 7} Alex Bennée
  2018-10-25 17:20 ` [Qemu-devel] [RFC 05/48] include: move exec/tb-hash-xx.h to qemu/xxhash.h Emilio G. Cota
                   ` (44 subsequent siblings)
  48 siblings, 1 reply; 132+ messages in thread
From: Emilio G. Cota @ 2018-10-25 17:20 UTC (permalink / raw)
  To: qemu-devel
  Cc: Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Alex Bennée, Stefan Hajnoczi

Before moving them all to include/qemu/xxhash.h.

Signed-off-by: Emilio G. Cota <cota@braap.org>
---
 include/exec/tb-hash-xx.h | 41 +++++++++++++++++++++++++++++----------
 include/exec/tb-hash.h    |  2 +-
 tests/qht-bench.c         |  2 +-
 util/qsp.c                | 12 ++++++------
 4 files changed, 39 insertions(+), 18 deletions(-)

diff --git a/include/exec/tb-hash-xx.h b/include/exec/tb-hash-xx.h
index 747a9a612c..98ce4b628a 100644
--- a/include/exec/tb-hash-xx.h
+++ b/include/exec/tb-hash-xx.h
@@ -42,23 +42,23 @@
 #define PRIME32_4    668265263U
 #define PRIME32_5    374761393U
 
-#define TB_HASH_XX_SEED 1
+#define QEMU_XXHASH_SEED 1
 
 /*
  * xxhash32, customized for input variables that are not guaranteed to be
  * contiguous in memory.
  */
 static inline uint32_t
-tb_hash_func7(uint64_t a0, uint64_t b0, uint32_t e, uint32_t f, uint32_t g)
+qemu_xxhash7(uint64_t ab, uint64_t cd, uint32_t e, uint32_t f, uint32_t g)
 {
-    uint32_t v1 = TB_HASH_XX_SEED + PRIME32_1 + PRIME32_2;
-    uint32_t v2 = TB_HASH_XX_SEED + PRIME32_2;
-    uint32_t v3 = TB_HASH_XX_SEED + 0;
-    uint32_t v4 = TB_HASH_XX_SEED - PRIME32_1;
-    uint32_t a = a0 >> 32;
-    uint32_t b = a0;
-    uint32_t c = b0 >> 32;
-    uint32_t d = b0;
+    uint32_t v1 = QEMU_XXHASH_SEED + PRIME32_1 + PRIME32_2;
+    uint32_t v2 = QEMU_XXHASH_SEED + PRIME32_2;
+    uint32_t v3 = QEMU_XXHASH_SEED + 0;
+    uint32_t v4 = QEMU_XXHASH_SEED - PRIME32_1;
+    uint32_t a = ab >> 32;
+    uint32_t b = ab;
+    uint32_t c = cd >> 32;
+    uint32_t d = cd;
     uint32_t h32;
 
     v1 += a * PRIME32_2;
@@ -98,4 +98,25 @@ tb_hash_func7(uint64_t a0, uint64_t b0, uint32_t e, uint32_t f, uint32_t g)
     return h32;
 }
 
+static inline uint32_t qemu_xxhash2(uint64_t ab)
+{
+    return qemu_xxhash7(ab, 0, 0, 0, 0);
+}
+
+static inline uint32_t qemu_xxhash4(uint64_t ab, uint64_t cd)
+{
+    return qemu_xxhash7(ab, cd, 0, 0, 0);
+}
+
+static inline uint32_t qemu_xxhash5(uint64_t ab, uint64_t cd, uint32_t e)
+{
+    return qemu_xxhash7(ab, cd, e, 0, 0);
+}
+
+static inline uint32_t qemu_xxhash6(uint64_t ab, uint64_t cd, uint32_t e,
+                                    uint32_t f)
+{
+    return qemu_xxhash7(ab, cd, e, f, 0);
+}
+
 #endif /* EXEC_TB_HASH_XX_H */
diff --git a/include/exec/tb-hash.h b/include/exec/tb-hash.h
index 0526c4f678..731ba4c272 100644
--- a/include/exec/tb-hash.h
+++ b/include/exec/tb-hash.h
@@ -61,7 +61,7 @@ static inline
 uint32_t tb_hash_func(tb_page_addr_t phys_pc, target_ulong pc, uint32_t flags,
                       uint32_t cf_mask, uint32_t trace_vcpu_dstate)
 {
-    return tb_hash_func7(phys_pc, pc, flags, cf_mask, trace_vcpu_dstate);
+    return qemu_xxhash7(phys_pc, pc, flags, cf_mask, trace_vcpu_dstate);
 }
 
 #endif
diff --git a/tests/qht-bench.c b/tests/qht-bench.c
index 2089e2bed1..2ea132fe57 100644
--- a/tests/qht-bench.c
+++ b/tests/qht-bench.c
@@ -104,7 +104,7 @@ static bool is_equal(const void *ap, const void *bp)
 
 static uint32_t h(unsigned long v)
 {
-    return tb_hash_func7(v, 0, 0, 0, 0);
+    return qemu_xxhash2(v);
 }
 
 static uint32_t hval(unsigned long v)
diff --git a/util/qsp.c b/util/qsp.c
index a848b09c6d..dc29c41fde 100644
--- a/util/qsp.c
+++ b/util/qsp.c
@@ -135,13 +135,13 @@ QemuCondWaitFunc qemu_cond_wait_func = qemu_cond_wait_impl;
  * without it we still get a pretty unique hash.
  */
 static inline
-uint32_t do_qsp_callsite_hash(const QSPCallSite *callsite, uint64_t a)
+uint32_t do_qsp_callsite_hash(const QSPCallSite *callsite, uint64_t ab)
 {
-    uint64_t b = (uint64_t)(uintptr_t)callsite->obj;
+    uint64_t cd = (uint64_t)(uintptr_t)callsite->obj;
     uint32_t e = callsite->line;
     uint32_t f = callsite->type;
 
-    return tb_hash_func7(a, b, e, f, 0);
+    return qemu_xxhash6(ab, cd, e, f);
 }
 
 static inline
@@ -169,11 +169,11 @@ static uint32_t qsp_entry_no_thread_hash(const QSPEntry *entry)
 static uint32_t qsp_entry_no_thread_obj_hash(const QSPEntry *entry)
 {
     const QSPCallSite *callsite = entry->callsite;
-    uint64_t a = g_str_hash(callsite->file);
-    uint64_t b = callsite->line;
+    uint64_t ab = g_str_hash(callsite->file);
+    uint64_t cd = callsite->line;
     uint32_t e = callsite->type;
 
-    return tb_hash_func7(a, b, e, 0, 0);
+    return qemu_xxhash5(ab, cd, e);
 }
 
 static bool qsp_callsite_cmp(const void *ap, const void *bp)
-- 
2.17.1

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

* [Qemu-devel] [RFC 05/48] include: move exec/tb-hash-xx.h to qemu/xxhash.h
  2018-10-25 17:20 [Qemu-devel] [RFC 00/48] Plugin support Emilio G. Cota
                   ` (3 preceding siblings ...)
  2018-10-25 17:20 ` [Qemu-devel] [RFC 04/48] exec: introduce qemu_xxhash{2,4,5,6,7} Emilio G. Cota
@ 2018-10-25 17:20 ` Emilio G. Cota
  2018-11-14 13:05   ` Alex Bennée
  2018-10-25 17:20 ` [Qemu-devel] [RFC 06/48] tcg: use QHT for helper_table Emilio G. Cota
                   ` (43 subsequent siblings)
  48 siblings, 1 reply; 132+ messages in thread
From: Emilio G. Cota @ 2018-10-25 17:20 UTC (permalink / raw)
  To: qemu-devel
  Cc: Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Alex Bennée, Stefan Hajnoczi

Signed-off-by: Emilio G. Cota <cota@braap.org>
---
 include/exec/tb-hash.h                       | 2 +-
 include/{exec/tb-hash-xx.h => qemu/xxhash.h} | 6 +++---
 tests/qht-bench.c                            | 2 +-
 util/qsp.c                                   | 2 +-
 4 files changed, 6 insertions(+), 6 deletions(-)
 rename include/{exec/tb-hash-xx.h => qemu/xxhash.h} (97%)

diff --git a/include/exec/tb-hash.h b/include/exec/tb-hash.h
index 731ba4c272..4f3a37d927 100644
--- a/include/exec/tb-hash.h
+++ b/include/exec/tb-hash.h
@@ -20,7 +20,7 @@
 #ifndef EXEC_TB_HASH_H
 #define EXEC_TB_HASH_H
 
-#include "exec/tb-hash-xx.h"
+#include "qemu/xxhash.h"
 
 #ifdef CONFIG_SOFTMMU
 
diff --git a/include/exec/tb-hash-xx.h b/include/qemu/xxhash.h
similarity index 97%
rename from include/exec/tb-hash-xx.h
rename to include/qemu/xxhash.h
index 98ce4b628a..fe35dde328 100644
--- a/include/exec/tb-hash-xx.h
+++ b/include/qemu/xxhash.h
@@ -31,8 +31,8 @@
  * - xxHash source repository : https://github.com/Cyan4973/xxHash
  */
 
-#ifndef EXEC_TB_HASH_XX_H
-#define EXEC_TB_HASH_XX_H
+#ifndef QEMU_XXHASH_H
+#define QEMU_XXHASH_H
 
 #include "qemu/bitops.h"
 
@@ -119,4 +119,4 @@ static inline uint32_t qemu_xxhash6(uint64_t ab, uint64_t cd, uint32_t e,
     return qemu_xxhash7(ab, cd, e, f, 0);
 }
 
-#endif /* EXEC_TB_HASH_XX_H */
+#endif /* QEMU_XXHASH_H */
diff --git a/tests/qht-bench.c b/tests/qht-bench.c
index 2ea132fe57..4d885ca303 100644
--- a/tests/qht-bench.c
+++ b/tests/qht-bench.c
@@ -9,7 +9,7 @@
 #include "qemu/atomic.h"
 #include "qemu/qht.h"
 #include "qemu/rcu.h"
-#include "exec/tb-hash-xx.h"
+#include "qemu/xxhash.h"
 
 struct thread_stats {
     size_t rd;
diff --git a/util/qsp.c b/util/qsp.c
index dc29c41fde..410f1ba004 100644
--- a/util/qsp.c
+++ b/util/qsp.c
@@ -61,7 +61,7 @@
 #include "qemu/timer.h"
 #include "qemu/qht.h"
 #include "qemu/rcu.h"
-#include "exec/tb-hash-xx.h"
+#include "qemu/xxhash.h"
 
 enum QSPType {
     QSP_MUTEX,
-- 
2.17.1

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

* [Qemu-devel] [RFC 06/48] tcg: use QHT for helper_table
  2018-10-25 17:20 [Qemu-devel] [RFC 00/48] Plugin support Emilio G. Cota
                   ` (4 preceding siblings ...)
  2018-10-25 17:20 ` [Qemu-devel] [RFC 05/48] include: move exec/tb-hash-xx.h to qemu/xxhash.h Emilio G. Cota
@ 2018-10-25 17:20 ` Emilio G. Cota
  2018-11-14 14:41   ` Alex Bennée
  2018-11-14 16:11   ` Alex Bennée
  2018-10-25 17:20 ` [Qemu-devel] [RFC 07/48] tcg: export TCGHelperInfo Emilio G. Cota
                   ` (42 subsequent siblings)
  48 siblings, 2 replies; 132+ messages in thread
From: Emilio G. Cota @ 2018-10-25 17:20 UTC (permalink / raw)
  To: qemu-devel
  Cc: Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Alex Bennée, Stefan Hajnoczi

This will allow us to add TCG helpers at run-time.

While at it, rename tcg_find_helper to tcg_helper_find for consistency
with the added tcg_helper_foo functions.

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

diff --git a/tcg/tcg.c b/tcg/tcg.c
index e85133ef05..65da3c5dbf 100644
--- a/tcg/tcg.c
+++ b/tcg/tcg.c
@@ -33,6 +33,7 @@
 #include "qemu/error-report.h"
 #include "qemu/cutils.h"
 #include "qemu/host-utils.h"
+#include "qemu/xxhash.h"
 #include "qemu/timer.h"
 
 /* Note: the long term plan is to reduce the dependencies on the QEMU
@@ -879,13 +880,46 @@ typedef struct TCGHelperInfo {
 static const TCGHelperInfo all_helpers[] = {
 #include "exec/helper-tcg.h"
 };
-static GHashTable *helper_table;
+static struct qht helper_table;
+static bool helper_table_inited;
 
 static int indirect_reg_alloc_order[ARRAY_SIZE(tcg_target_reg_alloc_order)];
 static void process_op_defs(TCGContext *s);
 static TCGTemp *tcg_global_reg_new_internal(TCGContext *s, TCGType type,
                                             TCGReg reg, const char *name);
 
+static inline uint32_t tcg_helper_func_hash(const void *func)
+{
+    return qemu_xxhash2((uint64_t)func);
+}
+
+static bool tcg_helper_cmp(const void *ap, const void *bp)
+{
+    const TCGHelperInfo *a = ap;
+    const TCGHelperInfo *b = bp;
+
+    return a->func == b->func &&
+        a->flags == b->flags &&
+        a->sizemask == b->sizemask &&
+        !strcmp(a->name, b->name);
+}
+
+static bool tcg_helper_lookup_cmp(const void *obj, const void *func)
+{
+    const TCGHelperInfo *info = obj;
+
+    return info->func == func;
+}
+
+static void tcg_helper_insert(const TCGHelperInfo *info)
+{
+    uint32_t hash = tcg_helper_func_hash(info->func);
+    bool inserted;
+
+    inserted = qht_insert(&helper_table, (void *)info, hash, NULL);
+    g_assert(inserted);
+}
+
 void tcg_context_init(TCGContext *s)
 {
     int op, total_args, n, i;
@@ -919,13 +953,13 @@ void tcg_context_init(TCGContext *s)
     }
 
     /* Register helpers.  */
-    /* Use g_direct_hash/equal for direct pointer comparisons on func.  */
-    helper_table = g_hash_table_new(NULL, NULL);
+    qht_init(&helper_table, tcg_helper_cmp, ARRAY_SIZE(all_helpers),
+             QHT_MODE_AUTO_RESIZE);
 
     for (i = 0; i < ARRAY_SIZE(all_helpers); ++i) {
-        g_hash_table_insert(helper_table, (gpointer)all_helpers[i].func,
-                            (gpointer)&all_helpers[i]);
+        tcg_helper_insert(&all_helpers[i]);
     }
+    helper_table_inited = true;
 
     tcg_target_init(s);
     process_op_defs(s);
@@ -1620,9 +1654,10 @@ void tcg_gen_callN(void *func, TCGTemp *ret, int nargs, TCGTemp **args)
     int i, real_args, nb_rets, pi;
     unsigned sizemask, flags;
     TCGHelperInfo *info;
+    uint32_t hash = tcg_helper_func_hash(func);
     TCGOp *op;
 
-    info = g_hash_table_lookup(helper_table, (gpointer)func);
+    info = qht_lookup_custom(&helper_table, func, hash, tcg_helper_lookup_cmp);
     flags = info->flags;
     sizemask = info->sizemask;
 
@@ -1825,11 +1860,15 @@ static char *tcg_get_arg_str(TCGContext *s, char *buf,
 }
 
 /* Find helper name.  */
-static inline const char *tcg_find_helper(TCGContext *s, uintptr_t val)
+static inline const char *tcg_helper_find(TCGContext *s, uintptr_t val)
 {
     const char *ret = NULL;
-    if (helper_table) {
-        TCGHelperInfo *info = g_hash_table_lookup(helper_table, (gpointer)val);
+    if (helper_table_inited) {
+        uint32_t hash = tcg_helper_func_hash((void *)val);
+        TCGHelperInfo *info;
+
+        info = qht_lookup_custom(&helper_table, (void *)val, hash,
+                                 tcg_helper_lookup_cmp);
         if (info) {
             ret = info->name;
         }
@@ -1919,7 +1958,7 @@ void tcg_dump_ops(TCGContext *s)
 
             /* function name, flags, out args */
             col += qemu_log(" %s %s,$0x%" TCG_PRIlx ",$%d", def->name,
-                            tcg_find_helper(s, op->args[nb_oargs + nb_iargs]),
+                            tcg_helper_find(s, op->args[nb_oargs + nb_iargs]),
                             op->args[nb_oargs + nb_iargs + 1], nb_oargs);
             for (i = 0; i < nb_oargs; i++) {
                 col += qemu_log(",%s", tcg_get_arg_str(s, buf, sizeof(buf),
-- 
2.17.1

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

* [Qemu-devel] [RFC 07/48] tcg: export TCGHelperInfo
  2018-10-25 17:20 [Qemu-devel] [RFC 00/48] Plugin support Emilio G. Cota
                   ` (5 preceding siblings ...)
  2018-10-25 17:20 ` [Qemu-devel] [RFC 06/48] tcg: use QHT for helper_table Emilio G. Cota
@ 2018-10-25 17:20 ` Emilio G. Cota
  2018-11-14 16:12   ` Alex Bennée
  2018-10-25 17:20 ` [Qemu-devel] [RFC 08/48] tcg: export tcg_gen_runtime_helper Emilio G. Cota
                   ` (41 subsequent siblings)
  48 siblings, 1 reply; 132+ messages in thread
From: Emilio G. Cota @ 2018-10-25 17:20 UTC (permalink / raw)
  To: qemu-devel
  Cc: Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Alex Bennée, Stefan Hajnoczi

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

diff --git a/tcg/tcg.h b/tcg/tcg.h
index f4efbaa680..9f9643b470 100644
--- a/tcg/tcg.h
+++ b/tcg/tcg.h
@@ -480,6 +480,13 @@ typedef TCGv_ptr TCGv_env;
 /* Used to align parameters.  See the comment before tcgv_i32_temp.  */
 #define TCG_CALL_DUMMY_ARG      ((TCGArg)0)
 
+typedef struct TCGHelperInfo {
+    void *func;
+    const char *name;
+    unsigned flags;
+    unsigned sizemask;
+} TCGHelperInfo;
+
 /* Conditions.  Note that these are laid out for easy manipulation by
    the functions below:
      bit 0 is used for inverting;
diff --git a/tcg/tcg.c b/tcg/tcg.c
index 65da3c5dbf..08b6926894 100644
--- a/tcg/tcg.c
+++ b/tcg/tcg.c
@@ -868,13 +868,6 @@ void tcg_pool_reset(TCGContext *s)
     s->pool_current = NULL;
 }
 
-typedef struct TCGHelperInfo {
-    void *func;
-    const char *name;
-    unsigned flags;
-    unsigned sizemask;
-} TCGHelperInfo;
-
 #include "exec/helper-proto.h"
 
 static const TCGHelperInfo all_helpers[] = {
-- 
2.17.1

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

* [Qemu-devel] [RFC 08/48] tcg: export tcg_gen_runtime_helper
  2018-10-25 17:20 [Qemu-devel] [RFC 00/48] Plugin support Emilio G. Cota
                   ` (6 preceding siblings ...)
  2018-10-25 17:20 ` [Qemu-devel] [RFC 07/48] tcg: export TCGHelperInfo Emilio G. Cota
@ 2018-10-25 17:20 ` Emilio G. Cota
  2018-11-14 16:44   ` Alex Bennée
  2018-10-25 17:20 ` [Qemu-devel] [RFC 09/48] tcg: reset runtime helpers when flushing the code cache Emilio G. Cota
                   ` (40 subsequent siblings)
  48 siblings, 1 reply; 132+ messages in thread
From: Emilio G. Cota @ 2018-10-25 17:20 UTC (permalink / raw)
  To: qemu-devel
  Cc: Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Alex Bennée, Stefan Hajnoczi

This takes the TCGHelperInfo directly, which will allow us to generate
helpers at run-time.

Signed-off-by: Emilio G. Cota <cota@braap.org>
---
 tcg/tcg.h |  2 ++
 tcg/tcg.c | 50 +++++++++++++++++++++++++++++++++++++++++++++-----
 2 files changed, 47 insertions(+), 5 deletions(-)

diff --git a/tcg/tcg.h b/tcg/tcg.h
index 9f9643b470..3fa434d891 100644
--- a/tcg/tcg.h
+++ b/tcg/tcg.h
@@ -1077,6 +1077,8 @@ do {\
 bool tcg_op_supported(TCGOpcode op);
 
 void tcg_gen_callN(void *func, TCGTemp *ret, int nargs, TCGTemp **args);
+void tcg_gen_runtime_helper(const TCGHelperInfo *orig, TCGTemp *ret, int nargs,
+                            TCGTemp **args);
 
 TCGOp *tcg_emit_op(TCGOpcode opc);
 void tcg_op_remove(TCGContext *s, TCGOp *op);
diff --git a/tcg/tcg.c b/tcg/tcg.c
index 08b6926894..87e02da740 100644
--- a/tcg/tcg.c
+++ b/tcg/tcg.c
@@ -1642,15 +1642,13 @@ bool tcg_op_supported(TCGOpcode op)
 /* Note: we convert the 64 bit args to 32 bit and do some alignment
    and endian swap. Maybe it would be better to do the alignment
    and endian swap in tcg_reg_alloc_call(). */
-void tcg_gen_callN(void *func, TCGTemp *ret, int nargs, TCGTemp **args)
+static void do_tcg_gen_callN(TCGHelperInfo *info, TCGTemp *ret, int nargs,
+                             TCGTemp **args)
 {
     int i, real_args, nb_rets, pi;
     unsigned sizemask, flags;
-    TCGHelperInfo *info;
-    uint32_t hash = tcg_helper_func_hash(func);
     TCGOp *op;
 
-    info = qht_lookup_custom(&helper_table, func, hash, tcg_helper_lookup_cmp);
     flags = info->flags;
     sizemask = info->sizemask;
 
@@ -1774,7 +1772,7 @@ void tcg_gen_callN(void *func, TCGTemp *ret, int nargs, TCGTemp **args)
         op->args[pi++] = temp_arg(args[i]);
         real_args++;
     }
-    op->args[pi++] = (uintptr_t)func;
+    op->args[pi++] = (uintptr_t)info->func;
     op->args[pi++] = flags;
     TCGOP_CALLI(op) = real_args;
 
@@ -1812,6 +1810,48 @@ void tcg_gen_callN(void *func, TCGTemp *ret, int nargs, TCGTemp **args)
 #endif /* TCG_TARGET_EXTEND_ARGS */
 }
 
+void tcg_gen_callN(void *func, TCGTemp *ret, int nargs, TCGTemp **args)
+{
+    TCGHelperInfo *info;
+    uint32_t hash = tcg_helper_func_hash(func);
+
+    /*
+     * Here we can get away with tcg_helper_lookup_cmp, which only looks
+     * at the function pointer, since we have the compile-time guarantee
+     * that @func can only be in one TCGHelperInfo.
+     */
+    info = qht_lookup_custom(&helper_table, func, hash, tcg_helper_lookup_cmp);
+    do_tcg_gen_callN(info, ret, nargs, args);
+}
+
+void tcg_gen_runtime_helper(const TCGHelperInfo *orig, TCGTemp *ret, int nargs,
+                            TCGTemp **args)
+{
+    TCGHelperInfo *info;
+    uint32_t hash = tcg_helper_func_hash(orig->func);
+
+    /*
+     * Use the full TCGHelperInfo lookup, since there is no guarantee that func
+     * will be unique to each TCGHelperInfo. For instance, we could have the
+     * same helper function registered in several TCGHelperInfo's, each of them
+     * with different flags.
+     */
+    info = qht_lookup(&helper_table, orig, hash);
+    if (info == NULL) {
+        void *existing = NULL;
+
+        /* @orig might be in the stack, so we need to allocate a new struct */
+        info = g_new(TCGHelperInfo, 1);
+        memcpy(info, orig, sizeof(TCGHelperInfo));
+        qht_insert(&helper_table, info, hash, &existing);
+        if (unlikely(existing)) {
+            g_free(info);
+            info = existing;
+        }
+    }
+    do_tcg_gen_callN(info, ret, nargs, args);
+}
+
 static void tcg_reg_alloc_start(TCGContext *s)
 {
     int i, n;
-- 
2.17.1

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

* [Qemu-devel] [RFC 09/48] tcg: reset runtime helpers when flushing the code cache
  2018-10-25 17:20 [Qemu-devel] [RFC 00/48] Plugin support Emilio G. Cota
                   ` (7 preceding siblings ...)
  2018-10-25 17:20 ` [Qemu-devel] [RFC 08/48] tcg: export tcg_gen_runtime_helper Emilio G. Cota
@ 2018-10-25 17:20 ` Emilio G. Cota
  2018-11-14 17:01   ` Alex Bennée
  2018-10-25 17:20 ` [Qemu-devel] [RFC 10/48] exec: export do_tb_flush Emilio G. Cota
                   ` (39 subsequent siblings)
  48 siblings, 1 reply; 132+ messages in thread
From: Emilio G. Cota @ 2018-10-25 17:20 UTC (permalink / raw)
  To: qemu-devel
  Cc: Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Alex Bennée, Stefan Hajnoczi

In preparation for adding plugin support. One of the clean-up
actions when uninstalling plugins will be to flush the code
cache. We'll also have to clear the runtime helpers, since
some of those runtime helpers may belong to the plugin
being uninstalled.

Signed-off-by: Emilio G. Cota <cota@braap.org>
---
 tcg/tcg.h                 |  1 +
 accel/tcg/translate-all.c |  1 +
 tcg/tcg.c                 | 21 +++++++++++++++++++++
 3 files changed, 23 insertions(+)

diff --git a/tcg/tcg.h b/tcg/tcg.h
index 3fa434d891..2c378415d2 100644
--- a/tcg/tcg.h
+++ b/tcg/tcg.h
@@ -1079,6 +1079,7 @@ bool tcg_op_supported(TCGOpcode op);
 void tcg_gen_callN(void *func, TCGTemp *ret, int nargs, TCGTemp **args);
 void tcg_gen_runtime_helper(const TCGHelperInfo *orig, TCGTemp *ret, int nargs,
                             TCGTemp **args);
+void tcg_reset_runtime_helpers(void);
 
 TCGOp *tcg_emit_op(TCGOpcode opc);
 void tcg_op_remove(TCGContext *s, TCGOp *op);
diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c
index 038d82fdb5..c8b3e0a491 100644
--- a/accel/tcg/translate-all.c
+++ b/accel/tcg/translate-all.c
@@ -1255,6 +1255,7 @@ static void do_tb_flush(CPUState *cpu, run_on_cpu_data tb_flush_count)
 
     qht_reset_size(&tb_ctx.htable, CODE_GEN_HTABLE_SIZE);
     page_flush_tb();
+    tcg_reset_runtime_helpers();
 
     tcg_region_reset_all();
     /* XXX: flush processor icache at this point if cache flush is
diff --git a/tcg/tcg.c b/tcg/tcg.c
index 87e02da740..a6824145b0 100644
--- a/tcg/tcg.c
+++ b/tcg/tcg.c
@@ -874,6 +874,7 @@ static const TCGHelperInfo all_helpers[] = {
 #include "exec/helper-tcg.h"
 };
 static struct qht helper_table;
+static struct qht runtime_helper_table;
 static bool helper_table_inited;
 
 static int indirect_reg_alloc_order[ARRAY_SIZE(tcg_target_reg_alloc_order)];
@@ -913,6 +914,22 @@ static void tcg_helper_insert(const TCGHelperInfo *info)
     g_assert(inserted);
 }
 
+static void
+rm_from_helper_table_and_free(void *p, uint32_t h, void *userp)
+{
+    bool success;
+
+    success = qht_remove(&helper_table, p, h);
+    g_assert(success);
+    g_free(p);
+}
+
+void tcg_reset_runtime_helpers(void)
+{
+    qht_iter(&runtime_helper_table, rm_from_helper_table_and_free, NULL);
+    qht_reset(&runtime_helper_table);
+}
+
 void tcg_context_init(TCGContext *s)
 {
     int op, total_args, n, i;
@@ -948,6 +965,7 @@ void tcg_context_init(TCGContext *s)
     /* Register helpers.  */
     qht_init(&helper_table, tcg_helper_cmp, ARRAY_SIZE(all_helpers),
              QHT_MODE_AUTO_RESIZE);
+    qht_init(&runtime_helper_table, tcg_helper_cmp, 1, QHT_MODE_AUTO_RESIZE);
 
     for (i = 0; i < ARRAY_SIZE(all_helpers); ++i) {
         tcg_helper_insert(&all_helpers[i]);
@@ -1847,6 +1865,9 @@ void tcg_gen_runtime_helper(const TCGHelperInfo *orig, TCGTemp *ret, int nargs,
         if (unlikely(existing)) {
             g_free(info);
             info = existing;
+        } else {
+            qht_insert(&runtime_helper_table, info, hash, &existing);
+            g_assert(existing == NULL);
         }
     }
     do_tcg_gen_callN(info, ret, nargs, args);
-- 
2.17.1

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

* [Qemu-devel] [RFC 10/48] exec: export do_tb_flush
  2018-10-25 17:20 [Qemu-devel] [RFC 00/48] Plugin support Emilio G. Cota
                   ` (8 preceding siblings ...)
  2018-10-25 17:20 ` [Qemu-devel] [RFC 09/48] tcg: reset runtime helpers when flushing the code cache Emilio G. Cota
@ 2018-10-25 17:20 ` Emilio G. Cota
  2018-11-22 17:09   ` Alex Bennée
  2018-10-25 17:20 ` [Qemu-devel] [RFC 11/48] atomic_template: fix indentation in GEN_ATOMIC_HELPER Emilio G. Cota
                   ` (38 subsequent siblings)
  48 siblings, 1 reply; 132+ messages in thread
From: Emilio G. Cota @ 2018-10-25 17:20 UTC (permalink / raw)
  To: qemu-devel
  Cc: Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Alex Bennée, Stefan Hajnoczi

This will be used by plugin code to flush the code cache as well
as doing other bookkeeping in a safe work environment.

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

diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h
index 815e5b1e83..232e2f8966 100644
--- a/include/exec/exec-all.h
+++ b/include/exec/exec-all.h
@@ -427,6 +427,7 @@ void tb_invalidate_phys_range(target_ulong start, target_ulong end);
 void tb_invalidate_phys_addr(AddressSpace *as, hwaddr addr, MemTxAttrs attrs);
 #endif
 void tb_flush(CPUState *cpu);
+void do_tb_flush(CPUState *cpu, run_on_cpu_data tb_flush_count);
 void tb_phys_invalidate(TranslationBlock *tb, tb_page_addr_t page_addr);
 TranslationBlock *tb_htable_lookup(CPUState *cpu, target_ulong pc,
                                    target_ulong cs_base, uint32_t flags,
diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c
index c8b3e0a491..db2d28f8d3 100644
--- a/accel/tcg/translate-all.c
+++ b/accel/tcg/translate-all.c
@@ -1230,7 +1230,7 @@ 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)
+void do_tb_flush(CPUState *cpu, run_on_cpu_data tb_flush_count)
 {
     mmap_lock();
     /* If it is already been done on request of another CPU,
-- 
2.17.1

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

* [Qemu-devel] [RFC 11/48] atomic_template: fix indentation in GEN_ATOMIC_HELPER
  2018-10-25 17:20 [Qemu-devel] [RFC 00/48] Plugin support Emilio G. Cota
                   ` (9 preceding siblings ...)
  2018-10-25 17:20 ` [Qemu-devel] [RFC 10/48] exec: export do_tb_flush Emilio G. Cota
@ 2018-10-25 17:20 ` Emilio G. Cota
  2018-11-22 17:09   ` Alex Bennée
  2018-10-25 17:20 ` [Qemu-devel] [RFC 12/48] atomic_template: define pre/post macros Emilio G. Cota
                   ` (37 subsequent siblings)
  48 siblings, 1 reply; 132+ messages in thread
From: Emilio G. Cota @ 2018-10-25 17:20 UTC (permalink / raw)
  To: qemu-devel
  Cc: Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Alex Bennée, Stefan Hajnoczi

Signed-off-by: Emilio G. Cota <cota@braap.org>
---
 accel/tcg/atomic_template.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

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

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

* [Qemu-devel] [RFC 12/48] atomic_template: define pre/post macros
  2018-10-25 17:20 [Qemu-devel] [RFC 00/48] Plugin support Emilio G. Cota
                   ` (10 preceding siblings ...)
  2018-10-25 17:20 ` [Qemu-devel] [RFC 11/48] atomic_template: fix indentation in GEN_ATOMIC_HELPER Emilio G. Cota
@ 2018-10-25 17:20 ` Emilio G. Cota
  2018-11-22 17:12   ` Alex Bennée
  2018-10-25 17:20 ` [Qemu-devel] [RFC 13/48] xxhash: add qemu_xxhash8 Emilio G. Cota
                   ` (36 subsequent siblings)
  48 siblings, 1 reply; 132+ messages in thread
From: Emilio G. Cota @ 2018-10-25 17:20 UTC (permalink / raw)
  To: qemu-devel
  Cc: Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Alex Bennée, Stefan Hajnoczi

In preparation for plugin support.

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

diff --git a/accel/tcg/atomic_template.h b/accel/tcg/atomic_template.h
index 8d177fefef..b13318c1ce 100644
--- a/accel/tcg/atomic_template.h
+++ b/accel/tcg/atomic_template.h
@@ -59,25 +59,26 @@
 # define ABI_TYPE  uint32_t
 #endif
 
-#define ATOMIC_TRACE_RMW do {                                           \
-        uint8_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, false); \
-                                                                        \
-        trace_guest_mem_before_exec(ENV_GET_CPU(env), addr, info);      \
-        trace_guest_mem_before_exec(ENV_GET_CPU(env), addr,             \
-                                    info | TRACE_MEM_ST);               \
-    } while (0)
-
-#define ATOMIC_TRACE_LD do {                                            \
-        uint8_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, false); \
-                                                                        \
-        trace_guest_mem_before_exec(ENV_GET_CPU(env), addr, info);      \
-    } while (0)
-
-# define ATOMIC_TRACE_ST do {                                           \
-        uint8_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, true); \
-                                                                        \
-        trace_guest_mem_before_exec(ENV_GET_CPU(env), addr, info);      \
-    } while (0)
+/* these don't depend on MEND/SHIFT, so we just define them once */
+#ifndef ATOMIC_TRACE_RMW_PRE
+# define ATOMIC_TRACE_RMW_PRE do {                                            \
+    trace_guest_mem_before_exec(ENV_GET_CPU(env), addr, info);                \
+    trace_guest_mem_before_exec(ENV_GET_CPU(env), addr, info | TRACE_MEM_ST); \
+} while (0)
+
+# define ATOMIC_TRACE_RMW_POST                                          \
+
+# define ATOMIC_TRACE_LD_PRE                                    \
+    trace_guest_mem_before_exec(ENV_GET_CPU(env), addr, info)
+
+# define ATOMIC_TRACE_LD_POST                                           \
+
+# define ATOMIC_TRACE_ST_PRE                                    \
+    trace_guest_mem_before_exec(ENV_GET_CPU(env), addr, info)
+
+# define ATOMIC_TRACE_ST_POST                                           \
+
+#endif /* ATOMIC_TRACE_RMW_PRE */
 
 /* Define host-endian atomic operations.  Note that END is used within
    the ATOMIC_NAME macro, and redefined below.  */
@@ -98,14 +99,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;
 #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;
     return ret;
 }
 
@@ -115,10 +118,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;
     val = atomic16_read(haddr);
     ATOMIC_MMU_CLEANUP;
+    ATOMIC_TRACE_LD_POST;
     return val;
 }
 
@@ -127,10 +132,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;
     atomic16_set(haddr, val);
     ATOMIC_MMU_CLEANUP;
+    ATOMIC_TRACE_ST_POST;
 }
 #endif
 #else
@@ -140,10 +147,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;
     ret = atomic_xchg__nocheck(haddr, val);
     ATOMIC_MMU_CLEANUP;
+    ATOMIC_TRACE_RMW_POST;
     return ret;
 }
 
@@ -154,10 +163,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;                                           \
     ret = atomic_##X(haddr, val);                                   \
     ATOMIC_MMU_CLEANUP;                                             \
+    ATOMIC_TRACE_RMW_POST;                                          \
     return ret;                                                     \
 }
 
@@ -186,8 +197,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;                                           \
     smp_mb();                                                       \
     cmp = atomic_read__nocheck(haddr);                              \
     do {                                                            \
@@ -195,6 +207,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;                                          \
     return RET;                                                     \
 }
 
@@ -232,14 +245,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;
 #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;
     return BSWAP(ret);
 }
 
@@ -249,10 +264,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;
     val = atomic16_read(haddr);
     ATOMIC_MMU_CLEANUP;
+    ATOMIC_TRACE_LD_POST;
     return BSWAP(val);
 }
 
@@ -261,11 +278,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;
     val = BSWAP(val);
     atomic16_set(haddr, val);
     ATOMIC_MMU_CLEANUP;
+    ATOMIC_TRACE_ST_POST;
 }
 #endif
 #else
@@ -275,10 +295,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;
     ret = atomic_xchg__nocheck(haddr, BSWAP(val));
     ATOMIC_MMU_CLEANUP;
+    ATOMIC_TRACE_RMW_POST;
     return BSWAP(ret);
 }
 
@@ -289,10 +311,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;                                           \
     ret = atomic_##X(haddr, BSWAP(val));                            \
     ATOMIC_MMU_CLEANUP;                                             \
+    ATOMIC_TRACE_RMW_POST;                                          \
     return BSWAP(ret);                                              \
 }
 
@@ -319,8 +343,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;                                           \
     smp_mb();                                                       \
     ldn = atomic_read__nocheck(haddr);                              \
     do {                                                            \
@@ -328,6 +353,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;                                          \
     return RET;                                                     \
 }
 
@@ -355,10 +381,6 @@ GEN_ATOMIC_HELPER_FN(add_fetch, ADD, DATA_TYPE, new)
 #undef MEND
 #endif /* DATA_SIZE > 1 */
 
-#undef ATOMIC_TRACE_ST
-#undef ATOMIC_TRACE_LD
-#undef ATOMIC_TRACE_RMW
-
 #undef BSWAP
 #undef ABI_TYPE
 #undef DATA_TYPE
-- 
2.17.1

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

* [Qemu-devel] [RFC 13/48] xxhash: add qemu_xxhash8
  2018-10-25 17:20 [Qemu-devel] [RFC 00/48] Plugin support Emilio G. Cota
                   ` (11 preceding siblings ...)
  2018-10-25 17:20 ` [Qemu-devel] [RFC 12/48] atomic_template: define pre/post macros Emilio G. Cota
@ 2018-10-25 17:20 ` Emilio G. Cota
  2018-11-22 17:15   ` Alex Bennée
  2018-10-25 17:20 ` [Qemu-devel] [RFC 14/48] plugin: preliminary user-facing API Emilio G. Cota
                   ` (35 subsequent siblings)
  48 siblings, 1 reply; 132+ messages in thread
From: Emilio G. Cota @ 2018-10-25 17:20 UTC (permalink / raw)
  To: qemu-devel
  Cc: Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Alex Bennée, Stefan Hajnoczi

It will be used for TB hashing soon.

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

diff --git a/include/qemu/xxhash.h b/include/qemu/xxhash.h
index fe35dde328..450427eeaa 100644
--- a/include/qemu/xxhash.h
+++ b/include/qemu/xxhash.h
@@ -49,7 +49,8 @@
  * contiguous in memory.
  */
 static inline uint32_t
-qemu_xxhash7(uint64_t ab, uint64_t cd, uint32_t e, uint32_t f, uint32_t g)
+qemu_xxhash8(uint64_t ab, uint64_t cd, uint32_t e, uint32_t f, uint32_t g,
+             uint32_t h)
 {
     uint32_t v1 = QEMU_XXHASH_SEED + PRIME32_1 + PRIME32_2;
     uint32_t v2 = QEMU_XXHASH_SEED + PRIME32_2;
@@ -77,17 +78,24 @@ qemu_xxhash7(uint64_t ab, uint64_t cd, uint32_t e, uint32_t f, uint32_t g)
     v4 = rol32(v4, 13);
     v4 *= PRIME32_1;
 
-    h32 = rol32(v1, 1) + rol32(v2, 7) + rol32(v3, 12) + rol32(v4, 18);
-    h32 += 28;
+    v1 += e * PRIME32_2;
+    v1 = rol32(v1, 13);
+    v1 *= PRIME32_1;
 
-    h32 += e * PRIME32_3;
-    h32  = rol32(h32, 17) * PRIME32_4;
+    v2 += f * PRIME32_2;
+    v2 = rol32(v2, 13);
+    v2 *= PRIME32_1;
+
+    v3 += g * PRIME32_2;
+    v3 = rol32(v3, 13);
+    v3 *= PRIME32_1;
 
-    h32 += f * PRIME32_3;
-    h32  = rol32(h32, 17) * PRIME32_4;
+    v4 += h * PRIME32_2;
+    v4 = rol32(v4, 13);
+    v4 *= PRIME32_1;
 
-    h32 += g * PRIME32_3;
-    h32  = rol32(h32, 17) * PRIME32_4;
+    h32 = rol32(v1, 1) + rol32(v2, 7) + rol32(v3, 12) + rol32(v4, 18);
+    h32 += 32;
 
     h32 ^= h32 >> 15;
     h32 *= PRIME32_2;
@@ -100,23 +108,29 @@ qemu_xxhash7(uint64_t ab, uint64_t cd, uint32_t e, uint32_t f, uint32_t g)
 
 static inline uint32_t qemu_xxhash2(uint64_t ab)
 {
-    return qemu_xxhash7(ab, 0, 0, 0, 0);
+    return qemu_xxhash8(ab, 0, 0, 0, 0, 0);
 }
 
 static inline uint32_t qemu_xxhash4(uint64_t ab, uint64_t cd)
 {
-    return qemu_xxhash7(ab, cd, 0, 0, 0);
+    return qemu_xxhash8(ab, cd, 0, 0, 0, 0);
 }
 
 static inline uint32_t qemu_xxhash5(uint64_t ab, uint64_t cd, uint32_t e)
 {
-    return qemu_xxhash7(ab, cd, e, 0, 0);
+    return qemu_xxhash8(ab, cd, e, 0, 0, 0);
 }
 
 static inline uint32_t qemu_xxhash6(uint64_t ab, uint64_t cd, uint32_t e,
                                     uint32_t f)
 {
-    return qemu_xxhash7(ab, cd, e, f, 0);
+    return qemu_xxhash8(ab, cd, e, f, 0, 0);
+}
+
+static inline uint32_t qemu_xxhash7(uint64_t ab, uint64_t cd, uint32_t e,
+                                    uint32_t f, uint32_t g)
+{
+    return qemu_xxhash8(ab, cd, e, f, g, 0);
 }
 
 #endif /* QEMU_XXHASH_H */
-- 
2.17.1

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

* [Qemu-devel] [RFC 14/48] plugin: preliminary user-facing API
  2018-10-25 17:20 [Qemu-devel] [RFC 00/48] Plugin support Emilio G. Cota
                   ` (12 preceding siblings ...)
  2018-10-25 17:20 ` [Qemu-devel] [RFC 13/48] xxhash: add qemu_xxhash8 Emilio G. Cota
@ 2018-10-25 17:20 ` Emilio G. Cota
  2018-10-25 17:20 ` [Qemu-devel] [RFC 15/48] plugin: add core code Emilio G. Cota
                   ` (34 subsequent siblings)
  48 siblings, 0 replies; 132+ messages in thread
From: Emilio G. Cota @ 2018-10-25 17:20 UTC (permalink / raw)
  To: qemu-devel
  Cc: Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Alex Bennée, Stefan Hajnoczi

Add the API first to ease review.

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

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

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

* [Qemu-devel] [RFC 15/48] plugin: add core code
  2018-10-25 17:20 [Qemu-devel] [RFC 00/48] Plugin support Emilio G. Cota
                   ` (13 preceding siblings ...)
  2018-10-25 17:20 ` [Qemu-devel] [RFC 14/48] plugin: preliminary user-facing API Emilio G. Cota
@ 2018-10-25 17:20 ` Emilio G. Cota
  2018-10-25 17:20 ` [Qemu-devel] [RFC 16/48] tcg: add plugin_mask to TB hash Emilio G. Cota
                   ` (33 subsequent siblings)
  48 siblings, 0 replies; 132+ messages in thread
From: Emilio G. Cota @ 2018-10-25 17:20 UTC (permalink / raw)
  To: qemu-devel
  Cc: Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Alex Bennée, Stefan Hajnoczi

The goals are to:
- Have a simple implementation that shares nothing with tracing code.

- Make sure we cannot deadlock, particularly under MTTCG. For this,
  we acquire a lock when called from plugin code, and keep
  RCU lists of callbacks so that we do not have to hold the lock
  when calling the callbacks. This is also for performance, since
  some callbacks (e.g. memory access callbacks) might be called very
  frequently.
  * A consequence of this is that we keep our own list of CPUs, so that
    we do not have to worry about locking order wrt cpu_list_lock.
  * Some functions such as vcpu_for_each will call plugin code with the
    lock held. Instead of making the lock recursive, just document that
    the function called by vcpu_for_each cannot call any other plugin API.

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

- Support the installation/uninstallation of a plugin any time (i.e. from
  any callback from the guest, or from QAPI/QMP).

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

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

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

- Using a per-vcpu mask is key to allow for maximum performance/scalability.
  With RCU callbacks and async work we can ensure no events are missed, and
  we keep maximum scalability (again, we assume callbacks are a lot more
  common than callback registration).

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

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

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

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

* [Qemu-devel] [RFC 16/48] tcg: add plugin_mask to TB hash
  2018-10-25 17:20 [Qemu-devel] [RFC 00/48] Plugin support Emilio G. Cota
                   ` (14 preceding siblings ...)
  2018-10-25 17:20 ` [Qemu-devel] [RFC 15/48] plugin: add core code Emilio G. Cota
@ 2018-10-25 17:20 ` Emilio G. Cota
  2018-11-23 16:52   ` Alex Bennée
  2018-10-25 17:20 ` [Qemu-devel] [RFC 17/48] plugin-gen: add TCG code generation helpers Emilio G. Cota
                   ` (32 subsequent siblings)
  48 siblings, 1 reply; 132+ messages in thread
From: Emilio G. Cota @ 2018-10-25 17:20 UTC (permalink / raw)
  To: qemu-devel
  Cc: Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Alex Bennée, Stefan Hajnoczi

Signed-off-by: Emilio G. Cota <cota@braap.org>
---
 include/exec/exec-all.h   | 2 ++
 include/exec/tb-hash.h    | 6 ++++--
 include/exec/tb-lookup.h  | 1 +
 accel/tcg/cpu-exec.c      | 6 +++++-
 accel/tcg/translate-all.c | 6 ++++--
 5 files changed, 16 insertions(+), 5 deletions(-)

diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h
index 232e2f8966..a1f60404b6 100644
--- a/include/exec/exec-all.h
+++ b/include/exec/exec-all.h
@@ -358,6 +358,8 @@ struct TranslationBlock {
     /* Per-vCPU dynamic tracing state used to generate this TB */
     uint32_t trace_vcpu_dstate;
 
+    uint32_t plugin_mask;
+
     struct tb_tc tc;
 
     /* original tb when cflags has CF_NOCACHE */
diff --git a/include/exec/tb-hash.h b/include/exec/tb-hash.h
index 4f3a37d927..37292474b7 100644
--- a/include/exec/tb-hash.h
+++ b/include/exec/tb-hash.h
@@ -59,9 +59,11 @@ static inline unsigned int tb_jmp_cache_hash_func(target_ulong pc)
 
 static inline
 uint32_t tb_hash_func(tb_page_addr_t phys_pc, target_ulong pc, uint32_t flags,
-                      uint32_t cf_mask, uint32_t trace_vcpu_dstate)
+                      uint32_t cf_mask, uint32_t trace_vcpu_dstate,
+                      uint32_t plugin_mask)
 {
-    return qemu_xxhash7(phys_pc, pc, flags, cf_mask, trace_vcpu_dstate);
+    return qemu_xxhash8(phys_pc, pc, flags, cf_mask, trace_vcpu_dstate,
+                        plugin_mask);
 }
 
 #endif
diff --git a/include/exec/tb-lookup.h b/include/exec/tb-lookup.h
index 492cb68289..dd1572f481 100644
--- a/include/exec/tb-lookup.h
+++ b/include/exec/tb-lookup.h
@@ -33,6 +33,7 @@ tb_lookup__cpu_state(CPUState *cpu, target_ulong *pc, target_ulong *cs_base,
                tb->cs_base == *cs_base &&
                tb->flags == *flags &&
                tb->trace_vcpu_dstate == *cpu->trace_dstate &&
+               tb->plugin_mask == *cpu->plugin_mask &&
                (tb_cflags(tb) & (CF_HASH_MASK | CF_INVALID)) == cf_mask)) {
         return tb;
     }
diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c
index d590f1f6c0..27aa3451da 100644
--- a/accel/tcg/cpu-exec.c
+++ b/accel/tcg/cpu-exec.c
@@ -287,6 +287,7 @@ struct tb_desc {
     uint32_t flags;
     uint32_t cf_mask;
     uint32_t trace_vcpu_dstate;
+    uint32_t plugin_mask;
 };
 
 static bool tb_lookup_cmp(const void *p, const void *d)
@@ -299,6 +300,7 @@ static bool tb_lookup_cmp(const void *p, const void *d)
         tb->cs_base == desc->cs_base &&
         tb->flags == desc->flags &&
         tb->trace_vcpu_dstate == desc->trace_vcpu_dstate &&
+        tb->plugin_mask == desc->plugin_mask &&
         (tb_cflags(tb) & (CF_HASH_MASK | CF_INVALID)) == desc->cf_mask) {
         /* check next page if needed */
         if (tb->page_addr[1] == -1) {
@@ -330,13 +332,15 @@ TranslationBlock *tb_htable_lookup(CPUState *cpu, target_ulong pc,
     desc.flags = flags;
     desc.cf_mask = cf_mask;
     desc.trace_vcpu_dstate = *cpu->trace_dstate;
+    desc.plugin_mask = *cpu->plugin_mask;
     desc.pc = pc;
     phys_pc = get_page_addr_code(desc.env, pc);
     if (phys_pc == -1) {
         return NULL;
     }
     desc.phys_page1 = phys_pc & TARGET_PAGE_MASK;
-    h = tb_hash_func(phys_pc, pc, flags, cf_mask, *cpu->trace_dstate);
+    h = tb_hash_func(phys_pc, pc, flags, cf_mask, *cpu->trace_dstate,
+                     *cpu->plugin_mask);
     return qht_lookup_custom(&tb_ctx.htable, &desc, h, tb_lookup_cmp);
 }
 
diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c
index db2d28f8d3..3423cf74db 100644
--- a/accel/tcg/translate-all.c
+++ b/accel/tcg/translate-all.c
@@ -1129,6 +1129,7 @@ static bool tb_cmp(const void *ap, const void *bp)
         a->flags == b->flags &&
         (tb_cflags(a) & CF_HASH_MASK) == (tb_cflags(b) & CF_HASH_MASK) &&
         a->trace_vcpu_dstate == b->trace_vcpu_dstate &&
+        a->plugin_mask == b->plugin_mask &&
         a->page_addr[0] == b->page_addr[0] &&
         a->page_addr[1] == b->page_addr[1];
 }
@@ -1444,7 +1445,7 @@ static void do_tb_phys_invalidate(TranslationBlock *tb, bool rm_from_page_list)
     /* remove the TB from the hash list */
     phys_pc = tb->page_addr[0] + (tb->pc & ~TARGET_PAGE_MASK);
     h = tb_hash_func(phys_pc, tb->pc, tb->flags, tb_cflags(tb) & CF_HASH_MASK,
-                     tb->trace_vcpu_dstate);
+                     tb->trace_vcpu_dstate, tb->plugin_mask);
     if (!(tb->cflags & CF_NOCACHE) &&
         !qht_remove(&tb_ctx.htable, tb, h)) {
         return;
@@ -1640,7 +1641,7 @@ tb_link_page(TranslationBlock *tb, tb_page_addr_t phys_pc,
 
         /* add in the hash table */
         h = tb_hash_func(phys_pc, tb->pc, tb->flags, tb->cflags & CF_HASH_MASK,
-                         tb->trace_vcpu_dstate);
+                         tb->trace_vcpu_dstate, tb->plugin_mask);
         qht_insert(&tb_ctx.htable, tb, h, &existing_tb);
 
         /* remove TB from the page(s) if we couldn't insert it */
@@ -1712,6 +1713,7 @@ TranslationBlock *tb_gen_code(CPUState *cpu,
     tb->cflags = cflags;
     tb->trace_vcpu_dstate = *cpu->trace_dstate;
     tcg_ctx->tb_cflags = cflags;
+    tb->plugin_mask = *cpu->plugin_mask;
 
 #ifdef CONFIG_PROFILER
     /* includes aborted translations because of exceptions */
-- 
2.17.1

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

* [Qemu-devel] [RFC 17/48] plugin-gen: add TCG code generation helpers
  2018-10-25 17:20 [Qemu-devel] [RFC 00/48] Plugin support Emilio G. Cota
                   ` (15 preceding siblings ...)
  2018-10-25 17:20 ` [Qemu-devel] [RFC 16/48] tcg: add plugin_mask to TB hash Emilio G. Cota
@ 2018-10-25 17:20 ` Emilio G. Cota
  2018-10-25 17:20 ` [Qemu-devel] [RFC 18/48] tcg: add memory callbacks for plugins (WIP) Emilio G. Cota
                   ` (31 subsequent siblings)
  48 siblings, 0 replies; 132+ messages in thread
From: Emilio G. Cota @ 2018-10-25 17:20 UTC (permalink / raw)
  To: qemu-devel
  Cc: Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Alex Bennée, Stefan Hajnoczi

Here we implement several features:

- Inlining TCG code for simple operations. Crucially, 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 comment.

Signed-off-by: Emilio G. Cota <cota@braap.org>
---
 include/exec/plugin-gen.h |  51 +++++++++
 accel/tcg/plugin-gen.c    | 230 ++++++++++++++++++++++++++++++++++++++
 accel/tcg/Makefile.objs   |   1 +
 3 files changed, 282 insertions(+)
 create mode 100644 include/exec/plugin-gen.h
 create mode 100644 accel/tcg/plugin-gen.c

diff --git a/include/exec/plugin-gen.h b/include/exec/plugin-gen.h
new file mode 100644
index 0000000000..46a167527e
--- /dev/null
+++ b/include/exec/plugin-gen.h
@@ -0,0 +1,51 @@
+/*
+ * 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"
+
+#ifdef CONFIG_PLUGINS
+
+void qemu_plugin_gen_vcpu_mem_callbacks(struct qemu_plugin_dyn_cb_arr *arr,
+                                        TCGv vaddr, uint8_t info);
+
+void qemu_plugin_gen_vcpu_udata_callbacks(struct qemu_plugin_dyn_cb_arr *arr);
+
+void qemu_plugin_gen_disable_mem_helpers(void);
+
+void
+qemu_plugin_gen_enable_mem_helpers(const struct qemu_plugin_dyn_cb_arr *orig);
+
+#else /* !CONFIG_PLUGINS */
+
+static inline void
+qemu_plugin_gen_vcpu_mem_callbacks(struct qemu_plugin_dyn_cb_arr *arr,
+                                   TCGv vaddr, uint8_t info)
+{ }
+
+static inline void
+qemu_plugin_gen_vcpu_udata_callbacks(struct qemu_plugin_dyn_cb_arr *arr)
+{ }
+
+static inline void qemu_plugin_gen_disable_mem_helpers(void)
+{ }
+
+static inline void
+qemu_plugin_gen_enable_mem_helpers(const struct qemu_plugin_dyn_cb_arr *orig)
+{ }
+
+#endif /* CONFIG_PLUGINS */
+
+#endif /* QEMU_PLUGIN_GEN_H */
+
diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c
new file mode 100644
index 0000000000..75f182be37
--- /dev/null
+++ b/accel/tcg/plugin-gen.c
@@ -0,0 +1,230 @@
+#include "qemu/osdep.h"
+#include "cpu.h"
+#include "tcg/tcg.h"
+#include "tcg/tcg-op.h"
+#include "exec/exec-all.h"
+#include "exec/plugin-gen.h"
+
+static void gen_inline_op(struct qemu_plugin_dyn_cb *cb)
+{
+    TCGv_i64 val = tcg_temp_new_i64();
+    TCGv_ptr ptr = tcg_const_ptr(cb->userp);
+
+    tcg_gen_ld_i64(val, ptr, 0);
+    switch (cb->inline_insn.op) {
+    case QEMU_PLUGIN_INLINE_ADD_U64:
+        tcg_gen_addi_i64(val, val, cb->inline_insn.imm);
+        break;
+    default:
+        g_assert_not_reached();
+    }
+    tcg_gen_st_i64(val, ptr, 0);
+
+    tcg_temp_free_ptr(ptr);
+    tcg_temp_free_i64(val);
+}
+
+static void gen_helper_mem_cb(const char *name, unsigned flags,
+                              qemu_plugin_vcpu_mem_cb_t cb, TCGv_i32 cpu_index,
+                              TCGv_i32 meminfo, TCGv_i64 vaddr, TCGv_ptr udata)
+{
+    TCGHelperInfo info = {
+        .func = cb,
+        .name = name,
+        .flags = flags,
+        .sizemask = dh_sizemask(void, 0) |
+                    dh_sizemask(i32, 1) |
+                    dh_sizemask(i32, 2) |
+                    dh_sizemask(i64, 3) |
+                    dh_sizemask(ptr, 4),
+
+    };
+    TCGTemp *args[] = {
+        tcgv_i32_temp(cpu_index),
+        tcgv_i32_temp(meminfo),
+        tcgv_i64_temp(vaddr),
+        tcgv_ptr_temp(udata),
+    };
+
+    tcg_gen_runtime_helper(&info, NULL, ARRAY_SIZE(args), args);
+}
+
+static void gen_helper_mem_haddr_cb(const char *name, unsigned flags,
+                                    qemu_plugin_vcpu_mem_haddr_cb_t cb,
+                                    TCGv_i32 cpu_index, TCGv_i32 meminfo,
+                                    TCGv_i64 vaddr, TCGv_ptr haddr,
+                                    TCGv_ptr udata)
+{
+    TCGHelperInfo info = {
+        .func = cb,
+        .name = name,
+        .flags = flags,
+        .sizemask = dh_sizemask(void, 0) |
+                    dh_sizemask(i32, 1) |
+                    dh_sizemask(i32, 2) |
+                    dh_sizemask(i64, 3) |
+                    dh_sizemask(ptr, 4) |
+                    dh_sizemask(ptr, 5),
+
+    };
+    TCGTemp *args[] = {
+        tcgv_i32_temp(cpu_index),
+        tcgv_i32_temp(meminfo),
+        tcgv_i64_temp(vaddr),
+        tcgv_ptr_temp(haddr),
+        tcgv_ptr_temp(udata),
+    };
+
+    tcg_gen_runtime_helper(&info, NULL, ARRAY_SIZE(args), args);
+}
+
+static void gen_mem_cb(struct qemu_plugin_dyn_cb *cb, TCGv vaddr, uint8_t info)
+{
+    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(cb->userp);
+    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 (cb->mem.haddr) {
+#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_mem_haddr_cb("helper_plugin_vcpu_mem_haddr_cb",
+                                cb->tcg_flags, cb->f.vcpu_mem_haddr,
+                                cpu_index, meminfo, vaddr64, haddr, udata);
+        tcg_temp_free_ptr(haddr);
+    } else {
+        gen_helper_mem_cb("helper_plugin_vcpu_mem_cb", cb->tcg_flags,
+                          cb->f.vcpu_mem, 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);
+}
+
+void qemu_plugin_gen_vcpu_mem_callbacks(struct qemu_plugin_dyn_cb_arr *arr,
+                                        TCGv vaddr, uint8_t info)
+{
+    size_t i;
+
+    for (i = 0; i < arr->n; i++) {
+        struct qemu_plugin_dyn_cb *cb = &arr->data[i];
+
+        switch (cb->type) {
+        case QEMU_PLUGIN_DYN_CB_TYPE_REGULAR:
+            gen_mem_cb(cb, vaddr, info);
+            break;
+        case QEMU_PLUGIN_DYN_CB_TYPE_INLINE:
+            gen_inline_op(cb);
+            break;
+        default:
+            g_assert_not_reached();
+        }
+    }
+}
+
+static void gen_helper_vcpu_udata_cb(const char *name, unsigned flags,
+                                     qemu_plugin_vcpu_udata_cb_t cb,
+                                     TCGv_i32 cpu_index, TCGv_ptr udata)
+{
+    TCGHelperInfo info = {
+        .func = cb,
+        .name = name,
+        .flags = flags,
+        .sizemask = dh_sizemask(void, 0) |
+                    dh_sizemask(i32, 1) |
+                    dh_sizemask(ptr, 2),
+    };
+    TCGTemp *args[] = {
+        tcgv_i32_temp(cpu_index),
+        tcgv_ptr_temp(udata),
+    };
+
+    tcg_gen_runtime_helper(&info, NULL, ARRAY_SIZE(args), args);
+}
+
+static void gen_vcpu_udata_cb(struct qemu_plugin_dyn_cb *cb)
+{
+    TCGv_i32 cpu_index = tcg_temp_new_i32();
+    TCGv_ptr udata = tcg_const_ptr(cb->userp);
+
+    tcg_gen_ld_i32(cpu_index, cpu_env,
+                   -ENV_OFFSET + offsetof(CPUState, cpu_index));
+
+    gen_helper_vcpu_udata_cb("helper_plugin_vcpu_udata_cb", cb->tcg_flags,
+                             cb->f.vcpu_udata, cpu_index, udata);
+
+    tcg_temp_free_ptr(udata);
+    tcg_temp_free_i32(cpu_index);
+}
+
+void qemu_plugin_gen_vcpu_udata_callbacks(struct qemu_plugin_dyn_cb_arr *arr)
+{
+    size_t i;
+
+    for (i = 0; i < arr->n; i++) {
+        struct qemu_plugin_dyn_cb *cb = &arr->data[i];
+
+        switch (cb->type) {
+        case QEMU_PLUGIN_DYN_CB_TYPE_REGULAR:
+            gen_vcpu_udata_cb(cb);
+            break;
+        case QEMU_PLUGIN_DYN_CB_TYPE_INLINE:
+            gen_inline_op(cb);
+            break;
+        default:
+            g_assert_not_reached();
+        }
+    }
+}
+
+/*
+ * Tracking memory accesses performed from helpers requires extra work.
+ * If an instruction is emulated with helpers, struct qemu_plugin_insn's
+ * .calls_helpers is set. If so, this function is called. Here we do two
+ * things: (1) copy the CB descriptor, and keep track of it so that it can be
+ * freed later on, and (2) point CPUState.plugin_mem_cbs to the descriptor, so
+ * that we can read it at run-time (i.e. when the helper executes).
+ * This run-time access is performed from qemu_plugin_vcpu_mem_cb.
+ *
+ * Note that qemu_plugin_gen_disable_mem_helpers undoes (2).
+ */
+void
+qemu_plugin_gen_enable_mem_helpers(const struct qemu_plugin_dyn_cb_arr *orig)
+{
+    struct qemu_plugin_dyn_cb_arr *arr;
+    TCGv_ptr ptr;
+
+    arr = g_new(struct qemu_plugin_dyn_cb_arr, 1);
+    arr->capacity = orig->n;
+    arr->n = orig->n;
+    arr->data = g_new(struct qemu_plugin_dyn_cb, arr->n);
+    memcpy(arr->data, orig->data, sizeof(*arr->data) * arr->n);
+    qemu_plugin_add_dyn_cb_arr(arr);
+
+    ptr = tcg_const_ptr(arr);
+    tcg_gen_st_ptr(ptr, cpu_env, -ENV_OFFSET + offsetof(CPUState,
+                                                        plugin_mem_cbs));
+    tcg_temp_free_ptr(ptr);
+}
+
+/* Called once we're done instrumenting an instruction that calls helpers */
+void qemu_plugin_gen_disable_mem_helpers(void)
+{
+    TCGv_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);
+}
diff --git a/accel/tcg/Makefile.objs b/accel/tcg/Makefile.objs
index d381a02f34..4f617e4fe5 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_PLUGINS) += plugin-gen.o
-- 
2.17.1

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

* [Qemu-devel] [RFC 18/48] tcg: add memory callbacks for plugins (WIP)
  2018-10-25 17:20 [Qemu-devel] [RFC 00/48] Plugin support Emilio G. Cota
                   ` (16 preceding siblings ...)
  2018-10-25 17:20 ` [Qemu-devel] [RFC 17/48] plugin-gen: add TCG code generation helpers Emilio G. Cota
@ 2018-10-25 17:20 ` Emilio G. Cota
  2018-11-23 16:55   ` Alex Bennée
  2018-10-25 17:20 ` [Qemu-devel] [RFC 19/48] translate-all: notify plugin code of tb_flush Emilio G. Cota
                   ` (30 subsequent siblings)
  48 siblings, 1 reply; 132+ messages in thread
From: Emilio G. Cota @ 2018-10-25 17:20 UTC (permalink / raw)
  To: qemu-devel
  Cc: Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Alex Bennée, Stefan Hajnoczi

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

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

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

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

Signed-off-by: Emilio G. Cota <cota@braap.org>
---
 accel/tcg/atomic_template.h               |  8 ++++-
 accel/tcg/softmmu_template.h              | 39 ++++++++++++++++++++
 include/exec/cpu-defs.h                   |  2 ++
 include/exec/cpu_ldst_template.h          | 43 +++++++++++++++--------
 include/exec/cpu_ldst_useronly_template.h | 42 +++++++++++++++-------
 tcg/tcg-op.h                              |  5 +++
 tcg/tcg.h                                 |  4 +++
 tcg/i386/tcg-target.inc.c                 |  5 +++
 tcg/tcg-op.c                              | 37 ++++++++++++++-----
 tcg/tcg.c                                 |  3 ++
 10 files changed, 152 insertions(+), 36 deletions(-)

diff --git a/accel/tcg/atomic_template.h b/accel/tcg/atomic_template.h
index b13318c1ce..3de34dc462 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
@@ -66,17 +67,22 @@
     trace_guest_mem_before_exec(ENV_GET_CPU(env), addr, info | TRACE_MEM_ST); \
 } while (0)
 
-# define ATOMIC_TRACE_RMW_POST                                          \
+# define ATOMIC_TRACE_RMW_POST do {                                            \
+  qemu_plugin_vcpu_mem_cb(ENV_GET_CPU(env), addr, haddr, info);                \
+  qemu_plugin_vcpu_mem_cb(ENV_GET_CPU(env), addr, haddr, info | TRACE_MEM_ST); \
+} while (0)
 
 # define ATOMIC_TRACE_LD_PRE                                    \
     trace_guest_mem_before_exec(ENV_GET_CPU(env), addr, info)
 
 # define ATOMIC_TRACE_LD_POST                                           \
+    qemu_plugin_vcpu_mem_cb(ENV_GET_CPU(env), addr, haddr, info)
 
 # define ATOMIC_TRACE_ST_PRE                                    \
     trace_guest_mem_before_exec(ENV_GET_CPU(env), addr, info)
 
 # define ATOMIC_TRACE_ST_POST                                           \
+    qemu_plugin_vcpu_mem_cb(ENV_GET_CPU(env), addr, haddr, info)
 
 #endif /* ATOMIC_TRACE_RMW_PRE */
 
diff --git a/accel/tcg/softmmu_template.h b/accel/tcg/softmmu_template.h
index b0adea045e..f6d2f60b81 100644
--- a/accel/tcg/softmmu_template.h
+++ b/accel/tcg/softmmu_template.h
@@ -103,6 +103,11 @@ static inline DATA_TYPE glue(io_read, SUFFIX)(CPUArchState *env,
                                               MMUAccessType access_type)
 {
     CPUIOTLBEntry *iotlbentry = &env->iotlb[mmu_idx][index];
+
+    /* XXX Any sensible choice other than NULL? */
+    if (tcg_ctx->plugin_mem_cb) {
+        env->hostaddr = NULL;
+    }
     return io_readx(env, iotlbentry, mmu_idx, addr, retaddr, recheck,
                     access_type, DATA_SIZE);
 }
@@ -162,12 +167,23 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr,
         res2 = helper_le_ld_name(env, addr2, oi, retaddr);
         shift = (addr & (DATA_SIZE - 1)) * 8;
 
+        /*
+         * XXX cross-page accesses would have to be split into separate accesses
+         * for the host address to make sense. For now, just return NULL.
+         */
+        if (tcg_ctx->plugin_mem_cb) {
+            env->hostaddr = NULL;
+        }
+
         /* Little-endian combine.  */
         res = (res1 >> shift) | (res2 << ((DATA_SIZE * 8) - shift));
         return res;
     }
 
     haddr = addr + entry->addend;
+    if (tcg_ctx->plugin_mem_cb) {
+        env->hostaddr = (void *)haddr;
+    }
 #if DATA_SIZE == 1
     res = glue(glue(ld, LSUFFIX), _p)((uint8_t *)haddr);
 #else
@@ -231,12 +247,19 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr,
         res2 = helper_be_ld_name(env, addr2, oi, retaddr);
         shift = (addr & (DATA_SIZE - 1)) * 8;
 
+        if (tcg_ctx->plugin_mem_cb) {
+            env->hostaddr = NULL;
+        }
+
         /* Big-endian combine.  */
         res = (res1 << shift) | (res2 >> ((DATA_SIZE * 8) - shift));
         return res;
     }
 
     haddr = addr + entry->addend;
+    if (tcg_ctx->plugin_mem_cb) {
+        env->hostaddr = (void *)haddr;
+    }
     res = glue(glue(ld, LSUFFIX), _be_p)((uint8_t *)haddr);
     return res;
 }
@@ -270,6 +293,10 @@ static inline void glue(io_write, SUFFIX)(CPUArchState *env,
                                           bool recheck)
 {
     CPUIOTLBEntry *iotlbentry = &env->iotlb[mmu_idx][index];
+
+    if (tcg_ctx->plugin_mem_cb) {
+        env->hostaddr = NULL;
+    }
     return io_writex(env, iotlbentry, mmu_idx, val, addr, retaddr,
                      recheck, DATA_SIZE);
 }
@@ -340,10 +367,16 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
             glue(helper_ret_stb, MMUSUFFIX)(env, addr + i, val8,
                                             oi, retaddr);
         }
+        if (tcg_ctx->plugin_mem_cb) {
+            env->hostaddr = NULL;
+        }
         return;
     }
 
     haddr = addr + entry->addend;
+    if (tcg_ctx->plugin_mem_cb) {
+        env->hostaddr = (void *)haddr;
+    }
 #if DATA_SIZE == 1
     glue(glue(st, SUFFIX), _p)((uint8_t *)haddr, val);
 #else
@@ -418,10 +451,16 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
             glue(helper_ret_stb, MMUSUFFIX)(env, addr + i, val8,
                                             oi, retaddr);
         }
+        if (tcg_ctx->plugin_mem_cb) {
+            env->hostaddr = NULL;
+        }
         return;
     }
 
     haddr = addr + entry->addend;
+    if (tcg_ctx->plugin_mem_cb) {
+        env->hostaddr = (void *)haddr;
+    }
     glue(glue(st, SUFFIX), _be_p)((uint8_t *)haddr, val);
 }
 #endif /* DATA_SIZE > 1 */
diff --git a/include/exec/cpu-defs.h b/include/exec/cpu-defs.h
index 40cd5d4774..c92c24a4a3 100644
--- a/include/exec/cpu-defs.h
+++ b/include/exec/cpu-defs.h
@@ -186,6 +186,8 @@ typedef struct CPUTLBDesc {
     CPUTLBEntry tlb_v_table[NB_MMU_MODES][CPU_VTLB_SIZE];               \
     CPU_IOTLB                                                           \
     CPUIOTLBEntry iotlb_v[NB_MMU_MODES][CPU_VTLB_SIZE];                 \
+    /* stores the host address of a guest access, if needed for plugins */ \
+    void *hostaddr;                                                     \
     size_t tlb_flush_count;                                             \
     target_ulong tlb_flush_addr;                                        \
     target_ulong tlb_flush_mask;                                        \
diff --git a/include/exec/cpu_ldst_template.h b/include/exec/cpu_ldst_template.h
index 0f061d47ef..e25319de4b 100644
--- a/include/exec/cpu_ldst_template.h
+++ b/include/exec/cpu_ldst_template.h
@@ -28,6 +28,7 @@
 #include "trace-root.h"
 #endif
 
+#include "qemu/plugin.h"
 #include "trace/mem.h"
 
 #if DATA_SIZE == 8
@@ -86,11 +87,11 @@ glue(glue(glue(cpu_ld, USUFFIX), MEMSUFFIX), _ra)(CPUArchState *env,
     target_ulong addr;
     int mmu_idx;
     TCGMemOpIdx oi;
-
+    uintptr_t hostaddr;
 #if !defined(SOFTMMU_CODE_ACCESS)
-    trace_guest_mem_before_exec(
-        ENV_GET_CPU(env), ptr,
-        trace_mem_build_info(SHIFT, false, MO_TE, false));
+    uint8_t meminfo = trace_mem_build_info(SHIFT, false, MO_TE, false);
+
+    trace_guest_mem_before_exec(ENV_GET_CPU(env), ptr, meminfo);
 #endif
 
     addr = ptr;
@@ -101,10 +102,14 @@ glue(glue(glue(cpu_ld, USUFFIX), MEMSUFFIX), _ra)(CPUArchState *env,
         oi = make_memop_idx(SHIFT, mmu_idx);
         res = glue(glue(helper_ret_ld, URETSUFFIX), MMUSUFFIX)(env, addr,
                                                             oi, retaddr);
+        hostaddr = (uintptr_t)env->hostaddr;
     } else {
-        uintptr_t hostaddr = addr + entry->addend;
+        hostaddr = addr + entry->addend;
         res = glue(glue(ld, USUFFIX), _p)((uint8_t *)hostaddr);
     }
+#ifndef SOFTMMU_CODE_ACCESS
+    qemu_plugin_vcpu_mem_cb(ENV_GET_CPU(env), ptr, (void *)hostaddr, meminfo);
+#endif
     return res;
 }
 
@@ -125,11 +130,11 @@ glue(glue(glue(cpu_lds, SUFFIX), MEMSUFFIX), _ra)(CPUArchState *env,
     target_ulong addr;
     int mmu_idx;
     TCGMemOpIdx oi;
-
+    uintptr_t hostaddr;
 #if !defined(SOFTMMU_CODE_ACCESS)
-    trace_guest_mem_before_exec(
-        ENV_GET_CPU(env), ptr,
-        trace_mem_build_info(SHIFT, true, MO_TE, false));
+    uint8_t meminfo = trace_mem_build_info(SHIFT, false, MO_TE, false);
+
+    trace_guest_mem_before_exec(ENV_GET_CPU(env), ptr, meminfo);
 #endif
 
     addr = ptr;
@@ -140,10 +145,14 @@ glue(glue(glue(cpu_lds, SUFFIX), MEMSUFFIX), _ra)(CPUArchState *env,
         oi = make_memop_idx(SHIFT, mmu_idx);
         res = (DATA_STYPE)glue(glue(helper_ret_ld, SRETSUFFIX),
                                MMUSUFFIX)(env, addr, oi, retaddr);
+        hostaddr = (uintptr_t)env->hostaddr;
     } else {
-        uintptr_t hostaddr = addr + entry->addend;
+        hostaddr = addr + entry->addend;
         res = glue(glue(lds, SUFFIX), _p)((uint8_t *)hostaddr);
     }
+#ifndef SOFTMMU_CODE_ACCESS
+    qemu_plugin_vcpu_mem_cb(ENV_GET_CPU(env), ptr, (void *)hostaddr, meminfo);
+#endif
     return res;
 }
 
@@ -167,11 +176,11 @@ glue(glue(glue(cpu_st, SUFFIX), MEMSUFFIX), _ra)(CPUArchState *env,
     target_ulong addr;
     int mmu_idx;
     TCGMemOpIdx oi;
-
+    uintptr_t hostaddr;
 #if !defined(SOFTMMU_CODE_ACCESS)
-    trace_guest_mem_before_exec(
-        ENV_GET_CPU(env), ptr,
-        trace_mem_build_info(SHIFT, false, MO_TE, true));
+    uint8_t meminfo = trace_mem_build_info(SHIFT, false, MO_TE, true);
+
+    trace_guest_mem_before_exec(ENV_GET_CPU(env), ptr, meminfo);
 #endif
 
     addr = ptr;
@@ -182,10 +191,14 @@ glue(glue(glue(cpu_st, SUFFIX), MEMSUFFIX), _ra)(CPUArchState *env,
         oi = make_memop_idx(SHIFT, mmu_idx);
         glue(glue(helper_ret_st, SUFFIX), MMUSUFFIX)(env, addr, v, oi,
                                                      retaddr);
+        hostaddr = (uintptr_t)env->hostaddr;
     } else {
-        uintptr_t hostaddr = addr + entry->addend;
+        hostaddr = addr + entry->addend;
         glue(glue(st, SUFFIX), _p)((uint8_t *)hostaddr, v);
     }
+#ifndef SOFTMMU_CODE_ACCESS
+    qemu_plugin_vcpu_mem_cb(ENV_GET_CPU(env), ptr, (void *)hostaddr, meminfo);
+#endif
 }
 
 static inline void
diff --git a/include/exec/cpu_ldst_useronly_template.h b/include/exec/cpu_ldst_useronly_template.h
index 0fd6019af0..e752e9c00e 100644
--- a/include/exec/cpu_ldst_useronly_template.h
+++ b/include/exec/cpu_ldst_useronly_template.h
@@ -64,12 +64,19 @@
 static inline RES_TYPE
 glue(glue(cpu_ld, USUFFIX), MEMSUFFIX)(CPUArchState *env, abi_ptr ptr)
 {
+    RES_TYPE ret;
+#if !defined(CODE_ACCESS)
+    uint8_t meminfo = trace_mem_build_info(SHIFT, false, MO_TE, false);
+
+    trace_guest_mem_before_exec(ENV_GET_CPU(env), ptr, meminfo);
+#endif
+
+    ret = glue(glue(ld, USUFFIX), _p)(g2h(ptr));
+
 #if !defined(CODE_ACCESS)
-    trace_guest_mem_before_exec(
-        ENV_GET_CPU(env), ptr,
-        trace_mem_build_info(SHIFT, false, MO_TE, false));
+    qemu_plugin_vcpu_mem_cb(ENV_GET_CPU(env), ptr, NULL, meminfo);
 #endif
-    return glue(glue(ld, USUFFIX), _p)(g2h(ptr));
+    return ret;
 }
 
 static inline RES_TYPE
@@ -88,12 +95,19 @@ glue(glue(glue(cpu_ld, USUFFIX), MEMSUFFIX), _ra)(CPUArchState *env,
 static inline int
 glue(glue(cpu_lds, SUFFIX), MEMSUFFIX)(CPUArchState *env, abi_ptr ptr)
 {
+    int ret;
 #if !defined(CODE_ACCESS)
-    trace_guest_mem_before_exec(
-        ENV_GET_CPU(env), ptr,
-        trace_mem_build_info(SHIFT, true, MO_TE, false));
+    uint8_t meminfo = trace_mem_build_info(SHIFT, true, MO_TE, false);
+
+    trace_guest_mem_before_exec(ENV_GET_CPU(env), ptr, meminfo);
 #endif
-    return glue(glue(lds, SUFFIX), _p)(g2h(ptr));
+
+    ret = glue(glue(lds, SUFFIX), _p)(g2h(ptr));
+
+#if !defined(CODE_ACCESS)
+    qemu_plugin_vcpu_mem_cb(ENV_GET_CPU(env), ptr, NULL, meminfo);
+#endif
+    return ret;
 }
 
 static inline int
@@ -109,17 +123,21 @@ glue(glue(glue(cpu_lds, SUFFIX), MEMSUFFIX), _ra)(CPUArchState *env,
 }
 #endif
 
-#ifndef CODE_ACCESS
+#if !defined(CODE_ACCESS)
 static inline void
 glue(glue(cpu_st, SUFFIX), MEMSUFFIX)(CPUArchState *env, abi_ptr ptr,
                                       RES_TYPE v)
 {
 #if !defined(CODE_ACCESS)
-    trace_guest_mem_before_exec(
-        ENV_GET_CPU(env), ptr,
-        trace_mem_build_info(SHIFT, false, MO_TE, true));
+    uint8_t meminfo = trace_mem_build_info(SHIFT, false, MO_TE, true);
+    trace_guest_mem_before_exec(ENV_GET_CPU(env), ptr, meminfo);
 #endif
+
     glue(glue(st, SUFFIX), _p)(g2h(ptr), v);
+
+#if !defined(CODE_ACCESS)
+    qemu_plugin_vcpu_mem_cb(ENV_GET_CPU(env), ptr, NULL, meminfo);
+#endif
 }
 
 static inline void
diff --git a/tcg/tcg-op.h b/tcg/tcg-op.h
index 7513c1eb7c..cded8fc05b 100644
--- a/tcg/tcg-op.h
+++ b/tcg/tcg-op.h
@@ -1208,6 +1208,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);
diff --git a/tcg/tcg.h b/tcg/tcg.h
index 2c378415d2..d5afe25c97 100644
--- a/tcg/tcg.h
+++ b/tcg/tcg.h
@@ -29,6 +29,7 @@
 #include "cpu.h"
 #include "exec/tb-context.h"
 #include "qemu/bitops.h"
+#include "qemu/plugin.h"
 #include "qemu/queue.h"
 #include "tcg-mo.h"
 #include "tcg-target.h"
@@ -719,6 +720,9 @@ struct TCGContext {
 
     TCGLabel *exitreq_label;
 
+    struct qemu_plugin_dyn_cb_arr *plugin_mem_cb;
+    struct qemu_plugin_insn *plugin_insn;
+
     TCGTempSet free_temps[TCG_TYPE_COUNT * 2];
     TCGTemp temps[TCG_MAX_TEMPS]; /* globals first, temps after */
 
diff --git a/tcg/i386/tcg-target.inc.c b/tcg/i386/tcg-target.inc.c
index 5cbb07deab..7d9e6765c4 100644
--- a/tcg/i386/tcg-target.inc.c
+++ b/tcg/i386/tcg-target.inc.c
@@ -1685,6 +1685,11 @@ 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));
+
+    if (s->plugin_mem_cb) {
+        tcg_out_st(s, TCG_TYPE_PTR, r1, TCG_AREG0,
+                   offsetof(CPUArchState, hostaddr));
+    }
 }
 
 /*
diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c
index 7a8015c5a9..add63547e3 100644
--- a/tcg/tcg-op.c
+++ b/tcg/tcg-op.c
@@ -31,6 +31,7 @@
 #include "tcg-mo.h"
 #include "trace-tcg.h"
 #include "trace/mem.h"
+#include "exec/plugin-gen.h"
 
 /* Reduce the number of ifdefs below.  This assumes that all uses of
    TCGV_HIGH and TCGV_LOW are properly protected by a conditional that
@@ -2699,26 +2700,42 @@ static void tcg_gen_req_mo(TCGBar type)
     }
 }
 
+static inline void plugin_gen_mem_callbacks(TCGv vaddr, uint8_t info)
+{
+    struct qemu_plugin_dyn_cb_arr *arr = tcg_ctx->plugin_mem_cb;
+
+    if (arr == NULL) {
+        return;
+    }
+    qemu_plugin_gen_vcpu_mem_callbacks(arr, vaddr, info);
+}
+
 void tcg_gen_qemu_ld_i32(TCGv_i32 val, TCGv addr, TCGArg idx, TCGMemOp memop)
 {
+    uint8_t info = trace_mem_get_info(memop, 0);
+
     tcg_gen_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD);
     memop = tcg_canonicalize_memop(memop, 0, 0);
-    trace_guest_mem_before_tcg(tcg_ctx->cpu, cpu_env,
-                               addr, trace_mem_get_info(memop, 0));
+    trace_guest_mem_before_tcg(tcg_ctx->cpu, cpu_env, addr, info);
     gen_ldst_i32(INDEX_op_qemu_ld_i32, val, addr, memop, idx);
+    plugin_gen_mem_callbacks(addr, info);
 }
 
 void tcg_gen_qemu_st_i32(TCGv_i32 val, TCGv addr, TCGArg idx, TCGMemOp memop)
 {
+    uint8_t info = trace_mem_get_info(memop, 1);
+
     tcg_gen_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST);
     memop = tcg_canonicalize_memop(memop, 0, 1);
-    trace_guest_mem_before_tcg(tcg_ctx->cpu, cpu_env,
-                               addr, trace_mem_get_info(memop, 1));
+    trace_guest_mem_before_tcg(tcg_ctx->cpu, cpu_env, addr, info);
     gen_ldst_i32(INDEX_op_qemu_st_i32, val, addr, memop, idx);
+    plugin_gen_mem_callbacks(addr, info);
 }
 
 void tcg_gen_qemu_ld_i64(TCGv_i64 val, TCGv addr, TCGArg idx, TCGMemOp memop)
 {
+    uint8_t info;
+
     tcg_gen_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD);
     if (TCG_TARGET_REG_BITS == 32 && (memop & MO_SIZE) < MO_64) {
         tcg_gen_qemu_ld_i32(TCGV_LOW(val), addr, idx, memop);
@@ -2731,13 +2748,16 @@ void tcg_gen_qemu_ld_i64(TCGv_i64 val, TCGv addr, TCGArg idx, TCGMemOp memop)
     }
 
     memop = tcg_canonicalize_memop(memop, 1, 0);
-    trace_guest_mem_before_tcg(tcg_ctx->cpu, cpu_env,
-                               addr, trace_mem_get_info(memop, 0));
+    info = trace_mem_get_info(memop, 0);
+    trace_guest_mem_before_tcg(tcg_ctx->cpu, cpu_env, addr, info);
     gen_ldst_i64(INDEX_op_qemu_ld_i64, val, addr, memop, idx);
+    plugin_gen_mem_callbacks(addr, info);
 }
 
 void tcg_gen_qemu_st_i64(TCGv_i64 val, TCGv addr, TCGArg idx, TCGMemOp memop)
 {
+    uint8_t info;
+
     tcg_gen_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST);
     if (TCG_TARGET_REG_BITS == 32 && (memop & MO_SIZE) < MO_64) {
         tcg_gen_qemu_st_i32(TCGV_LOW(val), addr, idx, memop);
@@ -2745,9 +2765,10 @@ void tcg_gen_qemu_st_i64(TCGv_i64 val, TCGv addr, TCGArg idx, TCGMemOp memop)
     }
 
     memop = tcg_canonicalize_memop(memop, 1, 1);
-    trace_guest_mem_before_tcg(tcg_ctx->cpu, cpu_env,
-                               addr, trace_mem_get_info(memop, 1));
+    info = trace_mem_get_info(memop, 1);
+    trace_guest_mem_before_tcg(tcg_ctx->cpu, cpu_env, addr, info);
     gen_ldst_i64(INDEX_op_qemu_st_i64, val, addr, memop, idx);
+    plugin_gen_mem_callbacks(addr, info);
 }
 
 static void tcg_gen_ext_i32(TCGv_i32 ret, TCGv_i32 val, TCGMemOp opc)
diff --git a/tcg/tcg.c b/tcg/tcg.c
index a6824145b0..b10bda587a 100644
--- a/tcg/tcg.c
+++ b/tcg/tcg.c
@@ -1839,6 +1839,9 @@ void tcg_gen_callN(void *func, TCGTemp *ret, int nargs, TCGTemp **args)
      * that @func can only be in one TCGHelperInfo.
      */
     info = qht_lookup_custom(&helper_table, func, hash, tcg_helper_lookup_cmp);
+    if (unlikely(tcg_ctx->plugin_insn)) {
+        tcg_ctx->plugin_insn->calls_helpers = true;
+    }
     do_tcg_gen_callN(info, ret, nargs, args);
 }
 
-- 
2.17.1

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

* [Qemu-devel] [RFC 19/48] translate-all: notify plugin code of tb_flush
  2018-10-25 17:20 [Qemu-devel] [RFC 00/48] Plugin support Emilio G. Cota
                   ` (17 preceding siblings ...)
  2018-10-25 17:20 ` [Qemu-devel] [RFC 18/48] tcg: add memory callbacks for plugins (WIP) Emilio G. Cota
@ 2018-10-25 17:20 ` Emilio G. Cota
  2018-11-23 17:00   ` Alex Bennée
  2018-10-25 17:20 ` [Qemu-devel] [RFC 20/48] *-user: notify plugin of exit Emilio G. Cota
                   ` (29 subsequent siblings)
  48 siblings, 1 reply; 132+ messages in thread
From: Emilio G. Cota @ 2018-10-25 17:20 UTC (permalink / raw)
  To: qemu-devel
  Cc: Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Alex Bennée, Stefan Hajnoczi

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 3423cf74db..1690e3fd5b 100644
--- a/accel/tcg/translate-all.c
+++ b/accel/tcg/translate-all.c
@@ -1233,6 +1233,8 @@ static gboolean tb_host_size_iter(gpointer key, gpointer value, gpointer data)
 /* flush all the translation blocks */
 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.
@@ -1240,6 +1242,7 @@ 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();
@@ -1265,6 +1268,9 @@ void do_tb_flush(CPUState *cpu, run_on_cpu_data tb_flush_count)
 
 done:
     mmap_unlock();
+    if (did_flush) {
+        qemu_plugin_flush_cb();
+    }
 }
 
 void tb_flush(CPUState *cpu)
-- 
2.17.1

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

* [Qemu-devel] [RFC 20/48] *-user: notify plugin of exit
  2018-10-25 17:20 [Qemu-devel] [RFC 00/48] Plugin support Emilio G. Cota
                   ` (18 preceding siblings ...)
  2018-10-25 17:20 ` [Qemu-devel] [RFC 19/48] translate-all: notify plugin code of tb_flush Emilio G. Cota
@ 2018-10-25 17:20 ` Emilio G. Cota
  2018-11-23 17:01   ` Alex Bennée
  2018-10-25 17:20 ` [Qemu-devel] [RFC 21/48] *-user: plugin syscalls Emilio G. Cota
                   ` (28 subsequent siblings)
  48 siblings, 1 reply; 132+ messages in thread
From: Emilio G. Cota @ 2018-10-25 17:20 UTC (permalink / raw)
  To: qemu-devel
  Cc: Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Alex Bennée, Stefan Hajnoczi

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

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

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

* [Qemu-devel] [RFC 21/48] *-user: plugin syscalls
  2018-10-25 17:20 [Qemu-devel] [RFC 00/48] Plugin support Emilio G. Cota
                   ` (19 preceding siblings ...)
  2018-10-25 17:20 ` [Qemu-devel] [RFC 20/48] *-user: notify plugin of exit Emilio G. Cota
@ 2018-10-25 17:20 ` Emilio G. Cota
  2018-11-23 17:04   ` Alex Bennée
  2018-10-25 17:20 ` [Qemu-devel] [RFC 22/48] cpu: hook plugin vcpu events Emilio G. Cota
                   ` (27 subsequent siblings)
  48 siblings, 1 reply; 132+ messages in thread
From: Emilio G. Cota @ 2018-10-25 17:20 UTC (permalink / raw)
  To: qemu-devel
  Cc: Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Alex Bennée, Stefan Hajnoczi

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

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

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

* [Qemu-devel] [RFC 22/48] cpu: hook plugin vcpu events
  2018-10-25 17:20 [Qemu-devel] [RFC 00/48] Plugin support Emilio G. Cota
                   ` (20 preceding siblings ...)
  2018-10-25 17:20 ` [Qemu-devel] [RFC 21/48] *-user: plugin syscalls Emilio G. Cota
@ 2018-10-25 17:20 ` Emilio G. Cota
  2018-11-23 17:10   ` Alex Bennée
  2018-10-25 17:20 ` [Qemu-devel] [RFC 23/48] translator: add plugin_insn argument to translate_insn Emilio G. Cota
                   ` (26 subsequent siblings)
  48 siblings, 1 reply; 132+ messages in thread
From: Emilio G. Cota @ 2018-10-25 17:20 UTC (permalink / raw)
  To: qemu-devel
  Cc: Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Alex Bennée, Stefan Hajnoczi

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 28e39f045a..3efe89354d 100644
--- a/cpus.c
+++ b/cpus.c
@@ -43,6 +43,7 @@
 #include "exec/exec-all.h"
 
 #include "qemu/thread.h"
+#include "qemu/plugin.h"
 #include "sysemu/cpus.h"
 #include "sysemu/qtest.h"
 #include "qemu/main-loop.h"
@@ -1322,12 +1323,21 @@ static void qemu_tcg_rr_wait_io_event(CPUState *cpu)
 
 static void qemu_wait_io_event(CPUState *cpu)
 {
+    bool asleep = false;
+
     g_assert(cpu_mutex_locked(cpu));
     g_assert(!qemu_mutex_iothread_locked());
 
     while (cpu_thread_is_idle(cpu)) {
+        if (!asleep) {
+            asleep = true;
+            qemu_plugin_vcpu_idle_cb(cpu);
+        }
         qemu_cond_wait(&cpu->halt_cond, &cpu->lock);
     }
+    if (asleep) {
+        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 cd171adb93..71fc76f55e 100644
--- a/exec.c
+++ b/exec.c
@@ -967,6 +967,8 @@ void cpu_exec_realizefn(CPUState *cpu, Error **errp)
     }
     tlb_init(cpu);
 
+    qemu_plugin_vcpu_init_hook(cpu);
+
 #ifndef CONFIG_USER_ONLY
     if (qdev_get_vmsd(DEVICE(cpu)) == NULL) {
         vmstate_register(NULL, cpu->cpu_index, &vmstate_cpu_common, cpu);
diff --git a/qom/cpu.c b/qom/cpu.c
index d1e6ecae03..062817c03b 100644
--- a/qom/cpu.c
+++ b/qom/cpu.c
@@ -32,6 +32,7 @@
 #include "hw/boards.h"
 #include "hw/qdev-properties.h"
 #include "trace-root.h"
+#include "qemu/plugin.h"
 
 CPUInterruptHandler cpu_interrupt_handler;
 
@@ -353,6 +354,7 @@ static void cpu_common_unrealizefn(DeviceState *dev, Error **errp)
     CPUState *cpu = CPU(dev);
     /* NOTE: latest generic point before the cpu is fully unrealized */
     trace_fini_vcpu(cpu);
+    qemu_plugin_vcpu_exit_hook(cpu);
     cpu_exec_unrealizefn(cpu);
 }
 
-- 
2.17.1

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

* [Qemu-devel] [RFC 23/48] translator: add plugin_insn argument to translate_insn
  2018-10-25 17:20 [Qemu-devel] [RFC 00/48] Plugin support Emilio G. Cota
                   ` (21 preceding siblings ...)
  2018-10-25 17:20 ` [Qemu-devel] [RFC 22/48] cpu: hook plugin vcpu events Emilio G. Cota
@ 2018-10-25 17:20 ` Emilio G. Cota
  2018-11-26 14:52   ` Alex Bennée
  2018-10-25 17:20 ` [Qemu-devel] [RFC 24/48] translator: add .ctx_base_offset and .ctx_size to TranslatorOps Emilio G. Cota
                   ` (25 subsequent siblings)
  48 siblings, 1 reply; 132+ messages in thread
From: Emilio G. Cota @ 2018-10-25 17:20 UTC (permalink / raw)
  To: qemu-devel
  Cc: Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Alex Bennée, Stefan Hajnoczi

Signed-off-by: Emilio G. Cota <cota@braap.org>
---
 include/exec/translator.h   | 4 +++-
 accel/tcg/translator.c      | 4 ++--
 target/alpha/translate.c    | 3 ++-
 target/arm/translate-a64.c  | 3 ++-
 target/arm/translate.c      | 6 ++++--
 target/hppa/translate.c     | 3 ++-
 target/i386/translate.c     | 3 ++-
 target/m68k/translate.c     | 3 ++-
 target/mips/translate.c     | 3 ++-
 target/openrisc/translate.c | 3 ++-
 target/ppc/translate.c      | 3 ++-
 target/riscv/translate.c    | 3 ++-
 target/s390x/translate.c    | 3 ++-
 target/sh4/translate.c      | 3 ++-
 target/sparc/translate.c    | 3 ++-
 target/xtensa/translate.c   | 3 ++-
 16 files changed, 35 insertions(+), 18 deletions(-)

diff --git a/include/exec/translator.h b/include/exec/translator.h
index 71e7b2c347..a28147b3dd 100644
--- a/include/exec/translator.h
+++ b/include/exec/translator.h
@@ -20,6 +20,7 @@
 
 
 #include "exec/exec-all.h"
+#include "qemu/plugin.h"
 #include "tcg/tcg.h"
 
 
@@ -112,7 +113,8 @@ typedef struct TranslatorOps {
     void (*insn_start)(DisasContextBase *db, CPUState *cpu);
     bool (*breakpoint_check)(DisasContextBase *db, CPUState *cpu,
                              const CPUBreakpoint *bp);
-    void (*translate_insn)(DisasContextBase *db, CPUState *cpu);
+    void (*translate_insn)(DisasContextBase *db, CPUState *cpu,
+                           struct qemu_plugin_insn *plugin_insn);
     void (*tb_stop)(DisasContextBase *db, CPUState *cpu);
     void (*disas_log)(const DisasContextBase *db, CPUState *cpu);
 } TranslatorOps;
diff --git a/accel/tcg/translator.c b/accel/tcg/translator.c
index afd0a49ea6..8591e4b72a 100644
--- a/accel/tcg/translator.c
+++ b/accel/tcg/translator.c
@@ -101,10 +101,10 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
             && (tb_cflags(db->tb) & CF_LAST_IO)) {
             /* Accept I/O on the last instruction.  */
             gen_io_start();
-            ops->translate_insn(db, cpu);
+            ops->translate_insn(db, cpu, NULL);
             gen_io_end();
         } else {
-            ops->translate_insn(db, cpu);
+            ops->translate_insn(db, cpu, NULL);
         }
 
         /* Stop translation if translate_insn so indicated.  */
diff --git a/target/alpha/translate.c b/target/alpha/translate.c
index 25cd95931d..72a302e102 100644
--- a/target/alpha/translate.c
+++ b/target/alpha/translate.c
@@ -2983,7 +2983,8 @@ static bool alpha_tr_breakpoint_check(DisasContextBase *dcbase, CPUState *cpu,
     return true;
 }
 
-static void alpha_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu)
+static void alpha_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu,
+                                    struct qemu_plugin_insn *plugin_insn)
 {
     DisasContext *ctx = container_of(dcbase, DisasContext, base);
     CPUAlphaState *env = cpu->env_ptr;
diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c
index bb9c4d8ac7..8b1e20dd59 100644
--- a/target/arm/translate-a64.c
+++ b/target/arm/translate-a64.c
@@ -13937,7 +13937,8 @@ static bool aarch64_tr_breakpoint_check(DisasContextBase *dcbase, CPUState *cpu,
     return true;
 }
 
-static void aarch64_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu)
+static void aarch64_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu,
+                                      struct qemu_plugin_insn *plugin_insn)
 {
     DisasContext *dc = container_of(dcbase, DisasContext, base);
     CPUARMState *env = cpu->env_ptr;
diff --git a/target/arm/translate.c b/target/arm/translate.c
index 1b4bacb522..2fd32a2684 100644
--- a/target/arm/translate.c
+++ b/target/arm/translate.c
@@ -12787,7 +12787,8 @@ static void arm_post_translate_insn(DisasContext *dc)
     translator_loop_temp_check(&dc->base);
 }
 
-static void arm_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu)
+static void arm_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu,
+                                  struct qemu_plugin_insn *plugin_insn)
 {
     DisasContext *dc = container_of(dcbase, DisasContext, base);
     CPUARMState *env = cpu->env_ptr;
@@ -12854,7 +12855,8 @@ static bool thumb_insn_is_unconditional(DisasContext *s, uint32_t insn)
     return false;
 }
 
-static void thumb_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu)
+static void thumb_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu,
+                                    struct qemu_plugin_insn *plugin_insn)
 {
     DisasContext *dc = container_of(dcbase, DisasContext, base);
     CPUARMState *env = cpu->env_ptr;
diff --git a/target/hppa/translate.c b/target/hppa/translate.c
index df9179e70f..6c2a7fbc46 100644
--- a/target/hppa/translate.c
+++ b/target/hppa/translate.c
@@ -4737,7 +4737,8 @@ static bool hppa_tr_breakpoint_check(DisasContextBase *dcbase, CPUState *cs,
     return true;
 }
 
-static void hppa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs)
+static void hppa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs,
+                                   struct qemu_plugin_insn *plugin_insn)
 {
     DisasContext *ctx = container_of(dcbase, DisasContext, base);
     CPUHPPAState *env = cs->env_ptr;
diff --git a/target/i386/translate.c b/target/i386/translate.c
index 83c1ebe491..86e59d7bf7 100644
--- a/target/i386/translate.c
+++ b/target/i386/translate.c
@@ -8525,7 +8525,8 @@ static bool i386_tr_breakpoint_check(DisasContextBase *dcbase, CPUState *cpu,
     }
 }
 
-static void i386_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu)
+static void i386_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu,
+                                   struct qemu_plugin_insn *plugin_insn)
 {
     DisasContext *dc = container_of(dcbase, DisasContext, base);
     target_ulong pc_next = disas_insn(dc, cpu);
diff --git a/target/m68k/translate.c b/target/m68k/translate.c
index d55e707cf6..dd7d868b25 100644
--- a/target/m68k/translate.c
+++ b/target/m68k/translate.c
@@ -6087,7 +6087,8 @@ static bool m68k_tr_breakpoint_check(DisasContextBase *dcbase, CPUState *cpu,
     return true;
 }
 
-static void m68k_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu)
+static void m68k_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu,
+                                   struct qemu_plugin_insn *plugin_insn)
 {
     DisasContext *dc = container_of(dcbase, DisasContext, base);
     CPUM68KState *env = cpu->env_ptr;
diff --git a/target/mips/translate.c b/target/mips/translate.c
index 544e4dc19c..efafc6e795 100644
--- a/target/mips/translate.c
+++ b/target/mips/translate.c
@@ -25349,7 +25349,8 @@ static bool mips_tr_breakpoint_check(DisasContextBase *dcbase, CPUState *cs,
     return true;
 }
 
-static void mips_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs)
+static void mips_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs,
+                                   struct qemu_plugin_insn *plugin_insn)
 {
     CPUMIPSState *env = cs->env_ptr;
     DisasContext *ctx = container_of(dcbase, DisasContext, base);
diff --git a/target/openrisc/translate.c b/target/openrisc/translate.c
index a271cd3903..947330e10a 100644
--- a/target/openrisc/translate.c
+++ b/target/openrisc/translate.c
@@ -1301,7 +1301,8 @@ static bool openrisc_tr_breakpoint_check(DisasContextBase *dcbase, CPUState *cs,
     return true;
 }
 
-static void openrisc_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs)
+static void openrisc_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs,
+                                       struct qemu_plugin_insn *plugin_insn)
 {
     DisasContext *dc = container_of(dcbase, DisasContext, base);
     OpenRISCCPU *cpu = OPENRISC_CPU(cs);
diff --git a/target/ppc/translate.c b/target/ppc/translate.c
index 2d31b5f7a1..34c3ed0a41 100644
--- a/target/ppc/translate.c
+++ b/target/ppc/translate.c
@@ -7545,7 +7545,8 @@ static bool ppc_tr_breakpoint_check(DisasContextBase *dcbase, CPUState *cs,
     return true;
 }
 
-static void ppc_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs)
+static void ppc_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs,
+                                  struct qemu_plugin_insn *plugin_insn)
 {
     DisasContext *ctx = container_of(dcbase, DisasContext, base);
     CPUPPCState *env = cs->env_ptr;
diff --git a/target/riscv/translate.c b/target/riscv/translate.c
index 18d7b6d147..a33cf6802b 100644
--- a/target/riscv/translate.c
+++ b/target/riscv/translate.c
@@ -1843,7 +1843,8 @@ static bool riscv_tr_breakpoint_check(DisasContextBase *dcbase, CPUState *cpu,
 }
 
 
-static void riscv_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu)
+static void riscv_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu,
+                                    struct qemu_plugin_insn *plugin_insn)
 {
     DisasContext *ctx = container_of(dcbase, DisasContext, base);
     CPURISCVState *env = cpu->env_ptr;
diff --git a/target/s390x/translate.c b/target/s390x/translate.c
index b5bd56b7ee..6ac1a8d821 100644
--- a/target/s390x/translate.c
+++ b/target/s390x/translate.c
@@ -6228,7 +6228,8 @@ static bool s390x_tr_breakpoint_check(DisasContextBase *dcbase, CPUState *cs,
     return true;
 }
 
-static void s390x_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs)
+static void s390x_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs,
+                                    struct qemu_plugin_insn *plugin_insn)
 {
     CPUS390XState *env = cs->env_ptr;
     DisasContext *dc = container_of(dcbase, DisasContext, base);
diff --git a/target/sh4/translate.c b/target/sh4/translate.c
index ab254b0e8d..ea88d46c04 100644
--- a/target/sh4/translate.c
+++ b/target/sh4/translate.c
@@ -2313,7 +2313,8 @@ static bool sh4_tr_breakpoint_check(DisasContextBase *dcbase, CPUState *cs,
     return true;
 }
 
-static void sh4_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs)
+static void sh4_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs,
+                                  struct qemu_plugin_insn *plugin_insn)
 {
     CPUSH4State *env = cs->env_ptr;
     DisasContext *ctx = container_of(dcbase, DisasContext, base);
diff --git a/target/sparc/translate.c b/target/sparc/translate.c
index 74315cdf09..2fa8b68e0a 100644
--- a/target/sparc/translate.c
+++ b/target/sparc/translate.c
@@ -5894,7 +5894,8 @@ static bool sparc_tr_breakpoint_check(DisasContextBase *dcbase, CPUState *cs,
     return true;
 }
 
-static void sparc_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs)
+static void sparc_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs,
+                                    struct qemu_plugin_insn *plugin_insn)
 {
     DisasContext *dc = container_of(dcbase, DisasContext, base);
     CPUSPARCState *env = cs->env_ptr;
diff --git a/target/xtensa/translate.c b/target/xtensa/translate.c
index 46e1338448..14ab1c5ceb 100644
--- a/target/xtensa/translate.c
+++ b/target/xtensa/translate.c
@@ -1146,7 +1146,8 @@ static bool xtensa_tr_breakpoint_check(DisasContextBase *dcbase, CPUState *cpu,
     return true;
 }
 
-static void xtensa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu)
+static void xtensa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu,
+                                     struct qemu_plugin_insn *plugin_insn)
 {
     DisasContext *dc = container_of(dcbase, DisasContext, base);
     CPUXtensaState *env = cpu->env_ptr;
-- 
2.17.1

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

* [Qemu-devel] [RFC 24/48] translator: add .ctx_base_offset and .ctx_size to TranslatorOps
  2018-10-25 17:20 [Qemu-devel] [RFC 00/48] Plugin support Emilio G. Cota
                   ` (22 preceding siblings ...)
  2018-10-25 17:20 ` [Qemu-devel] [RFC 23/48] translator: add plugin_insn argument to translate_insn Emilio G. Cota
@ 2018-10-25 17:20 ` Emilio G. Cota
  2018-10-25 17:20 ` [Qemu-devel] [RFC 25/48] target/arm: prepare for 2-pass translation Emilio G. Cota
                   ` (24 subsequent siblings)
  48 siblings, 0 replies; 132+ messages in thread
From: Emilio G. Cota @ 2018-10-25 17:20 UTC (permalink / raw)
  To: qemu-devel
  Cc: Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Alex Bennée, Stefan Hajnoczi

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

diff --git a/include/exec/translator.h b/include/exec/translator.h
index a28147b3dd..e20ca9f854 100644
--- a/include/exec/translator.h
+++ b/include/exec/translator.h
@@ -106,6 +106,8 @@ typedef struct DisasContextBase {
  *
  * @disas_log:
  *      Print instruction disassembly to log.
+ * @ctx_base_offset: offset of DisasContextBase within DisasContext.
+ * @ctx_size: size of DisasContext.
  */
 typedef struct TranslatorOps {
     void (*init_disas_context)(DisasContextBase *db, CPUState *cpu);
@@ -117,6 +119,8 @@ typedef struct TranslatorOps {
                            struct qemu_plugin_insn *plugin_insn);
     void (*tb_stop)(DisasContextBase *db, CPUState *cpu);
     void (*disas_log)(const DisasContextBase *db, CPUState *cpu);
+    size_t ctx_base_offset;
+    size_t ctx_size;
 } TranslatorOps;
 
 /**
-- 
2.17.1

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

* [Qemu-devel] [RFC 25/48] target/arm: prepare for 2-pass translation
  2018-10-25 17:20 [Qemu-devel] [RFC 00/48] Plugin support Emilio G. Cota
                   ` (23 preceding siblings ...)
  2018-10-25 17:20 ` [Qemu-devel] [RFC 24/48] translator: add .ctx_base_offset and .ctx_size to TranslatorOps Emilio G. Cota
@ 2018-10-25 17:20 ` Emilio G. Cota
  2018-10-25 17:20 ` [Qemu-devel] [RFC 26/48] target/ppc: " Emilio G. Cota
                   ` (23 subsequent siblings)
  48 siblings, 0 replies; 132+ messages in thread
From: Emilio G. Cota @ 2018-10-25 17:20 UTC (permalink / raw)
  To: qemu-devel
  Cc: Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Alex Bennée, Stefan Hajnoczi

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

diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c
index 8b1e20dd59..dab5f6efd3 100644
--- a/target/arm/translate-a64.c
+++ b/target/arm/translate-a64.c
@@ -13783,11 +13783,13 @@ static void disas_data_proc_simd_fp(DisasContext *s, uint32_t insn)
 }
 
 /* C3.1 A64 instruction index by encoding */
-static void disas_a64_insn(CPUARMState *env, DisasContext *s)
+static void disas_a64_insn(CPUARMState *env, DisasContext *s,
+                           struct qemu_plugin_insn *plugin_insn)
 {
     uint32_t insn;
 
     insn = arm_ldl_code(env, s->pc, s->sctlr_b);
+    qemu_plugin_insn_append(plugin_insn, &insn, sizeof(insn));
     s->insn = insn;
     s->pc += 4;
 
@@ -13959,7 +13961,7 @@ static void aarch64_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu,
                       default_exception_el(dc));
         dc->base.is_jmp = DISAS_NORETURN;
     } else {
-        disas_a64_insn(env, dc);
+        disas_a64_insn(env, dc, plugin_insn);
     }
 
     dc->base.pc_next = dc->pc;
@@ -14058,4 +14060,6 @@ const TranslatorOps aarch64_translator_ops = {
     .translate_insn     = aarch64_tr_translate_insn,
     .tb_stop            = aarch64_tr_tb_stop,
     .disas_log          = aarch64_tr_disas_log,
+    .ctx_base_offset    = offsetof(DisasContext, base),
+    .ctx_size           = sizeof(DisasContext),
 };
diff --git a/target/arm/translate.c b/target/arm/translate.c
index 2fd32a2684..015153a260 100644
--- a/target/arm/translate.c
+++ b/target/arm/translate.c
@@ -10198,7 +10198,8 @@ gen_thumb2_data_op(DisasContext *s, int op, int conds, uint32_t shifter_out,
 }
 
 /* Translate a 32-bit thumb instruction. */
-static void disas_thumb2_insn(DisasContext *s, uint32_t insn)
+static void disas_thumb2_insn(DisasContext *s, uint32_t insn,
+                              struct qemu_plugin_insn *plugin_insn)
 {
     uint32_t imm, shift, offset;
     uint32_t rd, rn, rm, rs;
@@ -11736,7 +11737,8 @@ illegal_op:
                        default_exception_el(s));
 }
 
-static void disas_thumb_insn(DisasContext *s, uint32_t insn)
+static void disas_thumb_insn(DisasContext *s, uint32_t insn,
+                             struct qemu_plugin_insn *plugin_insn)
 {
     uint32_t val, op, rm, rn, rd, shift, cond;
     int32_t offset;
@@ -12800,6 +12802,7 @@ static void arm_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu,
 
     insn = arm_ldl_code(env, dc->pc, dc->sctlr_b);
     dc->insn = insn;
+    qemu_plugin_insn_append(plugin_insn, &insn, sizeof(insn));
     dc->pc += 4;
     disas_arm_insn(dc, insn);
 
@@ -12870,11 +12873,21 @@ 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 (plugin_insn) {
+        uint16_t insn16 = insn;
+
+        qemu_plugin_insn_append(plugin_insn, &insn16, sizeof(insn16));
+    }
     if (!is_16bit) {
         uint32_t insn2 = arm_lduw_code(env, dc->pc, dc->sctlr_b);
 
         insn = insn << 16 | insn2;
         dc->pc += 2;
+        if (plugin_insn) {
+            uint16_t insn16 = insn2;
+
+            qemu_plugin_insn_append(plugin_insn, &insn16, sizeof(insn16));
+        }
     }
     dc->insn = insn;
 
@@ -12887,9 +12900,9 @@ static void thumb_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu,
     }
 
     if (is_16bit) {
-        disas_thumb_insn(dc, insn);
+        disas_thumb_insn(dc, insn, plugin_insn);
     } else {
-        disas_thumb2_insn(dc, insn);
+        disas_thumb2_insn(dc, insn, plugin_insn);
     }
 
     /* Advance the Thumb condexec condition.  */
@@ -13064,6 +13077,8 @@ static const TranslatorOps arm_translator_ops = {
     .translate_insn     = arm_tr_translate_insn,
     .tb_stop            = arm_tr_tb_stop,
     .disas_log          = arm_tr_disas_log,
+    .ctx_base_offset    = offsetof(DisasContext, base),
+    .ctx_size           = sizeof(DisasContext),
 };
 
 static const TranslatorOps thumb_translator_ops = {
@@ -13074,6 +13089,8 @@ static const TranslatorOps thumb_translator_ops = {
     .translate_insn     = thumb_tr_translate_insn,
     .tb_stop            = arm_tr_tb_stop,
     .disas_log          = arm_tr_disas_log,
+    .ctx_base_offset    = offsetof(DisasContext, base),
+    .ctx_size           = sizeof(DisasContext),
 };
 
 /* generate intermediate code for basic block 'tb'.  */
-- 
2.17.1

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

* [Qemu-devel] [RFC 26/48] target/ppc: prepare for 2-pass translation
  2018-10-25 17:20 [Qemu-devel] [RFC 00/48] Plugin support Emilio G. Cota
                   ` (24 preceding siblings ...)
  2018-10-25 17:20 ` [Qemu-devel] [RFC 25/48] target/arm: prepare for 2-pass translation Emilio G. Cota
@ 2018-10-25 17:20 ` Emilio G. Cota
  2018-10-25 17:20 ` [Qemu-devel] [RFC 27/48] target/sh4: prepare for 2-pass translation (WIP) Emilio G. Cota
                   ` (22 subsequent siblings)
  48 siblings, 0 replies; 132+ messages in thread
From: Emilio G. Cota @ 2018-10-25 17:20 UTC (permalink / raw)
  To: qemu-devel
  Cc: Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Alex Bennée, Stefan Hajnoczi

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

diff --git a/target/ppc/translate.c b/target/ppc/translate.c
index 34c3ed0a41..88f88adaff 100644
--- a/target/ppc/translate.c
+++ b/target/ppc/translate.c
@@ -7565,6 +7565,9 @@ static void ppc_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs,
               ctx->opcode, opc1(ctx->opcode), opc2(ctx->opcode),
               opc3(ctx->opcode), opc4(ctx->opcode),
               ctx->le_mode ? "little" : "big");
+
+    qemu_plugin_insn_append(plugin_insn, &ctx->opcode, sizeof(ctx->opcode));
+
     ctx->base.pc_next += 4;
     table = env->opcodes;
     handler = table[opc1(ctx->opcode)];
@@ -7664,6 +7667,8 @@ static const TranslatorOps ppc_tr_ops = {
     .translate_insn     = ppc_tr_translate_insn,
     .tb_stop            = ppc_tr_tb_stop,
     .disas_log          = ppc_tr_disas_log,
+    .ctx_base_offset    = offsetof(DisasContext, base),
+    .ctx_size           = sizeof(DisasContext),
 };
 
 void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb)
-- 
2.17.1

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

* [Qemu-devel] [RFC 27/48] target/sh4: prepare for 2-pass translation (WIP)
  2018-10-25 17:20 [Qemu-devel] [RFC 00/48] Plugin support Emilio G. Cota
                   ` (25 preceding siblings ...)
  2018-10-25 17:20 ` [Qemu-devel] [RFC 26/48] target/ppc: " Emilio G. Cota
@ 2018-10-25 17:20 ` Emilio G. Cota
  2018-10-25 17:20 ` [Qemu-devel] [RFC 28/48] target/i386: prepare for 2-pass translation Emilio G. Cota
                   ` (21 subsequent siblings)
  48 siblings, 0 replies; 132+ messages in thread
From: Emilio G. Cota @ 2018-10-25 17:20 UTC (permalink / raw)
  To: qemu-devel
  Cc: Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Alex Bennée, Stefan Hajnoczi

XXX: cleanly get the gUSA instructions

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

diff --git a/target/sh4/translate.c b/target/sh4/translate.c
index ea88d46c04..a3b0fb46a2 100644
--- a/target/sh4/translate.c
+++ b/target/sh4/translate.c
@@ -2333,6 +2333,7 @@ static void sh4_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs,
 #endif
 
     ctx->opcode = cpu_lduw_code(env, ctx->base.pc_next);
+    qemu_plugin_insn_append(plugin_insn, &ctx->opcode, sizeof(ctx->opcode));
     decode_opc(ctx);
     ctx->base.pc_next += 2;
 }
@@ -2381,6 +2382,8 @@ static const TranslatorOps sh4_tr_ops = {
     .translate_insn     = sh4_tr_translate_insn,
     .tb_stop            = sh4_tr_tb_stop,
     .disas_log          = sh4_tr_disas_log,
+    .ctx_base_offset    = offsetof(DisasContext, base),
+    .ctx_size           = sizeof(DisasContext),
 };
 
 void gen_intermediate_code(CPUState *cs, TranslationBlock *tb)
-- 
2.17.1

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

* [Qemu-devel] [RFC 28/48] target/i386: prepare for 2-pass translation
  2018-10-25 17:20 [Qemu-devel] [RFC 00/48] Plugin support Emilio G. Cota
                   ` (26 preceding siblings ...)
  2018-10-25 17:20 ` [Qemu-devel] [RFC 27/48] target/sh4: prepare for 2-pass translation (WIP) Emilio G. Cota
@ 2018-10-25 17:20 ` Emilio G. Cota
  2018-10-25 17:20 ` [Qemu-devel] [RFC 29/48] target/hppa: " Emilio G. Cota
                   ` (20 subsequent siblings)
  48 siblings, 0 replies; 132+ messages in thread
From: Emilio G. Cota @ 2018-10-25 17:20 UTC (permalink / raw)
  To: qemu-devel
  Cc: Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Alex Bennée, Stefan Hajnoczi

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

diff --git a/target/i386/translate.c b/target/i386/translate.c
index 86e59d7bf7..1d7b20bce3 100644
--- a/target/i386/translate.c
+++ b/target/i386/translate.c
@@ -142,6 +142,7 @@ typedef struct DisasContext {
     TCGv_i32 tmp3_i32;
     TCGv_i64 tmp1_i64;
 
+    struct qemu_plugin_insn *plugin_insn;
     sigjmp_buf jmpbuf;
 } DisasContext;
 
@@ -1900,28 +1901,43 @@ 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));
+    uint8_t ret = cpu_ldub_code(env, advance_pc(env, s, 1));
+
+    qemu_plugin_insn_append(s->plugin_insn, &ret, sizeof(ret));
+    return ret;
 }
 
 static inline int16_t x86_ldsw_code(CPUX86State *env, DisasContext *s)
 {
-    return cpu_ldsw_code(env, advance_pc(env, s, 2));
+    int16_t ret = cpu_ldsw_code(env, advance_pc(env, s, 2));
+
+    qemu_plugin_insn_append(s->plugin_insn, &ret, sizeof(ret));
+    return ret;
 }
 
 static inline uint16_t x86_lduw_code(CPUX86State *env, DisasContext *s)
 {
-    return cpu_lduw_code(env, advance_pc(env, s, 2));
+    uint16_t ret = cpu_lduw_code(env, advance_pc(env, s, 2));
+
+    qemu_plugin_insn_append(s->plugin_insn, &ret, sizeof(ret));
+    return ret;
 }
 
 static inline uint32_t x86_ldl_code(CPUX86State *env, DisasContext *s)
 {
-    return cpu_ldl_code(env, advance_pc(env, s, 4));
+    uint32_t ret = cpu_ldl_code(env, advance_pc(env, s, 4));
+
+    qemu_plugin_insn_append(s->plugin_insn, &ret, sizeof(ret));
+    return ret;
 }
 
 #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));
+    uint64_t ret = cpu_ldq_code(env, advance_pc(env, s, 8));
+
+    qemu_plugin_insn_append(s->plugin_insn, &ret, sizeof(ret));
+    return ret;
 }
 #endif
 
@@ -4473,7 +4489,8 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
 
 /* convert one instruction. s->base.is_jmp is set if the translation must
    be stopped. Return the next pc value */
-static target_ulong disas_insn(DisasContext *s, CPUState *cpu)
+static target_ulong disas_insn(DisasContext *s, CPUState *cpu,
+                               struct qemu_plugin_insn *plugin_insn)
 {
     CPUX86State *env = cpu->env_ptr;
     int b, prefixes;
@@ -4484,6 +4501,8 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu)
     int rex_w, rex_r;
     target_ulong pc_start = s->base.pc_next;
 
+    s->plugin_insn = plugin_insn;
+
     s->pc_start = s->pc = pc_start;
     s->override = -1;
 #ifdef TARGET_X86_64
@@ -8529,7 +8548,7 @@ static void i386_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu,
                                    struct qemu_plugin_insn *plugin_insn)
 {
     DisasContext *dc = container_of(dcbase, DisasContext, base);
-    target_ulong pc_next = disas_insn(dc, cpu);
+    target_ulong pc_next = disas_insn(dc, cpu, plugin_insn);
 
     if (dc->tf || (dc->base.tb->flags & HF_INHIBIT_IRQ_MASK)) {
         /* if single step mode, we generate only one instruction and
@@ -8584,6 +8603,8 @@ static const TranslatorOps i386_tr_ops = {
     .translate_insn     = i386_tr_translate_insn,
     .tb_stop            = i386_tr_tb_stop,
     .disas_log          = i386_tr_disas_log,
+    .ctx_base_offset    = offsetof(DisasContext, base),
+    .ctx_size           = sizeof(DisasContext),
 };
 
 /* generate intermediate code for basic block 'tb'.  */
-- 
2.17.1

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

* [Qemu-devel] [RFC 29/48] target/hppa: prepare for 2-pass translation
  2018-10-25 17:20 [Qemu-devel] [RFC 00/48] Plugin support Emilio G. Cota
                   ` (27 preceding siblings ...)
  2018-10-25 17:20 ` [Qemu-devel] [RFC 28/48] target/i386: prepare for 2-pass translation Emilio G. Cota
@ 2018-10-25 17:20 ` Emilio G. Cota
  2018-10-25 17:20 ` [Qemu-devel] [RFC 30/48] target/m68k: " Emilio G. Cota
                   ` (19 subsequent siblings)
  48 siblings, 0 replies; 132+ messages in thread
From: Emilio G. Cota @ 2018-10-25 17:20 UTC (permalink / raw)
  To: qemu-devel
  Cc: Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Alex Bennée, Stefan Hajnoczi

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

diff --git a/target/hppa/translate.c b/target/hppa/translate.c
index 6c2a7fbc46..08ebbeb21c 100644
--- a/target/hppa/translate.c
+++ b/target/hppa/translate.c
@@ -4757,6 +4757,8 @@ static void hppa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs,
            the page permissions for execute.  */
         uint32_t insn = cpu_ldl_code(env, ctx->base.pc_next);
 
+        qemu_plugin_insn_append(plugin_insn, &insn, sizeof(insn));
+
         /* Set up the IA queue for the next insn.
            This will be overwritten by a branch.  */
         if (ctx->iaoq_b == -1) {
@@ -4887,6 +4889,8 @@ static const TranslatorOps hppa_tr_ops = {
     .translate_insn     = hppa_tr_translate_insn,
     .tb_stop            = hppa_tr_tb_stop,
     .disas_log          = hppa_tr_disas_log,
+    .ctx_base_offset    = offsetof(DisasContext, base),
+    .ctx_size           = sizeof(DisasContext),
 };
 
 void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb)
-- 
2.17.1

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

* [Qemu-devel] [RFC 30/48] target/m68k: prepare for 2-pass translation
  2018-10-25 17:20 [Qemu-devel] [RFC 00/48] Plugin support Emilio G. Cota
                   ` (28 preceding siblings ...)
  2018-10-25 17:20 ` [Qemu-devel] [RFC 29/48] target/hppa: " Emilio G. Cota
@ 2018-10-25 17:20 ` Emilio G. Cota
  2018-10-25 17:20 ` [Qemu-devel] [RFC 31/48] target/mips: prepare for 2-pass translation (WIP) Emilio G. Cota
                   ` (18 subsequent siblings)
  48 siblings, 0 replies; 132+ messages in thread
From: Emilio G. Cota @ 2018-10-25 17:20 UTC (permalink / raw)
  To: qemu-devel
  Cc: Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Alex Bennée, Stefan Hajnoczi

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

diff --git a/target/m68k/translate.c b/target/m68k/translate.c
index dd7d868b25..9b5a4b1eb5 100644
--- a/target/m68k/translate.c
+++ b/target/m68k/translate.c
@@ -116,6 +116,7 @@ typedef struct DisasContext {
     int done_mac;
     int writeback_mask;
     TCGv writeback[8];
+    struct qemu_plugin_insn *plugin_insn;
 #define MAX_TO_RELEASE 8
     int release_count;
     TCGv release[MAX_TO_RELEASE];
@@ -375,6 +376,7 @@ static inline uint16_t read_im16(CPUM68KState *env, DisasContext *s)
     uint16_t im;
     im = cpu_lduw_code(env, s->pc);
     s->pc += 2;
+    qemu_plugin_insn_append(s->plugin_insn, &im, sizeof(im));
     return im;
 }
 
@@ -6092,7 +6094,10 @@ static void m68k_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu,
 {
     DisasContext *dc = container_of(dcbase, DisasContext, base);
     CPUM68KState *env = cpu->env_ptr;
-    uint16_t insn = read_im16(env, dc);
+    uint16_t insn;
+
+    dc->plugin_insn = plugin_insn;
+    insn = read_im16(env, dc);
 
     opcode_table[insn](env, dc, insn);
     do_writebacks(dc);
@@ -6167,6 +6172,8 @@ static const TranslatorOps m68k_tr_ops = {
     .translate_insn     = m68k_tr_translate_insn,
     .tb_stop            = m68k_tr_tb_stop,
     .disas_log          = m68k_tr_disas_log,
+    .ctx_base_offset    = offsetof(DisasContext, base),
+    .ctx_size           = sizeof(DisasContext),
 };
 
 void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb)
-- 
2.17.1

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

* [Qemu-devel] [RFC 31/48] target/mips: prepare for 2-pass translation (WIP)
  2018-10-25 17:20 [Qemu-devel] [RFC 00/48] Plugin support Emilio G. Cota
                   ` (29 preceding siblings ...)
  2018-10-25 17:20 ` [Qemu-devel] [RFC 30/48] target/m68k: " Emilio G. Cota
@ 2018-10-25 17:20 ` Emilio G. Cota
  2018-10-25 17:20 ` [Qemu-devel] [RFC 32/48] target/alpha: prepare for 2-pass translation Emilio G. Cota
                   ` (17 subsequent siblings)
  48 siblings, 0 replies; 132+ messages in thread
From: Emilio G. Cota @ 2018-10-25 17:20 UTC (permalink / raw)
  To: qemu-devel
  Cc: Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Alex Bennée, Stefan Hajnoczi

XXX: fill in plugin_insn

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

diff --git a/target/mips/translate.c b/target/mips/translate.c
index efafc6e795..46ed40cf8f 100644
--- a/target/mips/translate.c
+++ b/target/mips/translate.c
@@ -25455,6 +25455,8 @@ static const TranslatorOps mips_tr_ops = {
     .translate_insn     = mips_tr_translate_insn,
     .tb_stop            = mips_tr_tb_stop,
     .disas_log          = mips_tr_disas_log,
+    .ctx_base_offset    = offsetof(DisasContext, base),
+    .ctx_size           = sizeof(DisasContext),
 };
 
 void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb)
-- 
2.17.1

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

* [Qemu-devel] [RFC 32/48] target/alpha: prepare for 2-pass translation
  2018-10-25 17:20 [Qemu-devel] [RFC 00/48] Plugin support Emilio G. Cota
                   ` (30 preceding siblings ...)
  2018-10-25 17:20 ` [Qemu-devel] [RFC 31/48] target/mips: prepare for 2-pass translation (WIP) Emilio G. Cota
@ 2018-10-25 17:20 ` Emilio G. Cota
  2018-10-25 17:20 ` [Qemu-devel] [RFC 33/48] target/riscv: " Emilio G. Cota
                   ` (16 subsequent siblings)
  48 siblings, 0 replies; 132+ messages in thread
From: Emilio G. Cota @ 2018-10-25 17:20 UTC (permalink / raw)
  To: qemu-devel
  Cc: Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Alex Bennée, Stefan Hajnoczi

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

diff --git a/target/alpha/translate.c b/target/alpha/translate.c
index 72a302e102..21405df2b8 100644
--- a/target/alpha/translate.c
+++ b/target/alpha/translate.c
@@ -2990,6 +2990,7 @@ static void alpha_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu,
     CPUAlphaState *env = cpu->env_ptr;
     uint32_t insn = cpu_ldl_code(env, ctx->base.pc_next);
 
+    qemu_plugin_insn_append(plugin_insn, &insn, sizeof(insn));
     ctx->base.pc_next += 4;
     ctx->base.is_jmp = translate_one(ctx, insn);
 
@@ -3046,6 +3047,8 @@ static const TranslatorOps alpha_tr_ops = {
     .translate_insn     = alpha_tr_translate_insn,
     .tb_stop            = alpha_tr_tb_stop,
     .disas_log          = alpha_tr_disas_log,
+    .ctx_base_offset    = offsetof(DisasContext, base),
+    .ctx_size           = sizeof(DisasContext),
 };
 
 void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb)
-- 
2.17.1

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

* [Qemu-devel] [RFC 33/48] target/riscv: prepare for 2-pass translation
  2018-10-25 17:20 [Qemu-devel] [RFC 00/48] Plugin support Emilio G. Cota
                   ` (31 preceding siblings ...)
  2018-10-25 17:20 ` [Qemu-devel] [RFC 32/48] target/alpha: prepare for 2-pass translation Emilio G. Cota
@ 2018-10-25 17:20 ` Emilio G. Cota
  2018-10-25 17:20 ` [Qemu-devel] [RFC 34/48] target/s390x: " Emilio G. Cota
                   ` (15 subsequent siblings)
  48 siblings, 0 replies; 132+ messages in thread
From: Emilio G. Cota @ 2018-10-25 17:20 UTC (permalink / raw)
  To: qemu-devel
  Cc: Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Alex Bennée, Stefan Hajnoczi

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

diff --git a/target/riscv/translate.c b/target/riscv/translate.c
index a33cf6802b..6f5be8e7d7 100644
--- a/target/riscv/translate.c
+++ b/target/riscv/translate.c
@@ -1851,6 +1851,7 @@ static void riscv_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu,
 
     ctx->opcode = cpu_ldl_code(env, ctx->base.pc_next);
     decode_opc(env, ctx);
+    qemu_plugin_insn_append(plugin_insn, &ctx->opcode, sizeof(ctx->opcode));
     ctx->base.pc_next = ctx->pc_succ_insn;
 
     if (ctx->base.is_jmp == DISAS_NEXT) {
@@ -1892,6 +1893,8 @@ static const TranslatorOps riscv_tr_ops = {
     .translate_insn     = riscv_tr_translate_insn,
     .tb_stop            = riscv_tr_tb_stop,
     .disas_log          = riscv_tr_disas_log,
+    .ctx_base_offset    = offsetof(DisasContext, base),
+    .ctx_size           = sizeof(DisasContext),
 };
 
 void gen_intermediate_code(CPUState *cs, TranslationBlock *tb)
-- 
2.17.1

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

* [Qemu-devel] [RFC 34/48] target/s390x: prepare for 2-pass translation
  2018-10-25 17:20 [Qemu-devel] [RFC 00/48] Plugin support Emilio G. Cota
                   ` (32 preceding siblings ...)
  2018-10-25 17:20 ` [Qemu-devel] [RFC 33/48] target/riscv: " Emilio G. Cota
@ 2018-10-25 17:20 ` Emilio G. Cota
  2018-10-25 17:20 ` [Qemu-devel] [RFC 35/48] target/sparc: " Emilio G. Cota
                   ` (14 subsequent siblings)
  48 siblings, 0 replies; 132+ messages in thread
From: Emilio G. Cota @ 2018-10-25 17:20 UTC (permalink / raw)
  To: qemu-devel
  Cc: Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Alex Bennée, Stefan Hajnoczi

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

diff --git a/target/s390x/translate.c b/target/s390x/translate.c
index 6ac1a8d821..0c41e0d83a 100644
--- a/target/s390x/translate.c
+++ b/target/s390x/translate.c
@@ -5934,13 +5934,39 @@ static void extract_field(DisasFields *o, const DisasField *f, uint64_t insn)
     o->c[f->indexC] = r;
 }
 
+static inline void
+s390_plugin_insn(uint64_t insn, struct qemu_plugin_insn *plugin_insn, int len)
+{
+    uint16_t insn2;
+    uint32_t insn4;
+
+    if (plugin_insn == NULL) {
+        return;
+    }
+
+    switch (len) {
+    case 2:
+        insn2 = insn;
+        qemu_plugin_insn_append(plugin_insn, &insn2, sizeof(insn2));
+        break;
+    case 4:
+        insn4 = insn;
+        qemu_plugin_insn_append(plugin_insn, &insn4, sizeof(insn4));
+        break;
+    default:
+        g_assert_not_reached();
+    }
+}
+
 /* Lookup the insn at the current PC, extracting the operands into O and
    returning the info struct for the insn.  Returns NULL for invalid insn.  */
 
 static const DisasInsn *extract_insn(CPUS390XState *env, DisasContext *s,
-                                     DisasFields *f)
+                                     DisasFields *f,
+                                     struct qemu_plugin_insn *plugin_insn)
 {
     uint64_t insn, pc = s->base.pc_next;
+    uint64_t insn4;
     int op, op2, ilen;
     const DisasInsn *info;
 
@@ -5960,13 +5986,19 @@ static const DisasInsn *extract_insn(CPUS390XState *env, DisasContext *s,
         ilen = get_ilen(op);
         switch (ilen) {
         case 2:
-            insn = insn << 48;
+            s390_plugin_insn(insn, plugin_insn, 2);
+            insn <<= 48;
             break;
         case 4:
-            insn = ld_code4(env, pc) << 32;
+            insn = ld_code4(env, pc);
+            s390_plugin_insn(insn, plugin_insn, 4);
+            insn <<= 32;
             break;
         case 6:
-            insn = (insn << 48) | (ld_code4(env, pc + 2) << 16);
+            s390_plugin_insn(insn, plugin_insn, 2);
+            insn4 = ld_code4(env, pc + 2);
+            s390_plugin_insn(insn4, plugin_insn, 4);
+            insn = (insn << 48) | (insn4 << 16);
             break;
         default:
             g_assert_not_reached();
@@ -6049,7 +6081,8 @@ static bool is_fp_pair(int reg)
     return !(reg & 0x2);
 }
 
-static DisasJumpType translate_one(CPUS390XState *env, DisasContext *s)
+static DisasJumpType translate_one(CPUS390XState *env, DisasContext *s,
+                                   struct qemu_plugin_insn *plugin_insn)
 {
     const DisasInsn *insn;
     DisasJumpType ret = DISAS_NEXT;
@@ -6057,7 +6090,7 @@ static DisasJumpType translate_one(CPUS390XState *env, DisasContext *s)
     DisasOps o;
 
     /* Search for the insn in the table.  */
-    insn = extract_insn(env, s, &f);
+    insn = extract_insn(env, s, &f, plugin_insn);
 
     /* Not found means unimplemented/illegal opcode.  */
     if (insn == NULL) {
@@ -6234,7 +6267,7 @@ static void s390x_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs,
     CPUS390XState *env = cs->env_ptr;
     DisasContext *dc = container_of(dcbase, DisasContext, base);
 
-    dc->base.is_jmp = translate_one(env, dc);
+    dc->base.is_jmp = translate_one(env, dc, plugin_insn);
     if (dc->base.is_jmp == DISAS_NEXT) {
         uint64_t page_start;
 
@@ -6300,6 +6333,8 @@ static const TranslatorOps s390x_tr_ops = {
     .translate_insn     = s390x_tr_translate_insn,
     .tb_stop            = s390x_tr_tb_stop,
     .disas_log          = s390x_tr_disas_log,
+    .ctx_base_offset    = offsetof(DisasContext, base),
+    .ctx_size           = sizeof(DisasContext),
 };
 
 void gen_intermediate_code(CPUState *cs, TranslationBlock *tb)
-- 
2.17.1

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

* [Qemu-devel] [RFC 35/48] target/sparc: prepare for 2-pass translation
  2018-10-25 17:20 [Qemu-devel] [RFC 00/48] Plugin support Emilio G. Cota
                   ` (33 preceding siblings ...)
  2018-10-25 17:20 ` [Qemu-devel] [RFC 34/48] target/s390x: " Emilio G. Cota
@ 2018-10-25 17:20 ` Emilio G. Cota
  2018-10-25 17:20 ` [Qemu-devel] [RFC 36/48] target/xtensa: " Emilio G. Cota
                   ` (13 subsequent siblings)
  48 siblings, 0 replies; 132+ messages in thread
From: Emilio G. Cota @ 2018-10-25 17:20 UTC (permalink / raw)
  To: qemu-devel
  Cc: Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Alex Bennée, Stefan Hajnoczi

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

diff --git a/target/sparc/translate.c b/target/sparc/translate.c
index 2fa8b68e0a..74889de426 100644
--- a/target/sparc/translate.c
+++ b/target/sparc/translate.c
@@ -5902,6 +5902,7 @@ static void sparc_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs,
     unsigned int insn;
 
     insn = cpu_ldl_code(env, dc->pc);
+    qemu_plugin_insn_append(plugin_insn, &insn, sizeof(insn));
     dc->base.pc_next += 4;
     disas_sparc_insn(dc, insn);
 
@@ -5961,6 +5962,8 @@ static const TranslatorOps sparc_tr_ops = {
     .translate_insn     = sparc_tr_translate_insn,
     .tb_stop            = sparc_tr_tb_stop,
     .disas_log          = sparc_tr_disas_log,
+    .ctx_base_offset    = offsetof(DisasContext, base),
+    .ctx_size           = sizeof(DisasContext),
 };
 
 void gen_intermediate_code(CPUState *cs, TranslationBlock *tb)
-- 
2.17.1

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

* [Qemu-devel] [RFC 36/48] target/xtensa: prepare for 2-pass translation
  2018-10-25 17:20 [Qemu-devel] [RFC 00/48] Plugin support Emilio G. Cota
                   ` (34 preceding siblings ...)
  2018-10-25 17:20 ` [Qemu-devel] [RFC 35/48] target/sparc: " Emilio G. Cota
@ 2018-10-25 17:20 ` Emilio G. Cota
  2018-10-25 17:20 ` [Qemu-devel] [RFC 37/48] target/openrisc: " Emilio G. Cota
                   ` (12 subsequent siblings)
  48 siblings, 0 replies; 132+ messages in thread
From: Emilio G. Cota @ 2018-10-25 17:20 UTC (permalink / raw)
  To: qemu-devel
  Cc: Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Alex Bennée, Stefan Hajnoczi

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

diff --git a/target/xtensa/translate.c b/target/xtensa/translate.c
index 14ab1c5ceb..83711f2a18 100644
--- a/target/xtensa/translate.c
+++ b/target/xtensa/translate.c
@@ -879,7 +879,8 @@ static inline unsigned xtensa_op0_insn_len(DisasContext *dc, uint8_t op0)
     return xtensa_isa_length_from_chars(dc->config->isa, &op0);
 }
 
-static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc)
+static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc,
+                              struct qemu_plugin_insn *plugin_insn)
 {
     xtensa_isa isa = dc->config->isa;
     unsigned char b[MAX_INSN_LENGTH] = {cpu_ldub_code(env, dc->pc)};
@@ -916,6 +917,7 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc)
     for (i = 1; i < len; ++i) {
         b[i] = cpu_ldub_code(env, dc->pc + i);
     }
+    qemu_plugin_insn_append(plugin_insn, b, len);
     xtensa_insnbuf_from_chars(isa, dc->insnbuf, b, len);
     fmt = xtensa_format_decode(isa, dc->insnbuf);
     if (fmt == XTENSA_UNDEFINED) {
@@ -1183,7 +1185,7 @@ static void xtensa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu,
         gen_ibreak_check(env, dc);
     }
 
-    disas_xtensa_insn(env, dc);
+    disas_xtensa_insn(env, dc, plugin_insn);
 
     if (dc->icount) {
         tcg_gen_mov_i32(cpu_SR[ICOUNT], dc->next_icount);
@@ -1241,6 +1243,8 @@ static const TranslatorOps xtensa_translator_ops = {
     .translate_insn     = xtensa_tr_translate_insn,
     .tb_stop            = xtensa_tr_tb_stop,
     .disas_log          = xtensa_tr_disas_log,
+    .ctx_base_offset    = offsetof(DisasContext, base),
+    .ctx_size           = sizeof(DisasContext),
 };
 
 void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb)
-- 
2.17.1

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

* [Qemu-devel] [RFC 37/48] target/openrisc: prepare for 2-pass translation
  2018-10-25 17:20 [Qemu-devel] [RFC 00/48] Plugin support Emilio G. Cota
                   ` (35 preceding siblings ...)
  2018-10-25 17:20 ` [Qemu-devel] [RFC 36/48] target/xtensa: " Emilio G. Cota
@ 2018-10-25 17:20 ` Emilio G. Cota
  2018-10-25 17:20 ` [Qemu-devel] [RFC 38/48] translator: implement " Emilio G. Cota
                   ` (11 subsequent siblings)
  48 siblings, 0 replies; 132+ messages in thread
From: Emilio G. Cota @ 2018-10-25 17:20 UTC (permalink / raw)
  To: qemu-devel
  Cc: Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Alex Bennée, Stefan Hajnoczi

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

diff --git a/target/openrisc/translate.c b/target/openrisc/translate.c
index 947330e10a..2f084668f9 100644
--- a/target/openrisc/translate.c
+++ b/target/openrisc/translate.c
@@ -1308,6 +1308,8 @@ static void openrisc_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs,
     OpenRISCCPU *cpu = OPENRISC_CPU(cs);
     uint32_t insn = cpu_ldl_code(&cpu->env, dc->base.pc_next);
 
+    qemu_plugin_insn_append(plugin_insn, &insn, sizeof(insn));
+
     if (!decode(dc, insn)) {
         gen_illegal_exception(dc);
     }
@@ -1407,6 +1409,8 @@ static const TranslatorOps openrisc_tr_ops = {
     .translate_insn     = openrisc_tr_translate_insn,
     .tb_stop            = openrisc_tr_tb_stop,
     .disas_log          = openrisc_tr_disas_log,
+    .ctx_base_offset    = offsetof(DisasContext, base),
+    .ctx_size           = sizeof(DisasContext),
 };
 
 void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb)
-- 
2.17.1

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

* [Qemu-devel] [RFC 38/48] translator: implement 2-pass translation
  2018-10-25 17:20 [Qemu-devel] [RFC 00/48] Plugin support Emilio G. Cota
                   ` (36 preceding siblings ...)
  2018-10-25 17:20 ` [Qemu-devel] [RFC 37/48] target/openrisc: " Emilio G. Cota
@ 2018-10-25 17:20 ` Emilio G. Cota
  2018-11-26 15:16   ` Alex Bennée
  2018-10-25 17:20 ` [Qemu-devel] [RFC 39/48] plugin: add API symbols to qemu-plugins.symbols Emilio G. Cota
                   ` (10 subsequent siblings)
  48 siblings, 1 reply; 132+ messages in thread
From: Emilio G. Cota @ 2018-10-25 17:20 UTC (permalink / raw)
  To: qemu-devel
  Cc: Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Alex Bennée, Stefan Hajnoczi

The second pass only occurs when a plugin has subscribed to
TB translation events.

Signed-off-by: Emilio G. Cota <cota@braap.org>
---
 tcg/tcg.h              |  8 ++++
 accel/tcg/translator.c | 91 +++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 97 insertions(+), 2 deletions(-)

diff --git a/tcg/tcg.h b/tcg/tcg.h
index d5afe25c97..479b57d65f 100644
--- a/tcg/tcg.h
+++ b/tcg/tcg.h
@@ -720,6 +720,14 @@ struct TCGContext {
 
     TCGLabel *exitreq_label;
 
+    /*
+     * 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;
     struct qemu_plugin_dyn_cb_arr *plugin_mem_cb;
     struct qemu_plugin_insn *plugin_insn;
 
diff --git a/accel/tcg/translator.c b/accel/tcg/translator.c
index 8591e4b72a..88f9ac62a3 100644
--- a/accel/tcg/translator.c
+++ b/accel/tcg/translator.c
@@ -17,6 +17,7 @@
 #include "exec/gen-icount.h"
 #include "exec/log.h"
 #include "exec/translator.h"
+#include "exec/plugin-gen.h"
 
 /* Pairs with tcg_clear_temp_count.
    To be called by #TranslatorOps.{translate_insn,tb_stop} if
@@ -35,6 +36,21 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
                      CPUState *cpu, TranslationBlock *tb)
 {
     int bp_insn = 0;
+    int insn_idx = 0;
+    bool tb_trans_cb = false;
+    bool first_pass = true; /* second pass otherwise */
+    void *saved_dc = g_alloca(ops->ctx_size);
+    /* tb->plugin_mask is a u32 */
+    unsigned long plugin_mask = tb->plugin_mask;
+    struct qemu_plugin_tb *plugin_tb = &tcg_ctx->plugin_tb;
+
+    if (test_bit(QEMU_PLUGIN_EV_VCPU_TB_TRANS, &plugin_mask)) {
+        tb_trans_cb = true;
+        plugin_tb->cbs.n = 0;
+        plugin_tb->n = 0;
+        plugin_tb->vaddr = tb->pc;
+        tcg_ctx->plugin_mem_cb = NULL;
+    }
 
     /* Initialize DisasContext */
     db->tb = tb;
@@ -56,6 +72,21 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
         db->max_insns = 1;
     }
 
+ translate:
+    tcg_func_start(tcg_ctx);
+
+    /* See the "2-pass translation" comment below */
+    if (tb_trans_cb) {
+        void *dc = db;
+
+        dc -= ops->ctx_base_offset;
+        if (first_pass) {
+            memcpy(saved_dc, dc, ops->ctx_size);
+        } else {
+            memcpy(dc, saved_dc, ops->ctx_size);
+        }
+    }
+
     ops->init_disas_context(db, cpu);
     tcg_debug_assert(db->is_jmp == DISAS_NEXT);  /* no early exit */
 
@@ -67,7 +98,53 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
     ops->tb_start(db, cpu);
     tcg_debug_assert(db->is_jmp == DISAS_NEXT);  /* no early exit */
 
+    if (!first_pass && plugin_tb->cbs.n) {
+        qemu_plugin_gen_vcpu_udata_callbacks(&plugin_tb->cbs);
+    }
+
     while (true) {
+        struct qemu_plugin_insn *plugin_insn = NULL;
+        bool mem_helpers = false;
+
+        /*
+         * 2-pass translation.
+         *
+         * In the first pass we fully determine the TB.
+         * If no plugins have subscribed to TB translation events, we're done.
+         *
+         * If they have, we first share with plugins a TB descriptor so
+         * that plugins can subscribe to instruction-related events, e.g.
+         * memory accesses of particular instructions, or TB execution.
+         * With this info, which is kept in plugin_tb, we then do a second pass,
+         * inserting the appropriate instrumentation into the translated TB.
+         *
+         * Since all translation state is kept in DisasContext, we copy it
+         * before the first pass, and restore it before the second.
+         */
+        if (tb_trans_cb) {
+            if (first_pass) {
+                plugin_insn = qemu_plugin_tb_insn_get(plugin_tb);
+                tcg_ctx->plugin_insn = plugin_insn;
+                plugin_insn->vaddr = db->pc_next;
+                g_assert(tcg_ctx->plugin_mem_cb == NULL);
+            } else {
+                struct qemu_plugin_insn *insn = &plugin_tb->insns[insn_idx++];
+
+                tcg_ctx->plugin_insn = NULL;
+                if (unlikely(insn->exec_cbs.n)) {
+                    qemu_plugin_gen_vcpu_udata_callbacks(&insn->exec_cbs);
+                }
+                if (insn->mem_cbs.n) {
+                    tcg_ctx->plugin_mem_cb = &insn->mem_cbs;
+                    if (insn->calls_helpers) {
+                        qemu_plugin_gen_enable_mem_helpers(&insn->mem_cbs);
+                        mem_helpers = true;
+                    }
+                } else {
+                    tcg_ctx->plugin_mem_cb = NULL;
+                }
+            }
+        }
         db->num_insns++;
         ops->insn_start(db, cpu);
         tcg_debug_assert(db->is_jmp == DISAS_NEXT);  /* no early exit */
@@ -101,10 +178,14 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
             && (tb_cflags(db->tb) & CF_LAST_IO)) {
             /* Accept I/O on the last instruction.  */
             gen_io_start();
-            ops->translate_insn(db, cpu, NULL);
+            ops->translate_insn(db, cpu, plugin_insn);
             gen_io_end();
         } else {
-            ops->translate_insn(db, cpu, NULL);
+            ops->translate_insn(db, cpu, plugin_insn);
+        }
+
+        if (unlikely(mem_helpers)) {
+            qemu_plugin_gen_disable_mem_helpers();
         }
 
         /* Stop translation if translate_insn so indicated.  */
@@ -120,6 +201,12 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
         }
     }
 
+    if (tb_trans_cb && first_pass) {
+        qemu_plugin_tb_trans_cb(cpu, plugin_tb);
+        first_pass = false;
+        goto translate;
+    }
+
     /* Emit code to exit the TB, as indicated by db->is_jmp.  */
     ops->tb_stop(db, cpu);
     gen_tb_end(db->tb, db->num_insns - bp_insn);
-- 
2.17.1

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

* [Qemu-devel] [RFC 39/48] plugin: add API symbols to qemu-plugins.symbols
  2018-10-25 17:20 [Qemu-devel] [RFC 00/48] Plugin support Emilio G. Cota
                   ` (37 preceding siblings ...)
  2018-10-25 17:20 ` [Qemu-devel] [RFC 38/48] translator: implement " Emilio G. Cota
@ 2018-10-25 17:20 ` Emilio G. Cota
  2018-10-25 17:20 ` [Qemu-devel] [RFC 40/48] plugin: let plugins control the virtual clock Emilio G. Cota
                   ` (9 subsequent siblings)
  48 siblings, 0 replies; 132+ messages in thread
From: Emilio G. Cota @ 2018-10-25 17:20 UTC (permalink / raw)
  To: qemu-devel
  Cc: Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Alex Bennée, Stefan Hajnoczi

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

diff --git a/qemu-plugins.symbols b/qemu-plugins.symbols
new file mode 100644
index 0000000000..76a57e62bb
--- /dev/null
+++ b/qemu-plugins.symbols
@@ -0,0 +1,32 @@
+{
+  qemu_plugin_uninstall;
+  qemu_plugin_register_vcpu_init_cb;
+  qemu_plugin_register_vcpu_exit_cb;
+  qemu_plugin_register_vcpu_idle_cb;
+  qemu_plugin_register_vcpu_resume_cb;
+  qemu_plugin_register_vcpu_insn_exec_cb;
+  qemu_plugin_register_vcpu_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_mem_size_shift;
+  qemu_plugin_mem_is_sign_extended;
+  qemu_plugin_mem_is_big_endian;
+  qemu_plugin_mem_is_store;
+  qemu_plugin_vcpu_for_each;
+  qemu_plugin_n_vcpus;
+  qemu_plugin_n_max_vcpus;
+};
-- 
2.17.1

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

* [Qemu-devel] [RFC 40/48] plugin: let plugins control the virtual clock
  2018-10-25 17:20 [Qemu-devel] [RFC 00/48] Plugin support Emilio G. Cota
                   ` (38 preceding siblings ...)
  2018-10-25 17:20 ` [Qemu-devel] [RFC 39/48] plugin: add API symbols to qemu-plugins.symbols Emilio G. Cota
@ 2018-10-25 17:20 ` Emilio G. Cota
  2018-10-25 17:20 ` [Qemu-devel] [RFC 41/48] configure: add --enable-plugins Emilio G. Cota
                   ` (8 subsequent siblings)
  48 siblings, 0 replies; 132+ messages in thread
From: Emilio G. Cota @ 2018-10-25 17:20 UTC (permalink / raw)
  To: qemu-devel
  Cc: Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Alex Bennée, Stefan Hajnoczi

Signed-off-by: Emilio G. Cota <cota@braap.org>
---
 include/qemu/plugin-api.h |  9 +++++
 include/qemu/plugin.h     |  5 +++
 plugin.c                  | 79 +++++++++++++++++++++++++++++++++++++++
 stubs/plugin.c            |  9 +++++
 util/qemu-timer.c         |  3 ++
 qemu-plugins.symbols      |  1 +
 stubs/Makefile.objs       |  1 +
 7 files changed, 107 insertions(+)
 create mode 100644 stubs/plugin.c

diff --git a/include/qemu/plugin-api.h b/include/qemu/plugin-api.h
index 5c6bb45279..076353a2d2 100644
--- a/include/qemu/plugin-api.h
+++ b/include/qemu/plugin-api.h
@@ -218,6 +218,15 @@ void qemu_plugin_register_flush_cb(qemu_plugin_id_t id,
 void qemu_plugin_register_atexit_cb(qemu_plugin_id_t id,
                                     qemu_plugin_udata_cb_t cb, void *userdata);
 
+typedef int64_t (*qemu_plugin_clock_func_t)(void);
+
+/*
+ * Can only be called from plugin_init.
+ * Returns true on success
+ */
+bool qemu_plugin_register_virtual_clock(qemu_plugin_id_t id,
+                                        qemu_plugin_clock_func_t clock);
+
 /* returns -1 in user-mode */
 int qemu_plugin_n_vcpus(void);
 
diff --git a/include/qemu/plugin.h b/include/qemu/plugin.h
index 0da0f1b892..617161329f 100644
--- a/include/qemu/plugin.h
+++ b/include/qemu/plugin.h
@@ -127,6 +127,8 @@ struct qemu_plugin_tb {
     struct qemu_plugin_dyn_cb_arr cbs;
 };
 
+extern bool use_plugin_clock;
+
 static inline void qemu_plugin_insn_append(struct qemu_plugin_insn *insn,
                                            const void *from, size_t size)
 {
@@ -191,6 +193,7 @@ void qemu_plugin_flush_cb(void);
 void qemu_plugin_atexit_cb(void);
 
 void qemu_plugin_add_dyn_cb_arr(struct qemu_plugin_dyn_cb_arr *arr);
+int64_t plugin_get_clock(void);
 
 #else /* !CONFIG_PLUGINS */
 
@@ -234,6 +237,8 @@ static inline
 void qemu_plugin_add_dyn_cb_arr(struct qemu_plugin_dyn_cb_arr *arr)
 { }
 
+int64_t plugin_get_clock(void);
+
 #endif /* !CONFIG_PLUGINS */
 
 #endif /* QEMU_PLUGIN_H */
diff --git a/plugin.c b/plugin.c
index 76609f1da4..291767f2bb 100644
--- a/plugin.c
+++ b/plugin.c
@@ -71,6 +71,13 @@ struct qemu_plugin_state {
      * the code cache is flushed.
      */
     struct qht dyn_cb_arr_ht;
+    /*
+     * We support a single clock reference from plugins. We keep a pointer
+     * to the context of the plugin that provides the reference,
+     * so that we can remove the reference when the plugin is uninstalled.
+     */
+    qemu_plugin_clock_func_t clock_ref;
+    struct qemu_plugin_ctx *clock_ctx;
 };
 
 /*
@@ -104,6 +111,8 @@ QemuOptsList qemu_plugin_opts = {
 typedef int (*qemu_plugin_install_func_t)(qemu_plugin_id_t, int, char **);
 
 static struct qemu_plugin_state plugin;
+bool use_plugin_clock;
+static bool plugin_installing;
 
 static bool plugin_dyn_cb_arr_cmp(const void *ap, const void *bp)
 {
@@ -251,7 +260,9 @@ static int plugin_load(struct qemu_plugin_desc *desc)
     QTAILQ_INSERT_TAIL(&plugin.ctxs, ctx, entry);
     qemu_rec_mutex_unlock(&plugin.lock);
 
+    plugin_installing = true;
     rc = install(ctx->id, desc->argc, desc->argv);
+    plugin_installing = false;
     if (rc) {
         error_report("%s: qemu_plugin_install returned error code %d",
                      __func__, rc);
@@ -418,6 +429,10 @@ void qemu_plugin_uninstall(qemu_plugin_id_t id, qemu_plugin_uninstall_cb_t cb)
     for (ev = 0; ev < QEMU_PLUGIN_EV_MAX; ev++) {
         plugin_unregister_cb__locked(ctx, ev);
     }
+    if (ctx == plugin.clock_ctx) {
+        atomic_set(&plugin.clock_ref, NULL);
+        plugin.clock_ctx = NULL;
+    }
     qemu_rec_mutex_unlock(&plugin.lock);
 
     /* XXX how to flush when we're not in a vCPU thread? */
@@ -964,6 +979,70 @@ uint64_t qemu_plugin_ram_addr_from_host(void *haddr)
 #endif
 }
 
+#ifndef CONFIG_USER_ONLY
+static bool
+qemu_plugin_register_virtual_clock__locked(qemu_plugin_id_t id,
+                                           qemu_plugin_clock_func_t clock)
+{
+    struct qemu_plugin_ctx *ctx = id_to_ctx__locked(id);
+
+    if (!plugin_installing) {
+        error_report("plugin: can only call %s during plugin installation",
+                     __func__);
+        return false;
+    }
+    if (use_plugin_clock) {
+        error_report("plugin: clock reference already registered");
+        return false;
+    }
+    if (clock == NULL) {
+        error_report("%s: cannot pass NULL clock", __func__);
+        return false;
+    }
+    plugin.clock_ctx = ctx;
+    use_plugin_clock = true;
+    atomic_set(&plugin.clock_ref, clock);
+    return true;
+}
+#endif /* !CONFIG_USER_ONLY */
+
+bool qemu_plugin_register_virtual_clock(qemu_plugin_id_t id,
+                                        qemu_plugin_clock_func_t clock)
+{
+#ifdef CONFIG_USER_ONLY
+    return false;
+#else
+    bool ret;
+
+    qemu_rec_mutex_lock(&plugin.lock);
+    ret = qemu_plugin_register_virtual_clock__locked(id, clock);
+    qemu_rec_mutex_unlock(&plugin.lock);
+    return ret;
+#endif
+}
+
+#ifdef CONFIG_USER_ONLY
+int64_t plugin_get_clock(void)
+{
+    abort();
+    return 0;
+}
+#else
+/*
+ * Note: use_plugin_clock might be set, but the plugin providing the clock
+ * might have been uninstalled.
+ */
+int64_t plugin_get_clock(void)
+{
+    qemu_plugin_clock_func_t clock_ref = atomic_read(&plugin.clock_ref);
+
+    if (clock_ref) {
+        return clock_ref();
+    }
+    return cpu_get_clock();
+}
+#endif
+
 static void __attribute__((__constructor__)) plugin_init(void)
 {
     int i;
diff --git a/stubs/plugin.c b/stubs/plugin.c
new file mode 100644
index 0000000000..ab70365652
--- /dev/null
+++ b/stubs/plugin.c
@@ -0,0 +1,9 @@
+#include "qemu/osdep.h"
+#include "qemu/plugin.h"
+
+bool use_plugin_clock;
+
+int64_t plugin_get_clock(void)
+{
+    abort();
+}
diff --git a/util/qemu-timer.c b/util/qemu-timer.c
index eb60d8f73a..d20ab0331c 100644
--- a/util/qemu-timer.c
+++ b/util/qemu-timer.c
@@ -24,6 +24,7 @@
 
 #include "qemu/osdep.h"
 #include "qemu/main-loop.h"
+#include "qemu/plugin.h"
 #include "qemu/timer.h"
 #include "sysemu/replay.h"
 #include "sysemu/sysemu.h"
@@ -601,6 +602,8 @@ int64_t qemu_clock_get_ns(QEMUClockType type)
     case QEMU_CLOCK_VIRTUAL_EXT:
         if (use_icount) {
             return cpu_get_icount();
+        } else if (use_plugin_clock) {
+            return plugin_get_clock();
         } else {
             return cpu_get_clock();
         }
diff --git a/qemu-plugins.symbols b/qemu-plugins.symbols
index 76a57e62bb..93587b07e1 100644
--- a/qemu-plugins.symbols
+++ b/qemu-plugins.symbols
@@ -16,6 +16,7 @@
   qemu_plugin_register_vcpu_syscall_cb;
   qemu_plugin_register_vcpu_syscall_ret_cb;
   qemu_plugin_register_atexit_cb;
+  qemu_plugin_register_virtual_clock;
   qemu_plugin_tb_n_insns;
   qemu_plugin_tb_get_insn;
   qemu_plugin_tb_vaddr;
diff --git a/stubs/Makefile.objs b/stubs/Makefile.objs
index fbcdc0256d..f32bea429f 100644
--- a/stubs/Makefile.objs
+++ b/stubs/Makefile.objs
@@ -23,6 +23,7 @@ stub-obj-y += migr-blocker.o
 stub-obj-y += change-state-handler.o
 stub-obj-y += monitor.o
 stub-obj-y += notify-event.o
+stub-obj-y += plugin.o
 stub-obj-y += qtest.o
 stub-obj-y += replay.o
 stub-obj-y += runstate-check.o
-- 
2.17.1

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

* [Qemu-devel] [RFC 41/48] configure: add --enable-plugins
  2018-10-25 17:20 [Qemu-devel] [RFC 00/48] Plugin support Emilio G. Cota
                   ` (39 preceding siblings ...)
  2018-10-25 17:20 ` [Qemu-devel] [RFC 40/48] plugin: let plugins control the virtual clock Emilio G. Cota
@ 2018-10-25 17:20 ` Emilio G. Cota
  2018-11-27 12:11   ` Alex Bennée
  2018-11-27 12:43   ` Roman Bolshakov
  2018-10-25 17:20 ` [Qemu-devel] [RFC 42/48] vl: support -plugin option Emilio G. Cota
                   ` (7 subsequent siblings)
  48 siblings, 2 replies; 132+ messages in thread
From: Emilio G. Cota @ 2018-10-25 17:20 UTC (permalink / raw)
  To: qemu-devel
  Cc: Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Alex Bennée, Stefan Hajnoczi

For now only add it for ELF platforms, since we rely on the linker's
--dynamic-list flag to pass a list of symbols to be exported to the
executable. An alternative would be to use -rdynamic, but that would
expose all of QEMU's objects to plugins.

I have no experience with non-ELF systems but I suspect adding support
for those should be pretty easy.

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

diff --git a/configure b/configure
index 03bf719ca7..78e86098e5 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
 
@@ -477,6 +478,7 @@ libxml2=""
 docker="no"
 debug_mutex="no"
 libpmem=""
+plugins="no"
 
 # cross compilers defaults, can be overridden with --cross-cc-ARCH
 cross_cc_aarch64="aarch64-linux-gnu-gcc"
@@ -1443,6 +1445,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"
@@ -1633,6 +1639,8 @@ Advanced options (experts only):
                            xen pv domain builder
   --enable-debug-stack-usage
                            track the maximum stack usage of stacks created by qemu_alloc_stack
+  --enable-plugins
+                           enable plugins via shared library loading
 
 Optional features, enabled with --enable-FEATURE and
 disabled with --disable-FEATURE, default is enabled if available:
@@ -5204,6 +5212,42 @@ 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
+
+if compile_prog "" "-Wl,--dynamic-list=$TMPTXT" ; then
+  ld_dynamic_list="yes"
+else
+  if test "$plugins" = "yes" ; 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) used for this purpose."
+  fi
+fi
+
 ########################################
 # See if 16-byte vector operations are supported.
 # Even without a vector unit the compiler may expand these.
@@ -6091,6 +6135,7 @@ echo "VxHS block device $vxhs"
 echo "capstone          $capstone"
 echo "docker            $docker"
 echo "libpmem support   $libpmem"
+echo "plugin support    $plugins"
 
 if test "$sdl_too_old" = "yes"; then
 echo "-> Your SDL version is too old - please upgrade to have SDL support"
@@ -6849,6 +6894,12 @@ if test "$libpmem" = "yes" ; then
   echo "CONFIG_LIBPMEM=y" >> $config_host_mak
 fi
 
+if test "$plugins" = "yes" ; then
+    echo "CONFIG_PLUGINS=y" >> $config_host_mak
+    LIBS="-ldl $LIBS"
+    LDFLAGS="-Wl,--dynamic-list=\$(SRC_PATH)/qemu-plugins.symbols $LDFLAGS"
+fi
+
 if test "$tcg_interpreter" = "yes"; then
   QEMU_INCLUDES="-iquote \$(SRC_PATH)/tcg/tci $QEMU_INCLUDES"
 elif test "$ARCH" = "sparc64" ; then
-- 
2.17.1

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

* [Qemu-devel] [RFC 42/48] vl: support -plugin option
  2018-10-25 17:20 [Qemu-devel] [RFC 00/48] Plugin support Emilio G. Cota
                   ` (40 preceding siblings ...)
  2018-10-25 17:20 ` [Qemu-devel] [RFC 41/48] configure: add --enable-plugins Emilio G. Cota
@ 2018-10-25 17:20 ` Emilio G. Cota
  2018-10-25 17:20 ` [Qemu-devel] [RFC 43/48] linux-user: " Emilio G. Cota
                   ` (6 subsequent siblings)
  48 siblings, 0 replies; 132+ messages in thread
From: Emilio G. Cota @ 2018-10-25 17:20 UTC (permalink / raw)
  To: qemu-devel
  Cc: Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Alex Bennée, Stefan Hajnoczi

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

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

diff --git a/vl.c b/vl.c
index 4e25c78bff..ebb188552e 100644
--- a/vl.c
+++ b/vl.c
@@ -111,6 +111,7 @@ int main(int argc, char **argv)
 
 #include "trace-root.h"
 #include "trace/control.h"
+#include "qemu/plugin.h"
 #include "qemu/queue.h"
 #include "sysemu/arch_init.h"
 
@@ -2962,6 +2963,7 @@ int main(int argc, char **argv, char **envp)
     } BlockdevOptions_queue;
     QSIMPLEQ_HEAD(, BlockdevOptions_queue) bdo_queue
         = QSIMPLEQ_HEAD_INITIALIZER(bdo_queue);
+    struct qemu_plugin_list plugin_list = QTAILQ_HEAD_INITIALIZER(plugin_list);
 
     module_call_init(MODULE_INIT_TRACE);
 
@@ -2990,6 +2992,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);
@@ -3806,6 +3809,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);
@@ -4107,6 +4113,11 @@ int main(int argc, char **argv, char **envp)
                                machine_class->default_machine_opts, 0);
     }
 
+    /* process plugin before CPUs are created, but once -smp has been parsed */
+    if (qemu_plugin_load_list(&plugin_list)) {
+        exit(1);
+    }
+
     qemu_opts_foreach(qemu_find_opts("device"),
                       default_driver_check, NULL, NULL);
     qemu_opts_foreach(qemu_find_opts("global"),
diff --git a/qemu-options.hx b/qemu-options.hx
index f139459e80..5cd1d84df4 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -3838,6 +3838,23 @@ HXCOMM HX does not support conditional compilation of text.
 @findex -trace
 @include qemu-option-trace.texi
 ETEXI
+DEF("plugin", HAS_ARG, QEMU_OPTION_plugin,
+    "-plugin [file=]<file>[,arg=<string>]\n"
+    "                load a plugin\n",
+    QEMU_ARCH_ALL)
+STEXI
+@item -plugin file=@var{file}[,arg=@var{string}]
+@findex -plugin
+
+Load a plugin.
+
+@table @option
+@item file=@var{file}
+Load the given plugin from a shared library file.
+@item arg=@var{string}
+Argument string passed to the plugin. (Can be given multiple times.)
+@end table
+ETEXI
 
 HXCOMM Internal use
 DEF("qtest", HAS_ARG, QEMU_OPTION_qtest, "", QEMU_ARCH_ALL)
-- 
2.17.1

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

* [Qemu-devel] [RFC 43/48] linux-user: support -plugin option
  2018-10-25 17:20 [Qemu-devel] [RFC 00/48] Plugin support Emilio G. Cota
                   ` (41 preceding siblings ...)
  2018-10-25 17:20 ` [Qemu-devel] [RFC 42/48] vl: support -plugin option Emilio G. Cota
@ 2018-10-25 17:20 ` Emilio G. Cota
  2018-10-25 17:20 ` [Qemu-devel] [RFC 44/48] cpus: lockstep execution support Emilio G. Cota
                   ` (5 subsequent siblings)
  48 siblings, 0 replies; 132+ messages in thread
From: Emilio G. Cota @ 2018-10-25 17:20 UTC (permalink / raw)
  To: qemu-devel
  Cc: Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Alex Bennée, Stefan Hajnoczi

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

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

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

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

* [Qemu-devel] [RFC 44/48] cpus: lockstep execution support
  2018-10-25 17:20 [Qemu-devel] [RFC 00/48] Plugin support Emilio G. Cota
                   ` (42 preceding siblings ...)
  2018-10-25 17:20 ` [Qemu-devel] [RFC 43/48] linux-user: " Emilio G. Cota
@ 2018-10-25 17:20 ` Emilio G. Cota
  2018-11-14 16:43   ` Alex Bennée
  2018-10-25 17:20 ` [Qemu-devel] [RFC 45/48] plugin: " Emilio G. Cota
                   ` (4 subsequent siblings)
  48 siblings, 1 reply; 132+ messages in thread
From: Emilio G. Cota @ 2018-10-25 17:20 UTC (permalink / raw)
  To: qemu-devel
  Cc: Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Alex Bennée, Stefan Hajnoczi

Signed-off-by: Emilio G. Cota <cota@braap.org>
---
 include/qom/cpu.h |  27 +++++++++++
 cpus.c            | 113 +++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 139 insertions(+), 1 deletion(-)

diff --git a/include/qom/cpu.h b/include/qom/cpu.h
index 1ac56fe84b..5841421a20 100644
--- a/include/qom/cpu.h
+++ b/include/qom/cpu.h
@@ -279,6 +279,12 @@ typedef void (*run_on_cpu_func)(CPUState *cpu, run_on_cpu_data data);
 
 struct qemu_work_item;
 
+enum cpu_lockstep {
+    CPU_LOCKSTEP_RUN,
+    CPU_LOCKSTEP_STOP_REQUEST,
+    CPU_LOCKSTEP_WAIT,
+};
+
 #define CPU_UNSET_NUMA_NODE_ID -1
 #define CPU_TRACE_DSTATE_MAX_EVENTS 32
 
@@ -364,6 +370,7 @@ struct CPUState {
     QemuCond halt_cond;
     QSIMPLEQ_HEAD(, qemu_work_item) work_list;
     uint32_t halted;
+    enum cpu_lockstep lockstep;
     bool created;
     bool stop;
     bool stopped;
@@ -1019,6 +1026,26 @@ static inline void cpu_interrupt(CPUState *cpu, int mask)
     cpu_interrupt_handler(cpu, mask);
 }
 
+/**
+ * cpu_lockstep_enable - Enable execution of CPUs in lockstep
+ *
+ * Note: this feature is MTTCG-only.
+ * Lockstep execution allows CPUs to partition their execution into windows
+ * whose start is synchronized with that of other CPUs. This can have many
+ * uses, e.g. limiting execution skew in the guest.
+ *
+ * See also: cpu_lockstep_request_stop()
+ */
+void cpu_lockstep_enable(void);
+
+/**
+ * cpu_lockstep_request_stop - Finish the CPU's execution window
+ * @cpu: the CPU of interest
+ *
+ * See also: cpu_lockstep_enable()
+ */
+void cpu_lockstep_request_stop(CPUState *cpu);
+
 #else /* USER_ONLY */
 
 void cpu_interrupt(CPUState *cpu, int mask);
diff --git a/cpus.c b/cpus.c
index 3efe89354d..a446632a5c 100644
--- a/cpus.c
+++ b/cpus.c
@@ -80,6 +80,14 @@ int64_t max_advance;
 static QEMUTimer *throttle_timer;
 static unsigned int throttle_percentage;
 
+/* lockstep execution */
+static bool lockstep_enabled;
+static bool lockstep_ongoing_wakeup;
+static QemuMutex lockstep_lock;
+static int n_lockstep_running_cpus;
+static int n_lockstep_cpus;
+static CPUState **lockstep_cpus;
+
 #define CPU_THROTTLE_PCT_MIN 1
 #define CPU_THROTTLE_PCT_MAX 99
 #define CPU_THROTTLE_TIMESLICE_NS 10000000
@@ -1174,6 +1182,11 @@ static bool cpu_can_run(CPUState *cpu)
     if (cpu_is_stopped(cpu)) {
         return false;
     }
+    if (lockstep_enabled &&
+        (cpu->lockstep == CPU_LOCKSTEP_STOP_REQUEST ||
+         cpu->lockstep == CPU_LOCKSTEP_WAIT)) {
+        return false;
+    }
     return true;
 }
 
@@ -1246,6 +1259,7 @@ void qemu_init_cpu_loop(void)
 {
     qemu_init_sigbus();
     qemu_mutex_init(&qemu_global_mutex);
+    qemu_mutex_init(&lockstep_lock);
 
     qemu_thread_get_self(&io_thread);
 }
@@ -1298,6 +1312,90 @@ static void qemu_wait_io_event_common(CPUState *cpu)
     cpu_mutex_lock(cpu);
 }
 
+void cpu_lockstep_enable(void)
+{
+    atomic_xchg(&lockstep_enabled, true);
+}
+
+void cpu_lockstep_request_stop(CPUState *cpu)
+{
+    bool locked = cpu_mutex_locked(cpu);
+
+    g_assert(lockstep_enabled);
+    if (!locked) {
+        cpu_mutex_lock(cpu);
+    }
+    g_assert(cpu->lockstep == CPU_LOCKSTEP_RUN ||
+             cpu->lockstep == CPU_LOCKSTEP_STOP_REQUEST);
+    cpu->lockstep = CPU_LOCKSTEP_STOP_REQUEST;
+    if (!locked) {
+        cpu_mutex_unlock(cpu);
+    }
+    cpu_exit(cpu);
+}
+
+static void lockstep_resume(CPUState *cpu, run_on_cpu_data ignored)
+{
+    g_assert(lockstep_enabled);
+    cpu_mutex_lock(cpu);
+    g_assert(cpu->lockstep == CPU_LOCKSTEP_WAIT);
+    cpu->lockstep = CPU_LOCKSTEP_RUN;
+    cpu_mutex_unlock(cpu);
+}
+
+static void lockstep_check_stop(CPUState *cpu)
+{
+    if (!lockstep_enabled) {
+        return;
+    }
+    if (cpu->lockstep == CPU_LOCKSTEP_STOP_REQUEST ||
+        (cpu->lockstep == CPU_LOCKSTEP_RUN && cpu_thread_is_idle(cpu))) {
+        qemu_mutex_lock(&lockstep_lock);
+        cpu->lockstep = CPU_LOCKSTEP_WAIT;
+        n_lockstep_running_cpus--;
+        if (n_lockstep_running_cpus == 0) {
+            int i;
+
+            /* wake up all waiting cpus */
+            lockstep_ongoing_wakeup = true;
+            n_lockstep_running_cpus = n_lockstep_cpus;
+            qemu_mutex_unlock(&lockstep_lock);
+            cpu_mutex_unlock(cpu);
+            for (i = 0; i < n_lockstep_cpus; i++) {
+                run_on_cpu_no_bql(lockstep_cpus[i], lockstep_resume,
+                                  RUN_ON_CPU_NULL);
+            }
+            cpu_mutex_lock(cpu);
+            qemu_mutex_lock(&lockstep_lock);
+            lockstep_ongoing_wakeup = false;
+        }
+        qemu_mutex_unlock(&lockstep_lock);
+    }
+}
+
+static void cpu_lockstep_init(CPUState *cpu)
+{
+    if (!lockstep_enabled) {
+        return;
+    }
+    qemu_mutex_lock(&lockstep_lock);
+    /*
+     * HACK: avoid racing with a wakeup, which would miss the addition
+     * of this CPU; just wait until no wakeup is ongoing.
+     */
+    while (unlikely(lockstep_ongoing_wakeup)) {
+        qemu_mutex_unlock(&lockstep_lock);
+        sched_yield();
+        qemu_mutex_lock(&lockstep_lock);
+    }
+    lockstep_cpus = g_realloc(lockstep_cpus,
+                              (n_lockstep_cpus + 1) * sizeof(CPUState *));
+    lockstep_cpus[n_lockstep_cpus++] = cpu;
+    n_lockstep_running_cpus++;
+    qemu_mutex_unlock(&lockstep_lock);
+    cpu->lockstep = CPU_LOCKSTEP_RUN;
+}
+
 static void qemu_tcg_rr_wait_io_event(CPUState *cpu)
 {
     g_assert(qemu_mutex_iothread_locked());
@@ -1321,6 +1419,15 @@ static void qemu_tcg_rr_wait_io_event(CPUState *cpu)
     cpu_mutex_unlock(cpu);
 }
 
+static inline bool lockstep_is_waiting(CPUState *cpu)
+{
+    if (!lockstep_enabled) {
+        return true;
+    }
+    g_assert(cpu_mutex_locked(cpu));
+    return cpu->lockstep == CPU_LOCKSTEP_WAIT;
+}
+
 static void qemu_wait_io_event(CPUState *cpu)
 {
     bool asleep = false;
@@ -1328,7 +1435,9 @@ static void qemu_wait_io_event(CPUState *cpu)
     g_assert(cpu_mutex_locked(cpu));
     g_assert(!qemu_mutex_iothread_locked());
 
-    while (cpu_thread_is_idle(cpu)) {
+    lockstep_check_stop(cpu);
+
+    while (cpu_thread_is_idle(cpu) && lockstep_is_waiting(cpu)) {
         if (!asleep) {
             asleep = true;
             qemu_plugin_vcpu_idle_cb(cpu);
@@ -1884,6 +1993,8 @@ static void *qemu_tcg_cpu_thread_fn(void *arg)
     cpu->can_do_io = 1;
     current_cpu = cpu;
     qemu_cond_signal(&cpu->cond);
+    /* init lockstep */
+    cpu_lockstep_init(cpu);
 
     /* process any pending work */
     cpu->exit_request = 1;
-- 
2.17.1

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

* [Qemu-devel] [RFC 45/48] plugin: lockstep execution support
  2018-10-25 17:20 [Qemu-devel] [RFC 00/48] Plugin support Emilio G. Cota
                   ` (43 preceding siblings ...)
  2018-10-25 17:20 ` [Qemu-devel] [RFC 44/48] cpus: lockstep execution support Emilio G. Cota
@ 2018-10-25 17:20 ` Emilio G. Cota
  2018-11-27 18:20   ` Alex Bennée
  2018-10-25 17:20 ` [Qemu-devel] [RFC 46/48] plugin: add plugin-chan PCI device Emilio G. Cota
                   ` (3 subsequent siblings)
  48 siblings, 1 reply; 132+ messages in thread
From: Emilio G. Cota @ 2018-10-25 17:20 UTC (permalink / raw)
  To: qemu-devel
  Cc: Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Alex Bennée, Stefan Hajnoczi

Signed-off-by: Emilio G. Cota <cota@braap.org>
---
 include/qemu/plugin-api.h |  7 +++++++
 include/qemu/plugin.h     |  5 +++++
 cpus.c                    |  1 +
 plugin.c                  | 35 +++++++++++++++++++++++++++++++++++
 qemu-plugins.symbols      |  3 +++
 5 files changed, 51 insertions(+)

diff --git a/include/qemu/plugin-api.h b/include/qemu/plugin-api.h
index 076353a2d2..5062e20e08 100644
--- a/include/qemu/plugin-api.h
+++ b/include/qemu/plugin-api.h
@@ -227,6 +227,13 @@ typedef int64_t (*qemu_plugin_clock_func_t)(void);
 bool qemu_plugin_register_virtual_clock(qemu_plugin_id_t id,
                                         qemu_plugin_clock_func_t clock);
 
+void qemu_plugin_enable_lockstep_execution(void);
+
+void qemu_plugin_register_lockstep_cb(qemu_plugin_id_t id,
+                                      qemu_plugin_simple_cb_t cb);
+
+void qemu_plugin_end_time_slice(void);
+
 /* returns -1 in user-mode */
 int qemu_plugin_n_vcpus(void);
 
diff --git a/include/qemu/plugin.h b/include/qemu/plugin.h
index 617161329f..c19071bdbe 100644
--- a/include/qemu/plugin.h
+++ b/include/qemu/plugin.h
@@ -58,6 +58,7 @@ enum qemu_plugin_event {
     QEMU_PLUGIN_EV_VCPU_RESUME,
     QEMU_PLUGIN_EV_VCPU_SYSCALL,
     QEMU_PLUGIN_EV_VCPU_SYSCALL_RET,
+    QEMU_PLUGIN_EV_LOCKSTEP,
     QEMU_PLUGIN_EV_FLUSH,
     QEMU_PLUGIN_EV_ATEXIT,
     QEMU_PLUGIN_EV_MAX,
@@ -194,6 +195,7 @@ void qemu_plugin_atexit_cb(void);
 
 void qemu_plugin_add_dyn_cb_arr(struct qemu_plugin_dyn_cb_arr *arr);
 int64_t plugin_get_clock(void);
+void plugin_lockstep_cb(void);
 
 #else /* !CONFIG_PLUGINS */
 
@@ -237,6 +239,9 @@ static inline
 void qemu_plugin_add_dyn_cb_arr(struct qemu_plugin_dyn_cb_arr *arr)
 { }
 
+static inline void plugin_lockstep_cb(void)
+{ }
+
 int64_t plugin_get_clock(void);
 
 #endif /* !CONFIG_PLUGINS */
diff --git a/cpus.c b/cpus.c
index a446632a5c..8f490d1b11 100644
--- a/cpus.c
+++ b/cpus.c
@@ -1359,6 +1359,7 @@ static void lockstep_check_stop(CPUState *cpu)
             /* wake up all waiting cpus */
             lockstep_ongoing_wakeup = true;
             n_lockstep_running_cpus = n_lockstep_cpus;
+            plugin_lockstep_cb();
             qemu_mutex_unlock(&lockstep_lock);
             cpu_mutex_unlock(cpu);
             for (i = 0; i < n_lockstep_cpus; i++) {
diff --git a/plugin.c b/plugin.c
index 291767f2bb..117f303249 100644
--- a/plugin.c
+++ b/plugin.c
@@ -472,6 +472,7 @@ static void plugin_cb__simple(enum qemu_plugin_event ev)
 
     switch (ev) {
     case QEMU_PLUGIN_EV_FLUSH:
+    case QEMU_PLUGIN_EV_LOCKSTEP:
         QLIST_FOREACH_SAFE_RCU(cb, &plugin.cb_lists[ev], entry, next) {
             qemu_plugin_simple_cb_t func = cb->f.simple;
 
@@ -1043,6 +1044,40 @@ int64_t plugin_get_clock(void)
 }
 #endif
 
+/*
+ * We manage the CPU state changes; the plugin will control the length of the
+ * execution windows.
+ */
+void qemu_plugin_enable_lockstep_execution(void)
+{
+#ifdef CONFIG_USER_ONLY
+    abort();
+#else
+    cpu_lockstep_enable();
+#endif
+}
+
+void qemu_plugin_end_time_slice(void)
+{
+#ifdef CONFIG_USER_ONLY
+    abort();
+#else
+    g_assert(current_cpu);
+    cpu_lockstep_request_stop(current_cpu);
+#endif
+}
+
+void qemu_plugin_register_lockstep_cb(qemu_plugin_id_t id,
+                                      qemu_plugin_simple_cb_t cb)
+{
+    plugin_register_cb(id, QEMU_PLUGIN_EV_LOCKSTEP, cb);
+}
+
+void plugin_lockstep_cb(void)
+{
+    plugin_cb__simple(QEMU_PLUGIN_EV_LOCKSTEP);
+}
+
 static void __attribute__((__constructor__)) plugin_init(void)
 {
     int i;
diff --git a/qemu-plugins.symbols b/qemu-plugins.symbols
index 93587b07e1..a3268a40c7 100644
--- a/qemu-plugins.symbols
+++ b/qemu-plugins.symbols
@@ -1,5 +1,8 @@
 {
   qemu_plugin_uninstall;
+  qemu_plugin_enable_lockstep_execution;
+  qemu_plugin_end_time_slice;
+  qemu_plugin_register_lockstep_cb;
   qemu_plugin_register_vcpu_init_cb;
   qemu_plugin_register_vcpu_exit_cb;
   qemu_plugin_register_vcpu_idle_cb;
-- 
2.17.1

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

* [Qemu-devel] [RFC 46/48] plugin: add plugin-chan PCI device
  2018-10-25 17:20 [Qemu-devel] [RFC 00/48] Plugin support Emilio G. Cota
                   ` (44 preceding siblings ...)
  2018-10-25 17:20 ` [Qemu-devel] [RFC 45/48] plugin: " Emilio G. Cota
@ 2018-10-25 17:20 ` Emilio G. Cota
  2018-10-25 17:20 ` [Qemu-devel] [RFC 47/48] plugin: support guest hooks Emilio G. Cota
                   ` (2 subsequent siblings)
  48 siblings, 0 replies; 132+ messages in thread
From: Emilio G. Cota @ 2018-10-25 17:20 UTC (permalink / raw)
  To: qemu-devel
  Cc: Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Alex Bennée, Stefan Hajnoczi

This will allow communication between guest and plugins.

Signed-off-by: Emilio G. Cota <cota@braap.org>
---
 Makefile.target         |   2 +
 include/hw/pci/pci.h    |   2 +
 include/qemu/plugin.h   |   1 +
 hw/plugin/plugin-chan.c | 136 ++++++++++++++++++++++++++++++++++++++++
 plugin.c                |   4 ++
 5 files changed, 145 insertions(+)
 create mode 100644 hw/plugin/plugin-chan.c

diff --git a/Makefile.target b/Makefile.target
index 75637c285c..719699696d 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -156,6 +156,8 @@ else
 obj-y += hw/$(TARGET_BASE_ARCH)/
 endif
 
+obj-$(CONFIG_PLUGINS) += hw/plugin/plugin-chan.o
+
 GENERATED_FILES += hmp-commands.h hmp-commands-info.h
 
 endif # CONFIG_SOFTMMU
diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h
index e6514bba23..6878d02254 100644
--- a/include/hw/pci/pci.h
+++ b/include/hw/pci/pci.h
@@ -86,6 +86,8 @@ extern bool pci_available;
 #define PCI_DEVICE_ID_VIRTIO_9P          0x1009
 #define PCI_DEVICE_ID_VIRTIO_VSOCK       0x1012
 
+#define PCI_DEVICE_ID_QEMU_PLUGIN_CHAN   0x10f0
+
 #define PCI_VENDOR_ID_REDHAT             0x1b36
 #define PCI_DEVICE_ID_REDHAT_BRIDGE      0x0001
 #define PCI_DEVICE_ID_REDHAT_SERIAL      0x0002
diff --git a/include/qemu/plugin.h b/include/qemu/plugin.h
index c19071bdbe..f3c18d1032 100644
--- a/include/qemu/plugin.h
+++ b/include/qemu/plugin.h
@@ -196,6 +196,7 @@ void qemu_plugin_atexit_cb(void);
 void qemu_plugin_add_dyn_cb_arr(struct qemu_plugin_dyn_cb_arr *arr);
 int64_t plugin_get_clock(void);
 void plugin_lockstep_cb(void);
+void plugin_chan_xmit(uint32_t cmd, const void *data, size_t size);
 
 #else /* !CONFIG_PLUGINS */
 
diff --git a/hw/plugin/plugin-chan.c b/hw/plugin/plugin-chan.c
new file mode 100644
index 0000000000..c0749ce089
--- /dev/null
+++ b/hw/plugin/plugin-chan.c
@@ -0,0 +1,136 @@
+#include "qemu/osdep.h"
+#include "hw/pci/pci.h"
+#include "qemu/plugin.h"
+#include "qapi/error.h"
+#include "qemu/error-report.h"
+
+#define PLUGIN_CHAN_CFG_BAR    0
+#define PLUGIN_CHAN_CMD_BAR    1
+#define PLUGIN_CHAN_DATA_BAR   2
+
+struct plugin_chan_cfg {
+    uint32_t data_max_len;
+};
+
+struct plugin_chan {
+    PCIDevice dev;
+    MemoryRegion cfg_region;
+    MemoryRegion cmd_region;
+    MemoryRegion data_region;
+    void *data;
+    size_t data_size;
+    struct plugin_chan_cfg cfg;
+};
+
+static uint64_t chan_cfg_read(void *obj, hwaddr addr, unsigned size)
+{
+    struct plugin_chan *s = obj;
+
+    g_assert(size == 4);
+    switch (addr) {
+    case 0:
+        return s->cfg.data_max_len;
+        break;
+    }
+    g_assert_not_reached();
+    return 0;
+}
+
+static void chan_cmd_write(void *obj, hwaddr addr, uint64_t val, unsigned size)
+{
+    struct plugin_chan *s = obj;
+
+    g_assert(size == 4);
+    switch (addr) {
+    case 0:
+        plugin_chan_xmit(val, s->data_size ? s->data : NULL, s->data_size);
+        break;
+    case 4:
+        s->data_size = val;
+        break;
+    default:
+        g_assert_not_reached();
+    }
+}
+
+static const MemoryRegionOps chan_cfg_ops = {
+    .read = chan_cfg_read,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .impl = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    },
+};
+
+static const MemoryRegionOps chan_cmd_ops = {
+    .write = chan_cmd_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .impl = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    },
+};
+
+static void plugin_chan_realize(PCIDevice *pci_dev, Error **errp)
+{
+    struct plugin_chan *s = DO_UPCAST(struct plugin_chan, dev, pci_dev);
+    Error *err = NULL;
+
+    s->cfg.data_max_len = 4096;
+
+    pci_set_word(s->dev.config + PCI_COMMAND,
+                 PCI_COMMAND_IO | PCI_COMMAND_MEMORY);
+
+    /* data */
+    memory_region_init_ram(&s->data_region, OBJECT(s), "plugin_chan.data",
+                           pow2ceil(s->cfg.data_max_len), &err);
+    if (err) {
+        error_propagate(errp, err);
+        return;
+    }
+    pci_register_bar(&s->dev, PLUGIN_CHAN_DATA_BAR,
+                     PCI_BASE_ADDRESS_SPACE_MEMORY, &s->data_region);
+    s->data = qemu_map_ram_ptr(s->data_region.ram_block, 0);
+
+    /* config */
+    memory_region_init_io(&s->cfg_region, OBJECT(s), &chan_cfg_ops, s,
+                          "plugin_chan.cfg", sizeof(struct plugin_chan_cfg));
+    pci_register_bar(&s->dev, PLUGIN_CHAN_CFG_BAR,
+                     PCI_BASE_ADDRESS_SPACE_MEMORY, &s->cfg_region);
+
+    /* cmd */
+    memory_region_init_io(&s->cmd_region, OBJECT(s), &chan_cmd_ops, s,
+                          "plugin_chan.cmd", 8);
+    pci_register_bar(&s->dev, PLUGIN_CHAN_CMD_BAR,
+                     PCI_BASE_ADDRESS_SPACE_MEMORY, &s->cmd_region);
+}
+
+static void plugin_chan_class_init(ObjectClass *class, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(class);
+    PCIDeviceClass *p = PCI_DEVICE_CLASS(class);
+
+    p->realize = plugin_chan_realize;
+    p->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
+    p->device_id = PCI_DEVICE_ID_QEMU_PLUGIN_CHAN;
+    p->class_id = PCI_CLASS_MEMORY_RAM;
+    dc->desc = "Plugin communication channel between guest and host";
+}
+
+static TypeInfo plugin_chan_info = {
+    .name = "qemu-plugin-chan",
+    .parent = TYPE_PCI_DEVICE,
+    .instance_size = sizeof(struct plugin_chan),
+    .class_init = plugin_chan_class_init,
+    .interfaces = (InterfaceInfo[]) {
+        { INTERFACE_CONVENTIONAL_PCI_DEVICE },
+        { }
+    },
+};
+
+static void plugin_chan_register_types(void)
+{
+    type_register_static(&plugin_chan_info);
+}
+
+type_init(plugin_chan_register_types)
diff --git a/plugin.c b/plugin.c
index 117f303249..2bbc14e2f3 100644
--- a/plugin.c
+++ b/plugin.c
@@ -1078,6 +1078,10 @@ void plugin_lockstep_cb(void)
     plugin_cb__simple(QEMU_PLUGIN_EV_LOCKSTEP);
 }
 
+void plugin_chan_xmit(uint32_t cmd, const void *data, size_t size)
+{
+}
+
 static void __attribute__((__constructor__)) plugin_init(void)
 {
     int i;
-- 
2.17.1

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

* [Qemu-devel] [RFC 47/48] plugin: support guest hooks
  2018-10-25 17:20 [Qemu-devel] [RFC 00/48] Plugin support Emilio G. Cota
                   ` (45 preceding siblings ...)
  2018-10-25 17:20 ` [Qemu-devel] [RFC 46/48] plugin: add plugin-chan PCI device Emilio G. Cota
@ 2018-10-25 17:20 ` Emilio G. Cota
  2018-11-27 18:28   ` Alex Bennée
  2018-10-25 17:20 ` [Qemu-devel] [RFC 48/48] plugin: add a couple of very simple examples Emilio G. Cota
  2018-10-29  9:48 ` [Qemu-devel] [RFC 00/48] Plugin support Pavel Dovgalyuk
  48 siblings, 1 reply; 132+ messages in thread
From: Emilio G. Cota @ 2018-10-25 17:20 UTC (permalink / raw)
  To: qemu-devel
  Cc: Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Alex Bennée, Stefan Hajnoczi

These "hooks" are callbacks from the guest to plugins. This is
useful when we need guest-host communication, for instance to
signal the beginning/end of a certain "region of interest" in
the guest program. Simulators typically would use "magic"
instructions for this, but that is painful to maintain across
ISAs. Instead, we use plugin-chan PCI device through which we can
relay guest messages to the host.

Signed-off-by: Emilio G. Cota <cota@braap.org>
---
 include/qemu/plugin-api.h |  6 ++++++
 include/qemu/plugin.h     |  2 ++
 plugin.c                  | 13 +++++++++++++
 qemu-plugins.symbols      |  1 +
 4 files changed, 22 insertions(+)

diff --git a/include/qemu/plugin-api.h b/include/qemu/plugin-api.h
index 5062e20e08..a5faff6a2a 100644
--- a/include/qemu/plugin-api.h
+++ b/include/qemu/plugin-api.h
@@ -234,6 +234,12 @@ void qemu_plugin_register_lockstep_cb(qemu_plugin_id_t id,
 
 void qemu_plugin_end_time_slice(void);
 
+typedef void (*qemu_plugin_hook_cb_t)(uint32_t cmd, const void *data,
+                                      size_t size);
+
+void qemu_plugin_register_hook_cb(qemu_plugin_id_t id,
+                                  qemu_plugin_hook_cb_t cb);
+
 /* returns -1 in user-mode */
 int qemu_plugin_n_vcpus(void);
 
diff --git a/include/qemu/plugin.h b/include/qemu/plugin.h
index f3c18d1032..ced265a127 100644
--- a/include/qemu/plugin.h
+++ b/include/qemu/plugin.h
@@ -61,12 +61,14 @@ enum qemu_plugin_event {
     QEMU_PLUGIN_EV_LOCKSTEP,
     QEMU_PLUGIN_EV_FLUSH,
     QEMU_PLUGIN_EV_ATEXIT,
+    QEMU_PLUGIN_EV_HOOK,
     QEMU_PLUGIN_EV_MAX,
 };
 
 union qemu_plugin_cb_sig {
     qemu_plugin_simple_cb_t          simple;
     qemu_plugin_udata_cb_t           udata;
+    qemu_plugin_hook_cb_t            hook;
     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;
diff --git a/plugin.c b/plugin.c
index 2bbc14e2f3..4004451bdb 100644
--- a/plugin.c
+++ b/plugin.c
@@ -1078,8 +1078,21 @@ void plugin_lockstep_cb(void)
     plugin_cb__simple(QEMU_PLUGIN_EV_LOCKSTEP);
 }
 
+void qemu_plugin_register_hook_cb(qemu_plugin_id_t id, qemu_plugin_hook_cb_t cb)
+{
+    plugin_register_cb(id, QEMU_PLUGIN_EV_HOOK, cb);
+}
+
 void plugin_chan_xmit(uint32_t cmd, const void *data, size_t size)
 {
+    struct qemu_plugin_cb *cb, *next;
+    enum qemu_plugin_event ev = QEMU_PLUGIN_EV_HOOK;
+
+    QLIST_FOREACH_SAFE_RCU(cb, &plugin.cb_lists[ev], entry, next) {
+        qemu_plugin_hook_cb_t func = cb->f.hook;
+
+        func(cmd, data, size);
+    }
 }
 
 static void __attribute__((__constructor__)) plugin_init(void)
diff --git a/qemu-plugins.symbols b/qemu-plugins.symbols
index a3268a40c7..2e17693da8 100644
--- a/qemu-plugins.symbols
+++ b/qemu-plugins.symbols
@@ -31,6 +31,7 @@
   qemu_plugin_mem_is_big_endian;
   qemu_plugin_mem_is_store;
   qemu_plugin_vcpu_for_each;
+  qemu_plugin_register_hook_cb;
   qemu_plugin_n_vcpus;
   qemu_plugin_n_max_vcpus;
 };
-- 
2.17.1

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

* [Qemu-devel] [RFC 48/48] plugin: add a couple of very simple examples
  2018-10-25 17:20 [Qemu-devel] [RFC 00/48] Plugin support Emilio G. Cota
                   ` (46 preceding siblings ...)
  2018-10-25 17:20 ` [Qemu-devel] [RFC 47/48] plugin: support guest hooks Emilio G. Cota
@ 2018-10-25 17:20 ` Emilio G. Cota
  2018-10-29 10:59   ` Pavel Dovgalyuk
                     ` (2 more replies)
  2018-10-29  9:48 ` [Qemu-devel] [RFC 00/48] Plugin support Pavel Dovgalyuk
  48 siblings, 3 replies; 132+ messages in thread
From: Emilio G. Cota @ 2018-10-25 17:20 UTC (permalink / raw)
  To: qemu-devel
  Cc: Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Alex Bennée, Stefan Hajnoczi

Signed-off-by: Emilio G. Cota <cota@braap.org>
---
 plugin-examples/bbcount_avgsize_racy.c | 50 ++++++++++++++++++++++
 plugin-examples/mem_count_racy_both.c  | 58 ++++++++++++++++++++++++++
 plugin-examples/Makefile               | 31 ++++++++++++++
 3 files changed, 139 insertions(+)
 create mode 100644 plugin-examples/bbcount_avgsize_racy.c
 create mode 100644 plugin-examples/mem_count_racy_both.c
 create mode 100644 plugin-examples/Makefile

diff --git a/plugin-examples/bbcount_avgsize_racy.c b/plugin-examples/bbcount_avgsize_racy.c
new file mode 100644
index 0000000000..ccdf96c1fa
--- /dev/null
+++ b/plugin-examples/bbcount_avgsize_racy.c
@@ -0,0 +1,50 @@
+#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;
+const char *filename;
+static int stdout_fd;
+
+static void plugin_exit(qemu_plugin_id_t id, void *p)
+{
+    dprintf(stdout_fd, "insns: %" PRIu64", bb: %" PRIu64 ", "
+            "avg insns/bb: %.2f\n",
+            insn_count, bb_count, (double)insn_count / bb_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);
+
+    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)
+{
+    /* plugin_exit might write to stdout after stdout has been closed */
+    stdout_fd = dup(STDOUT_FILENO);
+    assert(stdout_fd);
+
+    qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans);
+    qemu_plugin_register_atexit_cb(id, plugin_exit, NULL);
+    return 0;
+}
diff --git a/plugin-examples/mem_count_racy_both.c b/plugin-examples/mem_count_racy_both.c
new file mode 100644
index 0000000000..a47f2025bf
--- /dev/null
+++ b/plugin-examples/mem_count_racy_both.c
@@ -0,0 +1,58 @@
+#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 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_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,
+                                                 QEMU_PLUGIN_INLINE_ADD_U64,
+                                                 &mem_count, 1);
+        } else {
+            qemu_plugin_register_vcpu_mem_cb(insn, vcpu_mem,
+                                             QEMU_PLUGIN_CB_NO_REGS, NULL);
+        }
+    }
+}
+
+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;
+    }
+    /* plugin_exit might write to stdout after stdout has been closed */
+    stdout_fd = dup(STDOUT_FILENO);
+    assert(stdout_fd);
+
+    qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans);
+    qemu_plugin_register_atexit_cb(id, plugin_exit, NULL);
+    return 0;
+}
diff --git a/plugin-examples/Makefile b/plugin-examples/Makefile
new file mode 100644
index 0000000000..71bbcda7a8
--- /dev/null
+++ b/plugin-examples/Makefile
@@ -0,0 +1,31 @@
+NAMES :=
+NAMES += bbcount_avgsize_racy
+NAMES += mem_count_racy_both
+
+SONAMES := $(addsuffix .so,$(addprefix lib,$(NAMES)))
+
+# QEMU installed path, set by --prefix during configure
+QEMU_PATH ?= /data/src/qemu-inst/plugin-test
+
+CC := gcc
+CFLAGS :=
+CFLAGS += -O2 -Werror -Wall
+CFLAGS += -Wundef -Wwrite-strings -Wmissing-prototypes
+CFLAGS += -Wstrict-prototypes -Wredundant-decls
+CFLAGS += -fno-strict-aliasing -fno-common -fwrapv
+CFLAGS += -I$(QEMU_PATH)/include
+LDLIBS := -lc
+
+all: $(SONAMES)
+
+%.o: %.c
+	$(CC) $(CFLAGS) -fPIC -c $< -o $@
+
+lib%.so: %.o
+	$(CC) -shared -Wl,-soname,$@ -o $@ $^ $(LDLIBS)
+
+clean:
+	$(RM) -f *.o *.so
+	$(RM) -Rf .libs
+
+.PHONY: all clean
-- 
2.17.1

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

* Re: [Qemu-devel] [RFC 00/48] Plugin support
  2018-10-25 17:20 [Qemu-devel] [RFC 00/48] Plugin support Emilio G. Cota
                   ` (47 preceding siblings ...)
  2018-10-25 17:20 ` [Qemu-devel] [RFC 48/48] plugin: add a couple of very simple examples Emilio G. Cota
@ 2018-10-29  9:48 ` Pavel Dovgalyuk
  2018-10-29 16:45   ` Emilio G. Cota
  48 siblings, 1 reply; 132+ messages in thread
From: Pavel Dovgalyuk @ 2018-10-29  9:48 UTC (permalink / raw)
  To: 'Emilio G. Cota', qemu-devel
  Cc: 'Pavel Dovgalyuk', 'Lluís Vilanova',
	'Peter Maydell', 'Alex Bennée',
	'Stefan Hajnoczi'

> From: Emilio G. Cota [mailto:cota@braap.org]
> - 2-pass translation. Once a "TB translation" callback is called,
>   the plugin must know the span of the TB. We should not
>   force plugins to guess where the TB will end; that is strictly
>   QEMU's job, and can change any time. A TB is thus a sequence
>   of instructions of whatever length the particular QEMU
>   implementation decides. Thus, for each TB, a 3-step process
>   is followed: (1) the plugin layer keeps a copy of the contents
>   of the current TB, (2) once the TB is well-defined, its
>   descriptor and contents are passed to plugins, which then
>   register their desired instrumentation (e.g. "call me back
>   on this particular instruction", or "call me back when
>   the whole TB executes"); note that plugins can use a disassembler
>   like capstone to decide what to do with each instruction; they
>   can also allocate memory and then get a pointer to it passed
>   back from the callbacks. And finally, (3) the target translator
>   is called again to generate the final instrumented translated TB.
>   This is what I called the "2-pass translation", since we go
>   twice over the translation loop in translator.c. Note that the
>   2-pass approach has virtually no overhead (0.40% for SPEC06int);
>   translation is much cheaper than execution. But anyway, if no
>   plugins have subscribed to TB translation, we only do one pass.

Can plugin affect the translation somehow to force flushing cached registers?
E.g. callback may need correct EFLAGS which usually does not updated
until the end of the block.

> - Support for inlining instrumentation. This is done via an
>   explicit API, i.e. we do not export TCG ops, which are internal
>   to QEMU. For now, I just have support for incrementing a u64
>   with an immediate, e.g. to increment a counter.

It means that we'll have "yet another one TCG"?

Pavel Dovgalyuk

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

* Re: [Qemu-devel] [RFC 48/48] plugin: add a couple of very simple examples
  2018-10-25 17:20 ` [Qemu-devel] [RFC 48/48] plugin: add a couple of very simple examples Emilio G. Cota
@ 2018-10-29 10:59   ` Pavel Dovgalyuk
  2018-10-29 16:38     ` Emilio G. Cota
  2018-11-28 11:28   ` Alex Bennée
  2018-11-29 20:45   ` Roman Bolshakov
  2 siblings, 1 reply; 132+ messages in thread
From: Pavel Dovgalyuk @ 2018-10-29 10:59 UTC (permalink / raw)
  To: 'Emilio G. Cota', qemu-devel
  Cc: 'Pavel Dovgalyuk', 'Lluís Vilanova',
	'Peter Maydell', 'Alex Bennée',
	'Stefan Hajnoczi'

> From: Emilio G. Cota [mailto:cota@braap.org]
> Signed-off-by: Emilio G. Cota <cota@braap.org>
> ---
>  plugin-examples/bbcount_avgsize_racy.c | 50 ++++++++++++++++++++++
>  plugin-examples/mem_count_racy_both.c  | 58 ++++++++++++++++++++++++++
>  plugin-examples/Makefile               | 31 ++++++++++++++
>  3 files changed, 139 insertions(+)
>  create mode 100644 plugin-examples/bbcount_avgsize_racy.c
>  create mode 100644 plugin-examples/mem_count_racy_both.c
>  create mode 100644 plugin-examples/Makefile
> 

<snip>

> diff --git a/plugin-examples/mem_count_racy_both.c b/plugin-examples/mem_count_racy_both.c
> new file mode 100644
> index 0000000000..a47f2025bf
> --- /dev/null
> +++ b/plugin-examples/mem_count_racy_both.c
> @@ -0,0 +1,58 @@
> +#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 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_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,
> +                                                 QEMU_PLUGIN_INLINE_ADD_U64,
> +                                                 &mem_count, 1);
> +        } else {
> +            qemu_plugin_register_vcpu_mem_cb(insn, vcpu_mem,
> +                                             QEMU_PLUGIN_CB_NO_REGS, NULL);
> +        }
> +    }
> +}
> +
> +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;
> +    }
> +    /* 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;
> +}

Thanks for the series.
Can you provide more plugin examples for better understanding of double-translate idea?
E.g., plugins that hook specific instructions or addresses.

Pavel Dovgalyuk

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

* Re: [Qemu-devel] [RFC 48/48] plugin: add a couple of very simple examples
  2018-10-29 10:59   ` Pavel Dovgalyuk
@ 2018-10-29 16:38     ` Emilio G. Cota
  0 siblings, 0 replies; 132+ messages in thread
From: Emilio G. Cota @ 2018-10-29 16:38 UTC (permalink / raw)
  To: Pavel Dovgalyuk
  Cc: qemu-devel, 'Pavel Dovgalyuk',
	'Lluís Vilanova', 'Peter Maydell',
	'Alex Bennée', 'Stefan Hajnoczi'

On Mon, Oct 29, 2018 at 13:59:03 +0300, Pavel Dovgalyuk wrote:
> > From: Emilio G. Cota [mailto:cota@braap.org]
> > Signed-off-by: Emilio G. Cota <cota@braap.org>
(snip)
> Thanks for the series.
> Can you provide more plugin examples for better understanding of double-translate idea?
> E.g., plugins that hook specific instructions or addresses.

Here's a plugin that at TB translation time, disassembles the
instructions provided via capstone. It doesn't do anything
with that info (vcpu_tb_exec is empty), but say a simulator
would pass via the *udata pointer some descriptor (allocated
at translation time) based on the instructions in the TB.

Note that the disassembly happens because we already have
a fully formed TB thanks to the 2-pass translation. Without it,
we'd have to (1) perform additional loads in the guest to read
instructions given the PC, and (2) guess when the TB would end
(recall that when to finish a TB is a decision internal to QEMU).

Thanks,

		Emilio

PS. Compile with -lcapstone
---
#include <inttypes.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>

#include <capstone/capstone.h>
#include <qemu-plugin.h>

struct tb {
	size_t n_insns;
};

static csh cap_handle;
static cs_insn *cap_insn;

static void vcpu_mem(unsigned int cpu_index, qemu_plugin_meminfo_t meminfo,
		     uint64_t vaddr, void *udata)
{ }

static void vcpu_tb_exec(unsigned int cpu_index, void *udata)
{ }

static void vcpu_tb_trans(qemu_plugin_id_t id, unsigned int cpu_index, struct qemu_plugin_tb *tb)
{
	struct tb *desc;
	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);
		size_t size = qemu_plugin_insn_size(insn);
		const uint8_t *code = qemu_plugin_insn_data(insn);
		uint64_t offset = 0;
		bool success;

		success = cs_disasm_iter(cap_handle, &code, &size, &offset, cap_insn);
		assert(success);
		qemu_plugin_register_vcpu_mem_cb(insn, vcpu_mem,
						 QEMU_PLUGIN_CB_NO_REGS, NULL);
	}
	desc = malloc(sizeof(*desc));
	assert(desc);
	desc->n_insns = n;

	qemu_plugin_register_vcpu_tb_exec_cb(tb, vcpu_tb_exec,
					     QEMU_PLUGIN_CB_NO_REGS, desc);
}

QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, int argc, char **argv)
{
	if (cs_open(CS_ARCH_X86, CS_MODE_64, &cap_handle) != CS_ERR_OK) {
		return -1;
	}
	cap_insn = cs_malloc(cap_handle);
	if (cap_insn == NULL) {
		return -1;
	}
	qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans);
	return 0;
}

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

* Re: [Qemu-devel] [RFC 00/48] Plugin support
  2018-10-29  9:48 ` [Qemu-devel] [RFC 00/48] Plugin support Pavel Dovgalyuk
@ 2018-10-29 16:45   ` Emilio G. Cota
  0 siblings, 0 replies; 132+ messages in thread
From: Emilio G. Cota @ 2018-10-29 16:45 UTC (permalink / raw)
  To: Pavel Dovgalyuk
  Cc: qemu-devel, 'Pavel Dovgalyuk',
	'Lluís Vilanova', 'Peter Maydell',
	'Alex Bennée', 'Stefan Hajnoczi'

On Mon, Oct 29, 2018 at 12:48:05 +0300, Pavel Dovgalyuk wrote:
> > From: Emilio G. Cota [mailto:cota@braap.org]
> > - 2-pass translation. Once a "TB translation" callback is called,
> >   the plugin must know the span of the TB. We should not
> >   force plugins to guess where the TB will end; that is strictly
> >   QEMU's job, and can change any time. A TB is thus a sequence
> >   of instructions of whatever length the particular QEMU
> >   implementation decides. Thus, for each TB, a 3-step process
> >   is followed: (1) the plugin layer keeps a copy of the contents
> >   of the current TB, (2) once the TB is well-defined, its
> >   descriptor and contents are passed to plugins, which then
> >   register their desired instrumentation (e.g. "call me back
> >   on this particular instruction", or "call me back when
> >   the whole TB executes"); note that plugins can use a disassembler
> >   like capstone to decide what to do with each instruction; they
> >   can also allocate memory and then get a pointer to it passed
> >   back from the callbacks. And finally, (3) the target translator
> >   is called again to generate the final instrumented translated TB.
> >   This is what I called the "2-pass translation", since we go
> >   twice over the translation loop in translator.c. Note that the
> >   2-pass approach has virtually no overhead (0.40% for SPEC06int);
> >   translation is much cheaper than execution. But anyway, if no
> >   plugins have subscribed to TB translation, we only do one pass.
> 
> Can plugin affect the translation somehow to force flushing cached registers?
> E.g. callback may need correct EFLAGS which usually does not updated
> until the end of the block.

I'd provide an API call to get those up to date, since the common
case is that callbacks won't require those to be up to date.

> > - Support for inlining instrumentation. This is done via an
> >   explicit API, i.e. we do not export TCG ops, which are internal
> >   to QEMU. For now, I just have support for incrementing a u64
> >   with an immediate, e.g. to increment a counter.
> 
> It means that we'll have "yet another one TCG"?

That's certainly not my intention.

I'd only export common ops that are trivial to implement,
whether with TCG or whatever we use in 100 years time. I think
incrementing a counter is a good use of this; letting plugins
go wild by exporting a "TCG-like API" is not at all the
point. Users that demand that can be just told to fork QEMU.

Thanks,

		Emilio

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

* Re: [Qemu-devel] [RFC 01/48] cpu: introduce run_on_cpu_no_bql
  2018-10-25 17:20 ` [Qemu-devel] [RFC 01/48] cpu: introduce run_on_cpu_no_bql Emilio G. Cota
@ 2018-11-14 11:30   ` Alex Bennée
  2018-11-14 17:08     ` Emilio G. Cota
  0 siblings, 1 reply; 132+ messages in thread
From: Alex Bennée @ 2018-11-14 11:30 UTC (permalink / raw)
  To: Emilio G. Cota
  Cc: qemu-devel, Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Stefan Hajnoczi


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

> This allows us to queue synchronous CPU work without the BQL.
>
> Will gain a user soon.

This is also in the cpu-lock series right?

>
> Signed-off-by: Emilio G. Cota <cota@braap.org>
> ---
>  include/qom/cpu.h | 13 +++++++++++++
>  cpus-common.c     | 28 ++++++++++++++++++++++------
>  2 files changed, 35 insertions(+), 6 deletions(-)
>
> diff --git a/include/qom/cpu.h b/include/qom/cpu.h
> index 204bc94056..863aa2bff1 100644
> --- a/include/qom/cpu.h
> +++ b/include/qom/cpu.h
> @@ -877,6 +877,19 @@ bool cpu_is_stopped(CPUState *cpu);
>   */
>  void run_on_cpu(CPUState *cpu, run_on_cpu_func func, run_on_cpu_data data);
>
> +/**
> + * run_on_cpu_no_bql
> + * @cpu: The vCPU to run on.
> + * @func: The function to be executed.
> + * @data: Data to pass to the function.
> + *
> + * Schedules the function @func for execution on the vCPU @cpu.
> + * This function is run outside the BQL.
> + * See also: run_on_cpu()
> + */
> +void run_on_cpu_no_bql(CPUState *cpu, run_on_cpu_func func,
> +                       run_on_cpu_data data);
> +
>  /**
>   * async_run_on_cpu:
>   * @cpu: The vCPU to run on.
> diff --git a/cpus-common.c b/cpus-common.c
> index cffb2b71ac..b478fc8741 100644
> --- a/cpus-common.c
> +++ b/cpus-common.c
> @@ -144,7 +144,8 @@ static void queue_work_on_cpu(CPUState *cpu, struct qemu_work_item *wi)
>      cpu_mutex_unlock(cpu);
>  }
>
> -void run_on_cpu(CPUState *cpu, run_on_cpu_func func, run_on_cpu_data data)
> +static void do_run_on_cpu(CPUState *cpu, run_on_cpu_func func,
> +                          run_on_cpu_data data, bool bql)
>  {
>      struct qemu_work_item wi;
>      bool has_bql = qemu_mutex_iothread_locked();
> @@ -152,12 +153,16 @@ void run_on_cpu(CPUState *cpu, run_on_cpu_func func, run_on_cpu_data data)
>      g_assert(no_cpu_mutex_locked());
>
>      if (qemu_cpu_is_self(cpu)) {
> -        if (has_bql) {
> -            func(cpu, data);
> +        if (bql) {
> +            if (has_bql) {
> +                func(cpu, data);
> +            } else {
> +                qemu_mutex_lock_iothread();
> +                func(cpu, data);
> +                qemu_mutex_unlock_iothread();
> +            }
>          } else {
> -            qemu_mutex_lock_iothread();
>              func(cpu, data);
> -            qemu_mutex_unlock_iothread();
>          }
>          return;
>      }
> @@ -172,7 +177,7 @@ void run_on_cpu(CPUState *cpu, run_on_cpu_func func, run_on_cpu_data data)
>      wi.done = false;
>      wi.free = false;
>      wi.exclusive = false;
> -    wi.bql = true;
> +    wi.bql = bql;
>
>      cpu_mutex_lock(cpu);
>      queue_work_on_cpu_locked(cpu, &wi);
> @@ -189,6 +194,17 @@ void run_on_cpu(CPUState *cpu, run_on_cpu_func func, run_on_cpu_data data)
>      }
>  }
>
> +void run_on_cpu(CPUState *cpu, run_on_cpu_func func, run_on_cpu_data data)
> +{
> +    do_run_on_cpu(cpu, func, data, true);
> +}
> +
> +void
> +run_on_cpu_no_bql(CPUState *cpu, run_on_cpu_func func, run_on_cpu_data data)
> +{
> +    do_run_on_cpu(cpu, func, data, false);
> +}
> +
>  void async_run_on_cpu(CPUState *cpu, run_on_cpu_func func, run_on_cpu_data data)
>  {
>      struct qemu_work_item *wi;


--
Alex Bennée

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

* Re: [Qemu-devel] [RFC 02/48] trace: expand mem_info:size_shift to 3 bits
  2018-10-25 17:20 ` [Qemu-devel] [RFC 02/48] trace: expand mem_info:size_shift to 3 bits Emilio G. Cota
@ 2018-11-14 13:03   ` Alex Bennée
  2018-11-14 17:17     ` Emilio G. Cota
  0 siblings, 1 reply; 132+ messages in thread
From: Alex Bennée @ 2018-11-14 13:03 UTC (permalink / raw)
  To: Emilio G. Cota
  Cc: qemu-devel, Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Stefan Hajnoczi


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

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

Currently we ignore atomic operation but I assume we'll want to
represent them at some point here. I don't know if memory barrier
behaviour would also be worth exporting?

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


--
Alex Bennée

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

* Re: [Qemu-devel] [RFC 03/48] tcg/README: fix typo s/afterwise/afterwards/
  2018-10-25 17:20 ` [Qemu-devel] [RFC 03/48] tcg/README: fix typo s/afterwise/afterwards/ Emilio G. Cota
@ 2018-11-14 13:03   ` Alex Bennée
  0 siblings, 0 replies; 132+ messages in thread
From: Alex Bennée @ 2018-11-14 13:03 UTC (permalink / raw)
  To: Emilio G. Cota
  Cc: qemu-devel, Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Stefan Hajnoczi


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

> Afterwise is "wise after the fact", as in "hindsight".
> Here we meant "afterwards" (as in "subsequently"). Fix it.
>
> Signed-off-by: Emilio G. Cota <cota@braap.org>

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

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


--
Alex Bennée

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

* Re: [Qemu-devel] [RFC 04/48] exec: introduce qemu_xxhash{2, 4, 5, 6, 7}
  2018-10-25 17:20 ` [Qemu-devel] [RFC 04/48] exec: introduce qemu_xxhash{2,4,5,6,7} Emilio G. Cota
@ 2018-11-14 13:04   ` Alex Bennée
  0 siblings, 0 replies; 132+ messages in thread
From: Alex Bennée @ 2018-11-14 13:04 UTC (permalink / raw)
  To: Emilio G. Cota
  Cc: qemu-devel, Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Stefan Hajnoczi


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

> Before moving them all to include/qemu/xxhash.h.
>
> Signed-off-by: Emilio G. Cota <cota@braap.org>

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

> ---
>  include/exec/tb-hash-xx.h | 41 +++++++++++++++++++++++++++++----------
>  include/exec/tb-hash.h    |  2 +-
>  tests/qht-bench.c         |  2 +-
>  util/qsp.c                | 12 ++++++------
>  4 files changed, 39 insertions(+), 18 deletions(-)
>
> diff --git a/include/exec/tb-hash-xx.h b/include/exec/tb-hash-xx.h
> index 747a9a612c..98ce4b628a 100644
> --- a/include/exec/tb-hash-xx.h
> +++ b/include/exec/tb-hash-xx.h
> @@ -42,23 +42,23 @@
>  #define PRIME32_4    668265263U
>  #define PRIME32_5    374761393U
>
> -#define TB_HASH_XX_SEED 1
> +#define QEMU_XXHASH_SEED 1
>
>  /*
>   * xxhash32, customized for input variables that are not guaranteed to be
>   * contiguous in memory.
>   */
>  static inline uint32_t
> -tb_hash_func7(uint64_t a0, uint64_t b0, uint32_t e, uint32_t f, uint32_t g)
> +qemu_xxhash7(uint64_t ab, uint64_t cd, uint32_t e, uint32_t f, uint32_t g)
>  {
> -    uint32_t v1 = TB_HASH_XX_SEED + PRIME32_1 + PRIME32_2;
> -    uint32_t v2 = TB_HASH_XX_SEED + PRIME32_2;
> -    uint32_t v3 = TB_HASH_XX_SEED + 0;
> -    uint32_t v4 = TB_HASH_XX_SEED - PRIME32_1;
> -    uint32_t a = a0 >> 32;
> -    uint32_t b = a0;
> -    uint32_t c = b0 >> 32;
> -    uint32_t d = b0;
> +    uint32_t v1 = QEMU_XXHASH_SEED + PRIME32_1 + PRIME32_2;
> +    uint32_t v2 = QEMU_XXHASH_SEED + PRIME32_2;
> +    uint32_t v3 = QEMU_XXHASH_SEED + 0;
> +    uint32_t v4 = QEMU_XXHASH_SEED - PRIME32_1;
> +    uint32_t a = ab >> 32;
> +    uint32_t b = ab;
> +    uint32_t c = cd >> 32;
> +    uint32_t d = cd;
>      uint32_t h32;
>
>      v1 += a * PRIME32_2;
> @@ -98,4 +98,25 @@ tb_hash_func7(uint64_t a0, uint64_t b0, uint32_t e, uint32_t f, uint32_t g)
>      return h32;
>  }
>
> +static inline uint32_t qemu_xxhash2(uint64_t ab)
> +{
> +    return qemu_xxhash7(ab, 0, 0, 0, 0);
> +}
> +
> +static inline uint32_t qemu_xxhash4(uint64_t ab, uint64_t cd)
> +{
> +    return qemu_xxhash7(ab, cd, 0, 0, 0);
> +}
> +
> +static inline uint32_t qemu_xxhash5(uint64_t ab, uint64_t cd, uint32_t e)
> +{
> +    return qemu_xxhash7(ab, cd, e, 0, 0);
> +}
> +
> +static inline uint32_t qemu_xxhash6(uint64_t ab, uint64_t cd, uint32_t e,
> +                                    uint32_t f)
> +{
> +    return qemu_xxhash7(ab, cd, e, f, 0);
> +}
> +
>  #endif /* EXEC_TB_HASH_XX_H */
> diff --git a/include/exec/tb-hash.h b/include/exec/tb-hash.h
> index 0526c4f678..731ba4c272 100644
> --- a/include/exec/tb-hash.h
> +++ b/include/exec/tb-hash.h
> @@ -61,7 +61,7 @@ static inline
>  uint32_t tb_hash_func(tb_page_addr_t phys_pc, target_ulong pc, uint32_t flags,
>                        uint32_t cf_mask, uint32_t trace_vcpu_dstate)
>  {
> -    return tb_hash_func7(phys_pc, pc, flags, cf_mask, trace_vcpu_dstate);
> +    return qemu_xxhash7(phys_pc, pc, flags, cf_mask, trace_vcpu_dstate);
>  }
>
>  #endif
> diff --git a/tests/qht-bench.c b/tests/qht-bench.c
> index 2089e2bed1..2ea132fe57 100644
> --- a/tests/qht-bench.c
> +++ b/tests/qht-bench.c
> @@ -104,7 +104,7 @@ static bool is_equal(const void *ap, const void *bp)
>
>  static uint32_t h(unsigned long v)
>  {
> -    return tb_hash_func7(v, 0, 0, 0, 0);
> +    return qemu_xxhash2(v);
>  }
>
>  static uint32_t hval(unsigned long v)
> diff --git a/util/qsp.c b/util/qsp.c
> index a848b09c6d..dc29c41fde 100644
> --- a/util/qsp.c
> +++ b/util/qsp.c
> @@ -135,13 +135,13 @@ QemuCondWaitFunc qemu_cond_wait_func = qemu_cond_wait_impl;
>   * without it we still get a pretty unique hash.
>   */
>  static inline
> -uint32_t do_qsp_callsite_hash(const QSPCallSite *callsite, uint64_t a)
> +uint32_t do_qsp_callsite_hash(const QSPCallSite *callsite, uint64_t ab)
>  {
> -    uint64_t b = (uint64_t)(uintptr_t)callsite->obj;
> +    uint64_t cd = (uint64_t)(uintptr_t)callsite->obj;
>      uint32_t e = callsite->line;
>      uint32_t f = callsite->type;
>
> -    return tb_hash_func7(a, b, e, f, 0);
> +    return qemu_xxhash6(ab, cd, e, f);
>  }
>
>  static inline
> @@ -169,11 +169,11 @@ static uint32_t qsp_entry_no_thread_hash(const QSPEntry *entry)
>  static uint32_t qsp_entry_no_thread_obj_hash(const QSPEntry *entry)
>  {
>      const QSPCallSite *callsite = entry->callsite;
> -    uint64_t a = g_str_hash(callsite->file);
> -    uint64_t b = callsite->line;
> +    uint64_t ab = g_str_hash(callsite->file);
> +    uint64_t cd = callsite->line;
>      uint32_t e = callsite->type;
>
> -    return tb_hash_func7(a, b, e, 0, 0);
> +    return qemu_xxhash5(ab, cd, e);
>  }
>
>  static bool qsp_callsite_cmp(const void *ap, const void *bp)


--
Alex Bennée

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

* Re: [Qemu-devel] [RFC 05/48] include: move exec/tb-hash-xx.h to qemu/xxhash.h
  2018-10-25 17:20 ` [Qemu-devel] [RFC 05/48] include: move exec/tb-hash-xx.h to qemu/xxhash.h Emilio G. Cota
@ 2018-11-14 13:05   ` Alex Bennée
  0 siblings, 0 replies; 132+ messages in thread
From: Alex Bennée @ 2018-11-14 13:05 UTC (permalink / raw)
  To: Emilio G. Cota
  Cc: qemu-devel, Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Stefan Hajnoczi


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

> Signed-off-by: Emilio G. Cota <cota@braap.org>

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

> ---
>  include/exec/tb-hash.h                       | 2 +-
>  include/{exec/tb-hash-xx.h => qemu/xxhash.h} | 6 +++---
>  tests/qht-bench.c                            | 2 +-
>  util/qsp.c                                   | 2 +-
>  4 files changed, 6 insertions(+), 6 deletions(-)
>  rename include/{exec/tb-hash-xx.h => qemu/xxhash.h} (97%)
>
> diff --git a/include/exec/tb-hash.h b/include/exec/tb-hash.h
> index 731ba4c272..4f3a37d927 100644
> --- a/include/exec/tb-hash.h
> +++ b/include/exec/tb-hash.h
> @@ -20,7 +20,7 @@
>  #ifndef EXEC_TB_HASH_H
>  #define EXEC_TB_HASH_H
>
> -#include "exec/tb-hash-xx.h"
> +#include "qemu/xxhash.h"
>
>  #ifdef CONFIG_SOFTMMU
>
> diff --git a/include/exec/tb-hash-xx.h b/include/qemu/xxhash.h
> similarity index 97%
> rename from include/exec/tb-hash-xx.h
> rename to include/qemu/xxhash.h
> index 98ce4b628a..fe35dde328 100644
> --- a/include/exec/tb-hash-xx.h
> +++ b/include/qemu/xxhash.h
> @@ -31,8 +31,8 @@
>   * - xxHash source repository : https://github.com/Cyan4973/xxHash
>   */
>
> -#ifndef EXEC_TB_HASH_XX_H
> -#define EXEC_TB_HASH_XX_H
> +#ifndef QEMU_XXHASH_H
> +#define QEMU_XXHASH_H
>
>  #include "qemu/bitops.h"
>
> @@ -119,4 +119,4 @@ static inline uint32_t qemu_xxhash6(uint64_t ab, uint64_t cd, uint32_t e,
>      return qemu_xxhash7(ab, cd, e, f, 0);
>  }
>
> -#endif /* EXEC_TB_HASH_XX_H */
> +#endif /* QEMU_XXHASH_H */
> diff --git a/tests/qht-bench.c b/tests/qht-bench.c
> index 2ea132fe57..4d885ca303 100644
> --- a/tests/qht-bench.c
> +++ b/tests/qht-bench.c
> @@ -9,7 +9,7 @@
>  #include "qemu/atomic.h"
>  #include "qemu/qht.h"
>  #include "qemu/rcu.h"
> -#include "exec/tb-hash-xx.h"
> +#include "qemu/xxhash.h"
>
>  struct thread_stats {
>      size_t rd;
> diff --git a/util/qsp.c b/util/qsp.c
> index dc29c41fde..410f1ba004 100644
> --- a/util/qsp.c
> +++ b/util/qsp.c
> @@ -61,7 +61,7 @@
>  #include "qemu/timer.h"
>  #include "qemu/qht.h"
>  #include "qemu/rcu.h"
> -#include "exec/tb-hash-xx.h"
> +#include "qemu/xxhash.h"
>
>  enum QSPType {
>      QSP_MUTEX,


--
Alex Bennée

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

* Re: [Qemu-devel] [RFC 06/48] tcg: use QHT for helper_table
  2018-10-25 17:20 ` [Qemu-devel] [RFC 06/48] tcg: use QHT for helper_table Emilio G. Cota
@ 2018-11-14 14:41   ` Alex Bennée
  2018-11-14 17:50     ` Emilio G. Cota
  2018-11-14 16:11   ` Alex Bennée
  1 sibling, 1 reply; 132+ messages in thread
From: Alex Bennée @ 2018-11-14 14:41 UTC (permalink / raw)
  To: Emilio G. Cota
  Cc: qemu-devel, Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Stefan Hajnoczi


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

> This will allow us to add TCG helpers at run-time.
>
> While at it, rename tcg_find_helper to tcg_helper_find for consistency
> with the added tcg_helper_foo functions.
>
> Signed-off-by: Emilio G. Cota <cota@braap.org>
> ---
>  tcg/tcg.c | 59 +++++++++++++++++++++++++++++++++++++++++++++----------
>  1 file changed, 49 insertions(+), 10 deletions(-)
>
> diff --git a/tcg/tcg.c b/tcg/tcg.c
> index e85133ef05..65da3c5dbf 100644
> --- a/tcg/tcg.c
> +++ b/tcg/tcg.c
> @@ -33,6 +33,7 @@
>  #include "qemu/error-report.h"
>  #include "qemu/cutils.h"
>  #include "qemu/host-utils.h"
> +#include "qemu/xxhash.h"
>  #include "qemu/timer.h"
>
>  /* Note: the long term plan is to reduce the dependencies on the QEMU
> @@ -879,13 +880,46 @@ typedef struct TCGHelperInfo {
>  static const TCGHelperInfo all_helpers[] = {
>  #include "exec/helper-tcg.h"
>  };
> -static GHashTable *helper_table;
> +static struct qht helper_table;
> +static bool helper_table_inited;

Having a flag for initialisation seems a little excessive considering
we've moved that initialisation into tcg_context_init() which has to be
called before we do anything TCG related.

>  static int indirect_reg_alloc_order[ARRAY_SIZE(tcg_target_reg_alloc_order)];
>  static void process_op_defs(TCGContext *s);
>  static TCGTemp *tcg_global_reg_new_internal(TCGContext *s, TCGType type,
>                                              TCGReg reg, const char *name);
>
> +static inline uint32_t tcg_helper_func_hash(const void *func)
> +{
> +    return qemu_xxhash2((uint64_t)func);
> +}
> +
> +static bool tcg_helper_cmp(const void *ap, const void *bp)
> +{
> +    const TCGHelperInfo *a = ap;
> +    const TCGHelperInfo *b = bp;
> +
> +    return a->func == b->func &&
> +        a->flags == b->flags &&
> +        a->sizemask == b->sizemask &&
> +        !strcmp(a->name, b->name);
> +}
> +
> +static bool tcg_helper_lookup_cmp(const void *obj, const void *func)
> +{
> +    const TCGHelperInfo *info = obj;
> +
> +    return info->func == func;
> +}
> +
> +static void tcg_helper_insert(const TCGHelperInfo *info)
> +{
> +    uint32_t hash = tcg_helper_func_hash(info->func);
> +    bool inserted;
> +
> +    inserted = qht_insert(&helper_table, (void *)info, hash, NULL);
> +    g_assert(inserted);
> +}
> +
>  void tcg_context_init(TCGContext *s)
>  {
>      int op, total_args, n, i;
> @@ -919,13 +953,13 @@ void tcg_context_init(TCGContext *s)
>      }
>
>      /* Register helpers.  */
> -    /* Use g_direct_hash/equal for direct pointer comparisons on func.  */
> -    helper_table = g_hash_table_new(NULL, NULL);
> +    qht_init(&helper_table, tcg_helper_cmp, ARRAY_SIZE(all_helpers),
> +             QHT_MODE_AUTO_RESIZE);
>
>      for (i = 0; i < ARRAY_SIZE(all_helpers); ++i) {
> -        g_hash_table_insert(helper_table, (gpointer)all_helpers[i].func,
> -                            (gpointer)&all_helpers[i]);
> +        tcg_helper_insert(&all_helpers[i]);
>      }
> +    helper_table_inited = true;

so I think we can drop this and...

>
>      tcg_target_init(s);
>      process_op_defs(s);
> @@ -1620,9 +1654,10 @@ void tcg_gen_callN(void *func, TCGTemp *ret, int nargs, TCGTemp **args)
>      int i, real_args, nb_rets, pi;
>      unsigned sizemask, flags;
>      TCGHelperInfo *info;
> +    uint32_t hash = tcg_helper_func_hash(func);
>      TCGOp *op;
>
> -    info = g_hash_table_lookup(helper_table, (gpointer)func);
> +    info = qht_lookup_custom(&helper_table, func, hash, tcg_helper_lookup_cmp);
>      flags = info->flags;
>      sizemask = info->sizemask;
>
> @@ -1825,11 +1860,15 @@ static char *tcg_get_arg_str(TCGContext *s, char *buf,
>  }
>
>  /* Find helper name.  */
> -static inline const char *tcg_find_helper(TCGContext *s, uintptr_t val)
> +static inline const char *tcg_helper_find(TCGContext *s, uintptr_t val)
>  {
>      const char *ret = NULL;
> -    if (helper_table) {
> -        TCGHelperInfo *info = g_hash_table_lookup(helper_table, (gpointer)val);
> +    if (helper_table_inited) {

change this to a assert(helper_table.cmp) if you really want to.

> +        uint32_t hash = tcg_helper_func_hash((void *)val);
> +        TCGHelperInfo *info;
> +
> +        info = qht_lookup_custom(&helper_table, (void *)val, hash,
> +                                 tcg_helper_lookup_cmp);
>          if (info) {
>              ret = info->name;
>          }
> @@ -1919,7 +1958,7 @@ void tcg_dump_ops(TCGContext *s)
>
>              /* function name, flags, out args */
>              col += qemu_log(" %s %s,$0x%" TCG_PRIlx ",$%d", def->name,
> -                            tcg_find_helper(s, op->args[nb_oargs + nb_iargs]),
> +                            tcg_helper_find(s, op->args[nb_oargs + nb_iargs]),
>                              op->args[nb_oargs + nb_iargs + 1], nb_oargs);
>              for (i = 0; i < nb_oargs; i++) {
>                  col += qemu_log(",%s", tcg_get_arg_str(s, buf, sizeof(buf),

Otherwise:

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

--
Alex Bennée

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

* Re: [Qemu-devel] [RFC 06/48] tcg: use QHT for helper_table
  2018-10-25 17:20 ` [Qemu-devel] [RFC 06/48] tcg: use QHT for helper_table Emilio G. Cota
  2018-11-14 14:41   ` Alex Bennée
@ 2018-11-14 16:11   ` Alex Bennée
  2018-11-14 17:52     ` Emilio G. Cota
  1 sibling, 1 reply; 132+ messages in thread
From: Alex Bennée @ 2018-11-14 16:11 UTC (permalink / raw)
  To: Emilio G. Cota
  Cc: qemu-devel, Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Stefan Hajnoczi


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

> This will allow us to add TCG helpers at run-time.
>
> While at it, rename tcg_find_helper to tcg_helper_find for consistency
> with the added tcg_helper_foo functions.
>
> Signed-off-by: Emilio G. Cota <cota@braap.org>
> ---
>  tcg/tcg.c | 59 +++++++++++++++++++++++++++++++++++++++++++++----------
>  1 file changed, 49 insertions(+), 10 deletions(-)
>
> diff --git a/tcg/tcg.c b/tcg/tcg.c
> index e85133ef05..65da3c5dbf 100644
> --- a/tcg/tcg.c
> +++ b/tcg/tcg.c
> @@ -33,6 +33,7 @@
>  #include "qemu/error-report.h"
>  #include "qemu/cutils.h"
>  #include "qemu/host-utils.h"
> +#include "qemu/xxhash.h"
>  #include "qemu/timer.h"
>
>  /* Note: the long term plan is to reduce the dependencies on the QEMU
> @@ -879,13 +880,46 @@ typedef struct TCGHelperInfo {
>  static const TCGHelperInfo all_helpers[] = {
>  #include "exec/helper-tcg.h"
>  };
> -static GHashTable *helper_table;
> +static struct qht helper_table;
> +static bool helper_table_inited;
>
>  static int indirect_reg_alloc_order[ARRAY_SIZE(tcg_target_reg_alloc_order)];
>  static void process_op_defs(TCGContext *s);
>  static TCGTemp *tcg_global_reg_new_internal(TCGContext *s, TCGType type,
>                                              TCGReg reg, const char *name);
>
> +static inline uint32_t tcg_helper_func_hash(const void *func)
> +{
> +    return qemu_xxhash2((uint64_t)func);
> +}

I needed to do this:

modified   tcg/tcg.c
@@ -884,7 +884,7 @@ static TCGTemp *tcg_global_reg_new_internal(TCGContext *s, TCGType type,

 static inline uint32_t tcg_helper_func_hash(const void *func)
 {
-    return qemu_xxhash2((uint64_t)func);
+    return qemu_xxhash2((uint64_t)(uintptr_t)func);
 }

To prevent the compiler complaining about:

 tcg.c:887:25: error: cast from pointer to integer of different size [-Werror=pointer-to-int-cast]

> +
> +static bool tcg_helper_cmp(const void *ap, const void *bp)
> +{
> +    const TCGHelperInfo *a = ap;
> +    const TCGHelperInfo *b = bp;
> +
> +    return a->func == b->func &&
> +        a->flags == b->flags &&
> +        a->sizemask == b->sizemask &&
> +        !strcmp(a->name, b->name);
> +}
> +
> +static bool tcg_helper_lookup_cmp(const void *obj, const void *func)
> +{
> +    const TCGHelperInfo *info = obj;
> +
> +    return info->func == func;
> +}
> +
> +static void tcg_helper_insert(const TCGHelperInfo *info)
> +{
> +    uint32_t hash = tcg_helper_func_hash(info->func);
> +    bool inserted;
> +
> +    inserted = qht_insert(&helper_table, (void *)info, hash, NULL);
> +    g_assert(inserted);
> +}
> +
>  void tcg_context_init(TCGContext *s)
>  {
>      int op, total_args, n, i;
> @@ -919,13 +953,13 @@ void tcg_context_init(TCGContext *s)
>      }
>
>      /* Register helpers.  */
> -    /* Use g_direct_hash/equal for direct pointer comparisons on func.  */
> -    helper_table = g_hash_table_new(NULL, NULL);
> +    qht_init(&helper_table, tcg_helper_cmp, ARRAY_SIZE(all_helpers),
> +             QHT_MODE_AUTO_RESIZE);
>
>      for (i = 0; i < ARRAY_SIZE(all_helpers); ++i) {
> -        g_hash_table_insert(helper_table, (gpointer)all_helpers[i].func,
> -                            (gpointer)&all_helpers[i]);
> +        tcg_helper_insert(&all_helpers[i]);
>      }
> +    helper_table_inited = true;
>
>      tcg_target_init(s);
>      process_op_defs(s);
> @@ -1620,9 +1654,10 @@ void tcg_gen_callN(void *func, TCGTemp *ret, int nargs, TCGTemp **args)
>      int i, real_args, nb_rets, pi;
>      unsigned sizemask, flags;
>      TCGHelperInfo *info;
> +    uint32_t hash = tcg_helper_func_hash(func);
>      TCGOp *op;
>
> -    info = g_hash_table_lookup(helper_table, (gpointer)func);
> +    info = qht_lookup_custom(&helper_table, func, hash, tcg_helper_lookup_cmp);
>      flags = info->flags;
>      sizemask = info->sizemask;
>
> @@ -1825,11 +1860,15 @@ static char *tcg_get_arg_str(TCGContext *s, char *buf,
>  }
>
>  /* Find helper name.  */
> -static inline const char *tcg_find_helper(TCGContext *s, uintptr_t val)
> +static inline const char *tcg_helper_find(TCGContext *s, uintptr_t val)
>  {
>      const char *ret = NULL;
> -    if (helper_table) {
> -        TCGHelperInfo *info = g_hash_table_lookup(helper_table, (gpointer)val);
> +    if (helper_table_inited) {
> +        uint32_t hash = tcg_helper_func_hash((void *)val);
> +        TCGHelperInfo *info;
> +
> +        info = qht_lookup_custom(&helper_table, (void *)val, hash,
> +                                 tcg_helper_lookup_cmp);
>          if (info) {
>              ret = info->name;
>          }
> @@ -1919,7 +1958,7 @@ void tcg_dump_ops(TCGContext *s)
>
>              /* function name, flags, out args */
>              col += qemu_log(" %s %s,$0x%" TCG_PRIlx ",$%d", def->name,
> -                            tcg_find_helper(s, op->args[nb_oargs + nb_iargs]),
> +                            tcg_helper_find(s, op->args[nb_oargs + nb_iargs]),
>                              op->args[nb_oargs + nb_iargs + 1], nb_oargs);
>              for (i = 0; i < nb_oargs; i++) {
>                  col += qemu_log(",%s", tcg_get_arg_str(s, buf, sizeof(buf),


--
Alex Bennée

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

* Re: [Qemu-devel] [RFC 07/48] tcg: export TCGHelperInfo
  2018-10-25 17:20 ` [Qemu-devel] [RFC 07/48] tcg: export TCGHelperInfo Emilio G. Cota
@ 2018-11-14 16:12   ` Alex Bennée
  0 siblings, 0 replies; 132+ messages in thread
From: Alex Bennée @ 2018-11-14 16:12 UTC (permalink / raw)
  To: Emilio G. Cota
  Cc: qemu-devel, Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Stefan Hajnoczi


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

> Signed-off-by: Emilio G. Cota <cota@braap.org>


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

> ---
>  tcg/tcg.h | 7 +++++++
>  tcg/tcg.c | 7 -------
>  2 files changed, 7 insertions(+), 7 deletions(-)
>
> diff --git a/tcg/tcg.h b/tcg/tcg.h
> index f4efbaa680..9f9643b470 100644
> --- a/tcg/tcg.h
> +++ b/tcg/tcg.h
> @@ -480,6 +480,13 @@ typedef TCGv_ptr TCGv_env;
>  /* Used to align parameters.  See the comment before tcgv_i32_temp.  */
>  #define TCG_CALL_DUMMY_ARG      ((TCGArg)0)
>
> +typedef struct TCGHelperInfo {
> +    void *func;
> +    const char *name;
> +    unsigned flags;
> +    unsigned sizemask;
> +} TCGHelperInfo;
> +
>  /* Conditions.  Note that these are laid out for easy manipulation by
>     the functions below:
>       bit 0 is used for inverting;
> diff --git a/tcg/tcg.c b/tcg/tcg.c
> index 65da3c5dbf..08b6926894 100644
> --- a/tcg/tcg.c
> +++ b/tcg/tcg.c
> @@ -868,13 +868,6 @@ void tcg_pool_reset(TCGContext *s)
>      s->pool_current = NULL;
>  }
>
> -typedef struct TCGHelperInfo {
> -    void *func;
> -    const char *name;
> -    unsigned flags;
> -    unsigned sizemask;
> -} TCGHelperInfo;
> -
>  #include "exec/helper-proto.h"
>
>  static const TCGHelperInfo all_helpers[] = {


--
Alex Bennée

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

* Re: [Qemu-devel] [RFC 44/48] cpus: lockstep execution support
  2018-10-25 17:20 ` [Qemu-devel] [RFC 44/48] cpus: lockstep execution support Emilio G. Cota
@ 2018-11-14 16:43   ` Alex Bennée
  2018-11-14 18:33     ` Emilio G. Cota
  0 siblings, 1 reply; 132+ messages in thread
From: Alex Bennée @ 2018-11-14 16:43 UTC (permalink / raw)
  To: Emilio G. Cota
  Cc: qemu-devel, Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Stefan Hajnoczi


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

> Signed-off-by: Emilio G. Cota <cota@braap.org>
> ---
<snip>
>
>  void cpu_interrupt(CPUState *cpu, int mask);
> diff --git a/cpus.c b/cpus.c
> index 3efe89354d..a446632a5c 100644
> --- a/cpus.c
> +++ b/cpus.c
<snip>
> +
> +static void cpu_lockstep_init(CPUState *cpu)
> +{
> +    if (!lockstep_enabled) {
> +        return;
> +    }
> +    qemu_mutex_lock(&lockstep_lock);
> +    /*
> +     * HACK: avoid racing with a wakeup, which would miss the addition
> +     * of this CPU; just wait until no wakeup is ongoing.
> +     */
> +    while (unlikely(lockstep_ongoing_wakeup)) {
> +        qemu_mutex_unlock(&lockstep_lock);
> +        sched_yield();

This breaks Windows builds. I suspect if we do want to this sort of
functionality we'll need to expose a utility function in
oslib-posix/oslib-win32


--
Alex Bennée

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

* Re: [Qemu-devel] [RFC 08/48] tcg: export tcg_gen_runtime_helper
  2018-10-25 17:20 ` [Qemu-devel] [RFC 08/48] tcg: export tcg_gen_runtime_helper Emilio G. Cota
@ 2018-11-14 16:44   ` Alex Bennée
  0 siblings, 0 replies; 132+ messages in thread
From: Alex Bennée @ 2018-11-14 16:44 UTC (permalink / raw)
  To: Emilio G. Cota
  Cc: qemu-devel, Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Stefan Hajnoczi


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

> This takes the TCGHelperInfo directly, which will allow us to generate
> helpers at run-time.
>
> Signed-off-by: Emilio G. Cota <cota@braap.org>

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

> ---
>  tcg/tcg.h |  2 ++
>  tcg/tcg.c | 50 +++++++++++++++++++++++++++++++++++++++++++++-----
>  2 files changed, 47 insertions(+), 5 deletions(-)
>
> diff --git a/tcg/tcg.h b/tcg/tcg.h
> index 9f9643b470..3fa434d891 100644
> --- a/tcg/tcg.h
> +++ b/tcg/tcg.h
> @@ -1077,6 +1077,8 @@ do {\
>  bool tcg_op_supported(TCGOpcode op);
>
>  void tcg_gen_callN(void *func, TCGTemp *ret, int nargs, TCGTemp **args);
> +void tcg_gen_runtime_helper(const TCGHelperInfo *orig, TCGTemp *ret, int nargs,
> +                            TCGTemp **args);
>
>  TCGOp *tcg_emit_op(TCGOpcode opc);
>  void tcg_op_remove(TCGContext *s, TCGOp *op);
> diff --git a/tcg/tcg.c b/tcg/tcg.c
> index 08b6926894..87e02da740 100644
> --- a/tcg/tcg.c
> +++ b/tcg/tcg.c
> @@ -1642,15 +1642,13 @@ bool tcg_op_supported(TCGOpcode op)
>  /* Note: we convert the 64 bit args to 32 bit and do some alignment
>     and endian swap. Maybe it would be better to do the alignment
>     and endian swap in tcg_reg_alloc_call(). */
> -void tcg_gen_callN(void *func, TCGTemp *ret, int nargs, TCGTemp **args)
> +static void do_tcg_gen_callN(TCGHelperInfo *info, TCGTemp *ret, int nargs,
> +                             TCGTemp **args)
>  {
>      int i, real_args, nb_rets, pi;
>      unsigned sizemask, flags;
> -    TCGHelperInfo *info;
> -    uint32_t hash = tcg_helper_func_hash(func);
>      TCGOp *op;
>
> -    info = qht_lookup_custom(&helper_table, func, hash, tcg_helper_lookup_cmp);
>      flags = info->flags;
>      sizemask = info->sizemask;
>
> @@ -1774,7 +1772,7 @@ void tcg_gen_callN(void *func, TCGTemp *ret, int nargs, TCGTemp **args)
>          op->args[pi++] = temp_arg(args[i]);
>          real_args++;
>      }
> -    op->args[pi++] = (uintptr_t)func;
> +    op->args[pi++] = (uintptr_t)info->func;
>      op->args[pi++] = flags;
>      TCGOP_CALLI(op) = real_args;
>
> @@ -1812,6 +1810,48 @@ void tcg_gen_callN(void *func, TCGTemp *ret, int nargs, TCGTemp **args)
>  #endif /* TCG_TARGET_EXTEND_ARGS */
>  }
>
> +void tcg_gen_callN(void *func, TCGTemp *ret, int nargs, TCGTemp **args)
> +{
> +    TCGHelperInfo *info;
> +    uint32_t hash = tcg_helper_func_hash(func);
> +
> +    /*
> +     * Here we can get away with tcg_helper_lookup_cmp, which only looks
> +     * at the function pointer, since we have the compile-time guarantee
> +     * that @func can only be in one TCGHelperInfo.
> +     */
> +    info = qht_lookup_custom(&helper_table, func, hash, tcg_helper_lookup_cmp);
> +    do_tcg_gen_callN(info, ret, nargs, args);
> +}
> +
> +void tcg_gen_runtime_helper(const TCGHelperInfo *orig, TCGTemp *ret, int nargs,
> +                            TCGTemp **args)
> +{
> +    TCGHelperInfo *info;
> +    uint32_t hash = tcg_helper_func_hash(orig->func);
> +
> +    /*
> +     * Use the full TCGHelperInfo lookup, since there is no guarantee that func
> +     * will be unique to each TCGHelperInfo. For instance, we could have the
> +     * same helper function registered in several TCGHelperInfo's, each of them
> +     * with different flags.
> +     */
> +    info = qht_lookup(&helper_table, orig, hash);
> +    if (info == NULL) {
> +        void *existing = NULL;
> +
> +        /* @orig might be in the stack, so we need to allocate a new struct */
> +        info = g_new(TCGHelperInfo, 1);
> +        memcpy(info, orig, sizeof(TCGHelperInfo));
> +        qht_insert(&helper_table, info, hash, &existing);
> +        if (unlikely(existing)) {
> +            g_free(info);
> +            info = existing;
> +        }
> +    }
> +    do_tcg_gen_callN(info, ret, nargs, args);
> +}
> +
>  static void tcg_reg_alloc_start(TCGContext *s)
>  {
>      int i, n;


--
Alex Bennée

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

* Re: [Qemu-devel] [RFC 09/48] tcg: reset runtime helpers when flushing the code cache
  2018-10-25 17:20 ` [Qemu-devel] [RFC 09/48] tcg: reset runtime helpers when flushing the code cache Emilio G. Cota
@ 2018-11-14 17:01   ` Alex Bennée
  2018-11-14 17:59     ` Emilio G. Cota
  0 siblings, 1 reply; 132+ messages in thread
From: Alex Bennée @ 2018-11-14 17:01 UTC (permalink / raw)
  To: Emilio G. Cota
  Cc: qemu-devel, Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Stefan Hajnoczi


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

> In preparation for adding plugin support. One of the clean-up
> actions when uninstalling plugins will be to flush the code
> cache. We'll also have to clear the runtime helpers, since
> some of those runtime helpers may belong to the plugin
> being uninstalled.
>
> Signed-off-by: Emilio G. Cota <cota@braap.org>
> ---
>  tcg/tcg.h                 |  1 +
>  accel/tcg/translate-all.c |  1 +
>  tcg/tcg.c                 | 21 +++++++++++++++++++++
>  3 files changed, 23 insertions(+)
>
> diff --git a/tcg/tcg.h b/tcg/tcg.h
> index 3fa434d891..2c378415d2 100644
> --- a/tcg/tcg.h
> +++ b/tcg/tcg.h
> @@ -1079,6 +1079,7 @@ bool tcg_op_supported(TCGOpcode op);
>  void tcg_gen_callN(void *func, TCGTemp *ret, int nargs, TCGTemp **args);
>  void tcg_gen_runtime_helper(const TCGHelperInfo *orig, TCGTemp *ret, int nargs,
>                              TCGTemp **args);
> +void tcg_reset_runtime_helpers(void);

I know tcg.h doesn't have and API doc blocks but perhaps we should
start?

  /**
   * tcg_reset_runtime_helpers:
   *
   * Remove all runtime registered helpers. Should only be called from a
   * quiescent system while flushing old code.
   */

>
>  TCGOp *tcg_emit_op(TCGOpcode opc);
>  void tcg_op_remove(TCGContext *s, TCGOp *op);
> diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c
> index 038d82fdb5..c8b3e0a491 100644
> --- a/accel/tcg/translate-all.c
> +++ b/accel/tcg/translate-all.c
> @@ -1255,6 +1255,7 @@ static void do_tb_flush(CPUState *cpu, run_on_cpu_data tb_flush_count)
>
>      qht_reset_size(&tb_ctx.htable, CODE_GEN_HTABLE_SIZE);
>      page_flush_tb();
> +    tcg_reset_runtime_helpers();
>
>      tcg_region_reset_all();
>      /* XXX: flush processor icache at this point if cache flush is
> diff --git a/tcg/tcg.c b/tcg/tcg.c
> index 87e02da740..a6824145b0 100644
> --- a/tcg/tcg.c
> +++ b/tcg/tcg.c
> @@ -874,6 +874,7 @@ static const TCGHelperInfo all_helpers[] = {
>  #include "exec/helper-tcg.h"
>  };
>  static struct qht helper_table;
> +static struct qht runtime_helper_table;
>  static bool helper_table_inited;
>
>  static int indirect_reg_alloc_order[ARRAY_SIZE(tcg_target_reg_alloc_order)];
> @@ -913,6 +914,22 @@ static void tcg_helper_insert(const TCGHelperInfo *info)
>      g_assert(inserted);
>  }
>
> +static void
> +rm_from_helper_table_and_free(void *p, uint32_t h, void *userp)
> +{
> +    bool success;
> +
> +    success = qht_remove(&helper_table, p, h);
> +    g_assert(success);
> +    g_free(p);
> +}
> +
> +void tcg_reset_runtime_helpers(void)
> +{
> +    qht_iter(&runtime_helper_table, rm_from_helper_table_and_free, NULL);
> +    qht_reset(&runtime_helper_table);
> +}
> +
>  void tcg_context_init(TCGContext *s)
>  {
>      int op, total_args, n, i;
> @@ -948,6 +965,7 @@ void tcg_context_init(TCGContext *s)
>      /* Register helpers.  */
>      qht_init(&helper_table, tcg_helper_cmp, ARRAY_SIZE(all_helpers),
>               QHT_MODE_AUTO_RESIZE);
> +    qht_init(&runtime_helper_table, tcg_helper_cmp, 1, QHT_MODE_AUTO_RESIZE);
>
>      for (i = 0; i < ARRAY_SIZE(all_helpers); ++i) {
>          tcg_helper_insert(&all_helpers[i]);
> @@ -1847,6 +1865,9 @@ void tcg_gen_runtime_helper(const TCGHelperInfo *orig, TCGTemp *ret, int nargs,
>          if (unlikely(existing)) {
>              g_free(info);
>              info = existing;
> +        } else {
> +            qht_insert(&runtime_helper_table, info, hash, &existing);
> +            g_assert(existing == NULL);
>          }
>      }
>      do_tcg_gen_callN(info, ret, nargs, args);

Otherwise:

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

--
Alex Bennée

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

* Re: [Qemu-devel] [RFC 01/48] cpu: introduce run_on_cpu_no_bql
  2018-11-14 11:30   ` Alex Bennée
@ 2018-11-14 17:08     ` Emilio G. Cota
  2018-11-14 18:23       ` Alex Bennée
  0 siblings, 1 reply; 132+ messages in thread
From: Emilio G. Cota @ 2018-11-14 17:08 UTC (permalink / raw)
  To: Alex Bennée
  Cc: qemu-devel, Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Stefan Hajnoczi

On Wed, Nov 14, 2018 at 11:30:19 +0000, Alex Bennée wrote:
> 
> Emilio G. Cota <cota@braap.org> writes:
> 
> > This allows us to queue synchronous CPU work without the BQL.
> >
> > Will gain a user soon.
> 
> This is also in the cpu-lock series right?

No, in the cpu-lock series we add async_run_on_cpu_no_bql;
here we add the sync version.

		E.

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

* Re: [Qemu-devel] [RFC 02/48] trace: expand mem_info:size_shift to 3 bits
  2018-11-14 13:03   ` Alex Bennée
@ 2018-11-14 17:17     ` Emilio G. Cota
  0 siblings, 0 replies; 132+ messages in thread
From: Emilio G. Cota @ 2018-11-14 17:17 UTC (permalink / raw)
  To: Alex Bennée
  Cc: qemu-devel, Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Stefan Hajnoczi

On Wed, Nov 14, 2018 at 13:03:19 +0000, Alex Bennée wrote:
> 
> Emilio G. Cota <cota@braap.org> writes:
> 
> > This will allow us to trace 16B-long memory accesses.
> >
> > While at it, add some defines for the mem_info bits and simplify
> > trace_mem_get_info by making it a wrapper around trace_mem_build_info.
> >
> > Signed-off-by: Emilio G. Cota <cota@braap.org>
> 
> Currently we ignore atomic operation but I assume we'll want to
> represent them at some point here.

We "ignore" them in that we just trace them without marking them
as atomic. We do trace them since d071f4cd55 ("trace: enable tracing
of TCG atomics", 2018-06-27), which BTW is a spin-off of early
iterations of this series.

> I don't know if memory barrier behaviour would also be worth exporting?

I don't see much value in that from a tracing viewpoint; if
users need such instruction-specific details they can just
use a plugin, where they can disassemble TB's.

Thanks,

		E.

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

* Re: [Qemu-devel] [RFC 06/48] tcg: use QHT for helper_table
  2018-11-14 14:41   ` Alex Bennée
@ 2018-11-14 17:50     ` Emilio G. Cota
  0 siblings, 0 replies; 132+ messages in thread
From: Emilio G. Cota @ 2018-11-14 17:50 UTC (permalink / raw)
  To: Alex Bennée
  Cc: qemu-devel, Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Stefan Hajnoczi

On Wed, Nov 14, 2018 at 14:41:53 +0000, Alex Bennée wrote:
> Emilio G. Cota <cota@braap.org> writes:
(snip)
> > -static GHashTable *helper_table;
> > +static struct qht helper_table;
> > +static bool helper_table_inited;
> 
> Having a flag for initialisation seems a little excessive considering
> we've moved that initialisation into tcg_context_init() which has to be
> called before we do anything TCG related.

(snip)
> > +    helper_table_inited = true;
> 
> so I think we can drop this and...

(snip)
> > +static inline const char *tcg_helper_find(TCGContext *s, uintptr_t val)
> >  {
> >      const char *ret = NULL;
> > -    if (helper_table) {
> > -        TCGHelperInfo *info = g_hash_table_lookup(helper_table, (gpointer)val);
> > +    if (helper_table_inited) {
> 
> change this to a assert(helper_table.cmp) if you really want to.

I like this suggestion. The only caller of tcg_helper_find
is tcg_dump_ops, which is unlikely to be called on an
uninitialized TCGContext.

I have added this to v2, without the assert.

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

Thanks!

		E.

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

* Re: [Qemu-devel] [RFC 06/48] tcg: use QHT for helper_table
  2018-11-14 16:11   ` Alex Bennée
@ 2018-11-14 17:52     ` Emilio G. Cota
  0 siblings, 0 replies; 132+ messages in thread
From: Emilio G. Cota @ 2018-11-14 17:52 UTC (permalink / raw)
  To: Alex Bennée
  Cc: qemu-devel, Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Stefan Hajnoczi

On Wed, Nov 14, 2018 at 16:11:35 +0000, Alex Bennée wrote:
> 
> Emilio G. Cota <cota@braap.org> writes:
(snip)
> I needed to do this:
> 
> modified   tcg/tcg.c
> @@ -884,7 +884,7 @@ static TCGTemp *tcg_global_reg_new_internal(TCGContext *s, TCGType type,
> 
>  static inline uint32_t tcg_helper_func_hash(const void *func)
>  {
> -    return qemu_xxhash2((uint64_t)func);
> +    return qemu_xxhash2((uint64_t)(uintptr_t)func);
>  }
> 
> To prevent the compiler complaining about:
> 
>  tcg.c:887:25: error: cast from pointer to integer of different size [-Werror=pointer-to-int-cast]

Fixed, thanks.

		E.

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

* Re: [Qemu-devel] [RFC 09/48] tcg: reset runtime helpers when flushing the code cache
  2018-11-14 17:01   ` Alex Bennée
@ 2018-11-14 17:59     ` Emilio G. Cota
  0 siblings, 0 replies; 132+ messages in thread
From: Emilio G. Cota @ 2018-11-14 17:59 UTC (permalink / raw)
  To: Alex Bennée
  Cc: qemu-devel, Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Stefan Hajnoczi

On Wed, Nov 14, 2018 at 17:01:13 +0000, Alex Bennée wrote:
> 
> Emilio G. Cota <cota@braap.org> writes:
> 
> > In preparation for adding plugin support. One of the clean-up
> > actions when uninstalling plugins will be to flush the code
> > cache. We'll also have to clear the runtime helpers, since
> > some of those runtime helpers may belong to the plugin
> > being uninstalled.
> >
> > Signed-off-by: Emilio G. Cota <cota@braap.org>
> > ---
(snip)
> > +void tcg_reset_runtime_helpers(void);
> 
> I know tcg.h doesn't have and API doc blocks but perhaps we should
> start?
> 
>   /**
>    * tcg_reset_runtime_helpers:
>    *
>    * Remove all runtime registered helpers. Should only be called from a
>    * quiescent system while flushing old code.
>    */

Better late than never. Added to v2.

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

Thanks,

		E.

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

* Re: [Qemu-devel] [RFC 01/48] cpu: introduce run_on_cpu_no_bql
  2018-11-14 17:08     ` Emilio G. Cota
@ 2018-11-14 18:23       ` Alex Bennée
  0 siblings, 0 replies; 132+ messages in thread
From: Alex Bennée @ 2018-11-14 18:23 UTC (permalink / raw)
  To: Emilio G. Cota
  Cc: qemu-devel, Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Stefan Hajnoczi


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

> On Wed, Nov 14, 2018 at 11:30:19 +0000, Alex Bennée wrote:
>>
>> Emilio G. Cota <cota@braap.org> writes:
>>
>> > This allows us to queue synchronous CPU work without the BQL.
>> >
>> > Will gain a user soon.
>>
>> This is also in the cpu-lock series right?
>
> No, in the cpu-lock series we add async_run_on_cpu_no_bql;
> here we add the sync version.

My mistake, being word blind:

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

--
Alex Bennée

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

* Re: [Qemu-devel] [RFC 44/48] cpus: lockstep execution support
  2018-11-14 16:43   ` Alex Bennée
@ 2018-11-14 18:33     ` Emilio G. Cota
  0 siblings, 0 replies; 132+ messages in thread
From: Emilio G. Cota @ 2018-11-14 18:33 UTC (permalink / raw)
  To: Alex Bennée
  Cc: qemu-devel, Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Stefan Hajnoczi

On Wed, Nov 14, 2018 at 16:43:22 +0000, Alex Bennée wrote:
> 
> Emilio G. Cota <cota@braap.org> writes:
> 
> > Signed-off-by: Emilio G. Cota <cota@braap.org>
> > ---
> <snip>
> >
> >  void cpu_interrupt(CPUState *cpu, int mask);
> > diff --git a/cpus.c b/cpus.c
> > index 3efe89354d..a446632a5c 100644
> > --- a/cpus.c
> > +++ b/cpus.c
> <snip>
> > +
> > +static void cpu_lockstep_init(CPUState *cpu)
> > +{
> > +    if (!lockstep_enabled) {
> > +        return;
> > +    }
> > +    qemu_mutex_lock(&lockstep_lock);
> > +    /*
> > +     * HACK: avoid racing with a wakeup, which would miss the addition
> > +     * of this CPU; just wait until no wakeup is ongoing.
> > +     */
> > +    while (unlikely(lockstep_ongoing_wakeup)) {
> > +        qemu_mutex_unlock(&lockstep_lock);
> > +        sched_yield();
> 
> This breaks Windows builds. I suspect if we do want to this sort of
> functionality we'll need to expose a utility function in
> oslib-posix/oslib-win32

This was just a quick hack to avoid adding a condvar to the
wake-up fast path. Fixed in v2 with the below, which only calls
cond_broadcast if needed (i.e. very rarely).

Thanks,

		Emilio

diff --git a/cpus.c b/cpus.c
index d7d1bd3e00..06a952e504 100644
--- a/cpus.c
+++ b/cpus.c
@@ -84,8 +84,10 @@ static unsigned int throttle_percentage;
 static bool lockstep_enabled;
 static bool lockstep_ongoing_wakeup;
 static QemuMutex lockstep_lock;
+static QemuCond lockstep_cond;
 static int n_lockstep_running_cpus;
 static int n_lockstep_cpus;
+static int n_lockstep_initializing_cpus;
 static CPUState **lockstep_cpus;
 
 #define CPU_THROTTLE_PCT_MIN 1
@@ -1260,6 +1262,7 @@ void qemu_init_cpu_loop(void)
     qemu_init_sigbus();
     qemu_mutex_init(&qemu_global_mutex);
     qemu_mutex_init(&lockstep_lock);
+    qemu_cond_init(&lockstep_cond);
 
     qemu_thread_get_self(&io_thread);
 }
@@ -1369,6 +1372,13 @@ static void lockstep_check_stop(CPUState *cpu)
             cpu_mutex_lock(cpu);
             qemu_mutex_lock(&lockstep_lock);
             lockstep_ongoing_wakeup = false;
+            /*
+             * If newly spawned CPUs are waiting to be added to the wait list,
+             * let them do so now.
+             */
+            if (unlikely(n_lockstep_initializing_cpus)) {
+                qemu_cond_broadcast(&lockstep_cond);
+            }
         }
         qemu_mutex_unlock(&lockstep_lock);
     }
@@ -1379,16 +1389,15 @@ static void cpu_lockstep_init(CPUState *cpu)
     if (!lockstep_enabled) {
         return;
     }
+
     qemu_mutex_lock(&lockstep_lock);
-    /*
-     * HACK: avoid racing with a wakeup, which would miss the addition
-     * of this CPU; just wait until no wakeup is ongoing.
-     */
-    while (unlikely(lockstep_ongoing_wakeup)) {
-        qemu_mutex_unlock(&lockstep_lock);
-        sched_yield();
-        qemu_mutex_lock(&lockstep_lock);
+    /* avoid racing with a wakeup, which would miss the addition of this CPU */
+    n_lockstep_initializing_cpus++;
+    while (lockstep_ongoing_wakeup) {
+        qemu_cond_wait(&lockstep_cond, &lockstep_lock);
     }
+    n_lockstep_initializing_cpus--;
+
     lockstep_cpus = g_realloc(lockstep_cpus,
                               (n_lockstep_cpus + 1) * sizeof(CPUState *));
     lockstep_cpus[n_lockstep_cpus++] = cpu;

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

* Re: [Qemu-devel] [RFC 10/48] exec: export do_tb_flush
  2018-10-25 17:20 ` [Qemu-devel] [RFC 10/48] exec: export do_tb_flush Emilio G. Cota
@ 2018-11-22 17:09   ` Alex Bennée
  2018-11-23 23:19     ` Emilio G. Cota
  0 siblings, 1 reply; 132+ messages in thread
From: Alex Bennée @ 2018-11-22 17:09 UTC (permalink / raw)
  To: Emilio G. Cota
  Cc: qemu-devel, Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Stefan Hajnoczi


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

> This will be used by plugin code to flush the code cache as well
> as doing other bookkeeping in a safe work environment.

This seems a little excessive given the plugin code could just call
tb_flush() directly. Wouldn't calling tb_flush after scheduling the
plugin_destroy be enough?

If there is a race condition here maybe we could build some sort of
awareness into tb_flush as to the current run state. But having two
entry points to this rather fundamental action seems likely to either be
misused or misunderstood.

>
> Signed-off-by: Emilio G. Cota <cota@braap.org>
> ---
>  include/exec/exec-all.h   | 1 +
>  accel/tcg/translate-all.c | 2 +-
>  2 files changed, 2 insertions(+), 1 deletion(-)
>
> diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h
> index 815e5b1e83..232e2f8966 100644
> --- a/include/exec/exec-all.h
> +++ b/include/exec/exec-all.h
> @@ -427,6 +427,7 @@ void tb_invalidate_phys_range(target_ulong start, target_ulong end);
>  void tb_invalidate_phys_addr(AddressSpace *as, hwaddr addr, MemTxAttrs attrs);
>  #endif
>  void tb_flush(CPUState *cpu);
> +void do_tb_flush(CPUState *cpu, run_on_cpu_data tb_flush_count);
>  void tb_phys_invalidate(TranslationBlock *tb, tb_page_addr_t page_addr);
>  TranslationBlock *tb_htable_lookup(CPUState *cpu, target_ulong pc,
>                                     target_ulong cs_base, uint32_t flags,
> diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c
> index c8b3e0a491..db2d28f8d3 100644
> --- a/accel/tcg/translate-all.c
> +++ b/accel/tcg/translate-all.c
> @@ -1230,7 +1230,7 @@ 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)
> +void do_tb_flush(CPUState *cpu, run_on_cpu_data tb_flush_count)
>  {
>      mmap_lock();
>      /* If it is already been done on request of another CPU,


--
Alex Bennée

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

* Re: [Qemu-devel] [RFC 11/48] atomic_template: fix indentation in GEN_ATOMIC_HELPER
  2018-10-25 17:20 ` [Qemu-devel] [RFC 11/48] atomic_template: fix indentation in GEN_ATOMIC_HELPER Emilio G. Cota
@ 2018-11-22 17:09   ` Alex Bennée
  0 siblings, 0 replies; 132+ messages in thread
From: Alex Bennée @ 2018-11-22 17:09 UTC (permalink / raw)
  To: Emilio G. Cota
  Cc: qemu-devel, Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Stefan Hajnoczi


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

> Signed-off-by: Emilio G. Cota <cota@braap.org>

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

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


--
Alex Bennée

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

* Re: [Qemu-devel] [RFC 12/48] atomic_template: define pre/post macros
  2018-10-25 17:20 ` [Qemu-devel] [RFC 12/48] atomic_template: define pre/post macros Emilio G. Cota
@ 2018-11-22 17:12   ` Alex Bennée
  2018-11-24  0:09     ` Emilio G. Cota
  0 siblings, 1 reply; 132+ messages in thread
From: Alex Bennée @ 2018-11-22 17:12 UTC (permalink / raw)
  To: Emilio G. Cota
  Cc: qemu-devel, Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Stefan Hajnoczi


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

> In preparation for plugin support.
>
> Signed-off-by: Emilio G. Cota <cota@braap.org>

More macros for the macro-god. I guess this works but I wonder if it's
possible to do a clean-up ala softfloat and the experimental softmmu
re-factor that makes this less a mess of macros?


> ---
>  accel/tcg/atomic_template.h | 92 +++++++++++++++++++++++--------------
>  1 file changed, 57 insertions(+), 35 deletions(-)
>
> diff --git a/accel/tcg/atomic_template.h b/accel/tcg/atomic_template.h
> index 8d177fefef..b13318c1ce 100644
> --- a/accel/tcg/atomic_template.h
> +++ b/accel/tcg/atomic_template.h
> @@ -59,25 +59,26 @@
>  # define ABI_TYPE  uint32_t
>  #endif
>
> -#define ATOMIC_TRACE_RMW do {                                           \
> -        uint8_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, false); \
> -                                                                        \
> -        trace_guest_mem_before_exec(ENV_GET_CPU(env), addr, info);      \
> -        trace_guest_mem_before_exec(ENV_GET_CPU(env), addr,             \
> -                                    info | TRACE_MEM_ST);               \
> -    } while (0)
> -
> -#define ATOMIC_TRACE_LD do {                                            \
> -        uint8_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, false); \
> -                                                                        \
> -        trace_guest_mem_before_exec(ENV_GET_CPU(env), addr, info);      \
> -    } while (0)
> -
> -# define ATOMIC_TRACE_ST do {                                           \
> -        uint8_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, true); \
> -                                                                        \
> -        trace_guest_mem_before_exec(ENV_GET_CPU(env), addr, info);      \
> -    } while (0)
> +/* these don't depend on MEND/SHIFT, so we just define them once */
> +#ifndef ATOMIC_TRACE_RMW_PRE
> +# define ATOMIC_TRACE_RMW_PRE do {                                            \
> +    trace_guest_mem_before_exec(ENV_GET_CPU(env), addr, info);                \
> +    trace_guest_mem_before_exec(ENV_GET_CPU(env), addr, info | TRACE_MEM_ST); \
> +} while (0)
> +
> +# define ATOMIC_TRACE_RMW_POST                                          \
> +
> +# define ATOMIC_TRACE_LD_PRE                                    \
> +    trace_guest_mem_before_exec(ENV_GET_CPU(env), addr, info)
> +
> +# define ATOMIC_TRACE_LD_POST                                           \
> +
> +# define ATOMIC_TRACE_ST_PRE                                    \
> +    trace_guest_mem_before_exec(ENV_GET_CPU(env), addr, info)
> +
> +# define ATOMIC_TRACE_ST_POST                                           \
> +
> +#endif /* ATOMIC_TRACE_RMW_PRE */
>
>  /* Define host-endian atomic operations.  Note that END is used within
>     the ATOMIC_NAME macro, and redefined below.  */
> @@ -98,14 +99,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;
>  #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;
>      return ret;
>  }
>
> @@ -115,10 +118,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;
>      val = atomic16_read(haddr);
>      ATOMIC_MMU_CLEANUP;
> +    ATOMIC_TRACE_LD_POST;
>      return val;
>  }
>
> @@ -127,10 +132,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;
>      atomic16_set(haddr, val);
>      ATOMIC_MMU_CLEANUP;
> +    ATOMIC_TRACE_ST_POST;
>  }
>  #endif
>  #else
> @@ -140,10 +147,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;
>      ret = atomic_xchg__nocheck(haddr, val);
>      ATOMIC_MMU_CLEANUP;
> +    ATOMIC_TRACE_RMW_POST;
>      return ret;
>  }
>
> @@ -154,10 +163,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;                                           \
>      ret = atomic_##X(haddr, val);                                   \
>      ATOMIC_MMU_CLEANUP;                                             \
> +    ATOMIC_TRACE_RMW_POST;                                          \
>      return ret;                                                     \
>  }
>
> @@ -186,8 +197,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;                                           \
>      smp_mb();                                                       \
>      cmp = atomic_read__nocheck(haddr);                              \
>      do {                                                            \
> @@ -195,6 +207,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;                                          \
>      return RET;                                                     \
>  }
>
> @@ -232,14 +245,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;
>  #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;
>      return BSWAP(ret);
>  }
>
> @@ -249,10 +264,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;
>      val = atomic16_read(haddr);
>      ATOMIC_MMU_CLEANUP;
> +    ATOMIC_TRACE_LD_POST;
>      return BSWAP(val);
>  }
>
> @@ -261,11 +278,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;
>      val = BSWAP(val);
>      atomic16_set(haddr, val);
>      ATOMIC_MMU_CLEANUP;
> +    ATOMIC_TRACE_ST_POST;
>  }
>  #endif
>  #else
> @@ -275,10 +295,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;
>      ret = atomic_xchg__nocheck(haddr, BSWAP(val));
>      ATOMIC_MMU_CLEANUP;
> +    ATOMIC_TRACE_RMW_POST;
>      return BSWAP(ret);
>  }
>
> @@ -289,10 +311,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;                                           \
>      ret = atomic_##X(haddr, BSWAP(val));                            \
>      ATOMIC_MMU_CLEANUP;                                             \
> +    ATOMIC_TRACE_RMW_POST;                                          \
>      return BSWAP(ret);                                              \
>  }
>
> @@ -319,8 +343,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;                                           \
>      smp_mb();                                                       \
>      ldn = atomic_read__nocheck(haddr);                              \
>      do {                                                            \
> @@ -328,6 +353,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;                                          \
>      return RET;                                                     \
>  }
>
> @@ -355,10 +381,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


--
Alex Bennée

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

* Re: [Qemu-devel] [RFC 13/48] xxhash: add qemu_xxhash8
  2018-10-25 17:20 ` [Qemu-devel] [RFC 13/48] xxhash: add qemu_xxhash8 Emilio G. Cota
@ 2018-11-22 17:15   ` Alex Bennée
  2018-11-23 22:34     ` Emilio G. Cota
  0 siblings, 1 reply; 132+ messages in thread
From: Alex Bennée @ 2018-11-22 17:15 UTC (permalink / raw)
  To: Emilio G. Cota
  Cc: qemu-devel, Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Stefan Hajnoczi


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

> It will be used for TB hashing soon.
>
> Signed-off-by: Emilio G. Cota <cota@braap.org>
> ---
>  include/qemu/xxhash.h | 40 +++++++++++++++++++++++++++-------------
>  1 file changed, 27 insertions(+), 13 deletions(-)
>
> diff --git a/include/qemu/xxhash.h b/include/qemu/xxhash.h
> index fe35dde328..450427eeaa 100644
> --- a/include/qemu/xxhash.h
> +++ b/include/qemu/xxhash.h
> @@ -49,7 +49,8 @@
>   * contiguous in memory.
>   */
>  static inline uint32_t
> -qemu_xxhash7(uint64_t ab, uint64_t cd, uint32_t e, uint32_t f, uint32_t g)
> +qemu_xxhash8(uint64_t ab, uint64_t cd, uint32_t e, uint32_t f, uint32_t g,
> +             uint32_t h)

As we've expanded to bigger and bigger hashes why are everything after
cd passed as 32 bit values? Isn't this just generating extra register
pressure or is the compiler smart enough to figure it out?

>  {
>      uint32_t v1 = QEMU_XXHASH_SEED + PRIME32_1 + PRIME32_2;
>      uint32_t v2 = QEMU_XXHASH_SEED + PRIME32_2;
> @@ -77,17 +78,24 @@ qemu_xxhash7(uint64_t ab, uint64_t cd, uint32_t e, uint32_t f, uint32_t g)
>      v4 = rol32(v4, 13);
>      v4 *= PRIME32_1;
>
> -    h32 = rol32(v1, 1) + rol32(v2, 7) + rol32(v3, 12) + rol32(v4, 18);
> -    h32 += 28;
> +    v1 += e * PRIME32_2;
> +    v1 = rol32(v1, 13);
> +    v1 *= PRIME32_1;
>
> -    h32 += e * PRIME32_3;
> -    h32  = rol32(h32, 17) * PRIME32_4;
> +    v2 += f * PRIME32_2;
> +    v2 = rol32(v2, 13);
> +    v2 *= PRIME32_1;
> +
> +    v3 += g * PRIME32_2;
> +    v3 = rol32(v3, 13);
> +    v3 *= PRIME32_1;
>
> -    h32 += f * PRIME32_3;
> -    h32  = rol32(h32, 17) * PRIME32_4;
> +    v4 += h * PRIME32_2;
> +    v4 = rol32(v4, 13);
> +    v4 *= PRIME32_1;
>
> -    h32 += g * PRIME32_3;
> -    h32  = rol32(h32, 17) * PRIME32_4;
> +    h32 = rol32(v1, 1) + rol32(v2, 7) + rol32(v3, 12) + rol32(v4, 18);
> +    h32 += 32;

How do we validate we haven't broken the distribution of the original
xxhash as we've extended it?

>
>      h32 ^= h32 >> 15;
>      h32 *= PRIME32_2;
> @@ -100,23 +108,29 @@ qemu_xxhash7(uint64_t ab, uint64_t cd, uint32_t e, uint32_t f, uint32_t g)
>
>  static inline uint32_t qemu_xxhash2(uint64_t ab)
>  {
> -    return qemu_xxhash7(ab, 0, 0, 0, 0);
> +    return qemu_xxhash8(ab, 0, 0, 0, 0, 0);
>  }
>
>  static inline uint32_t qemu_xxhash4(uint64_t ab, uint64_t cd)
>  {
> -    return qemu_xxhash7(ab, cd, 0, 0, 0);
> +    return qemu_xxhash8(ab, cd, 0, 0, 0, 0);
>  }
>
>  static inline uint32_t qemu_xxhash5(uint64_t ab, uint64_t cd, uint32_t e)
>  {
> -    return qemu_xxhash7(ab, cd, e, 0, 0);
> +    return qemu_xxhash8(ab, cd, e, 0, 0, 0);
>  }
>
>  static inline uint32_t qemu_xxhash6(uint64_t ab, uint64_t cd, uint32_t e,
>                                      uint32_t f)
>  {
> -    return qemu_xxhash7(ab, cd, e, f, 0);
> +    return qemu_xxhash8(ab, cd, e, f, 0, 0);
> +}
> +
> +static inline uint32_t qemu_xxhash7(uint64_t ab, uint64_t cd, uint32_t e,
> +                                    uint32_t f, uint32_t g)
> +{
> +    return qemu_xxhash8(ab, cd, e, f, g, 0);
>  }
>
>  #endif /* QEMU_XXHASH_H */


--
Alex Bennée

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

* Re: [Qemu-devel] [RFC 16/48] tcg: add plugin_mask to TB hash
  2018-10-25 17:20 ` [Qemu-devel] [RFC 16/48] tcg: add plugin_mask to TB hash Emilio G. Cota
@ 2018-11-23 16:52   ` Alex Bennée
  2018-11-24  0:02     ` Emilio G. Cota
  0 siblings, 1 reply; 132+ messages in thread
From: Alex Bennée @ 2018-11-23 16:52 UTC (permalink / raw)
  To: Emilio G. Cota
  Cc: qemu-devel, Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Stefan Hajnoczi


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

> Signed-off-by: Emilio G. Cota <cota@braap.org>

This commit message needs more description. What is this plugin mask
for? Are we extending the TB flags with a mask of loaded plugins so we
can correctly identify what code was generated under what plugins?

> ---
>  include/exec/exec-all.h   | 2 ++
>  include/exec/tb-hash.h    | 6 ++++--
>  include/exec/tb-lookup.h  | 1 +
>  accel/tcg/cpu-exec.c      | 6 +++++-
>  accel/tcg/translate-all.c | 6 ++++--
>  5 files changed, 16 insertions(+), 5 deletions(-)
>
> diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h
> index 232e2f8966..a1f60404b6 100644
> --- a/include/exec/exec-all.h
> +++ b/include/exec/exec-all.h
> @@ -358,6 +358,8 @@ struct TranslationBlock {
>      /* Per-vCPU dynamic tracing state used to generate this TB */
>      uint32_t trace_vcpu_dstate;
>
> +    uint32_t plugin_mask;
> +
>      struct tb_tc tc;
>
>      /* original tb when cflags has CF_NOCACHE */
> diff --git a/include/exec/tb-hash.h b/include/exec/tb-hash.h
> index 4f3a37d927..37292474b7 100644
> --- a/include/exec/tb-hash.h
> +++ b/include/exec/tb-hash.h
> @@ -59,9 +59,11 @@ static inline unsigned int tb_jmp_cache_hash_func(target_ulong pc)
>
>  static inline
>  uint32_t tb_hash_func(tb_page_addr_t phys_pc, target_ulong pc, uint32_t flags,
> -                      uint32_t cf_mask, uint32_t trace_vcpu_dstate)
> +                      uint32_t cf_mask, uint32_t trace_vcpu_dstate,
> +                      uint32_t plugin_mask)
>  {
> -    return qemu_xxhash7(phys_pc, pc, flags, cf_mask, trace_vcpu_dstate);
> +    return qemu_xxhash8(phys_pc, pc, flags, cf_mask, trace_vcpu_dstate,
> +                        plugin_mask);
>  }
>
>  #endif
> diff --git a/include/exec/tb-lookup.h b/include/exec/tb-lookup.h
> index 492cb68289..dd1572f481 100644
> --- a/include/exec/tb-lookup.h
> +++ b/include/exec/tb-lookup.h
> @@ -33,6 +33,7 @@ tb_lookup__cpu_state(CPUState *cpu, target_ulong *pc, target_ulong *cs_base,
>                 tb->cs_base == *cs_base &&
>                 tb->flags == *flags &&
>                 tb->trace_vcpu_dstate == *cpu->trace_dstate &&
> +               tb->plugin_mask == *cpu->plugin_mask &&
>                 (tb_cflags(tb) & (CF_HASH_MASK | CF_INVALID)) == cf_mask)) {
>          return tb;
>      }
> diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c
> index d590f1f6c0..27aa3451da 100644
> --- a/accel/tcg/cpu-exec.c
> +++ b/accel/tcg/cpu-exec.c
> @@ -287,6 +287,7 @@ struct tb_desc {
>      uint32_t flags;
>      uint32_t cf_mask;
>      uint32_t trace_vcpu_dstate;
> +    uint32_t plugin_mask;
>  };
>
>  static bool tb_lookup_cmp(const void *p, const void *d)
> @@ -299,6 +300,7 @@ static bool tb_lookup_cmp(const void *p, const void *d)
>          tb->cs_base == desc->cs_base &&
>          tb->flags == desc->flags &&
>          tb->trace_vcpu_dstate == desc->trace_vcpu_dstate &&
> +        tb->plugin_mask == desc->plugin_mask &&
>          (tb_cflags(tb) & (CF_HASH_MASK | CF_INVALID)) == desc->cf_mask) {
>          /* check next page if needed */
>          if (tb->page_addr[1] == -1) {
> @@ -330,13 +332,15 @@ TranslationBlock *tb_htable_lookup(CPUState *cpu, target_ulong pc,
>      desc.flags = flags;
>      desc.cf_mask = cf_mask;
>      desc.trace_vcpu_dstate = *cpu->trace_dstate;
> +    desc.plugin_mask = *cpu->plugin_mask;
>      desc.pc = pc;
>      phys_pc = get_page_addr_code(desc.env, pc);
>      if (phys_pc == -1) {
>          return NULL;
>      }
>      desc.phys_page1 = phys_pc & TARGET_PAGE_MASK;
> -    h = tb_hash_func(phys_pc, pc, flags, cf_mask, *cpu->trace_dstate);
> +    h = tb_hash_func(phys_pc, pc, flags, cf_mask, *cpu->trace_dstate,
> +                     *cpu->plugin_mask);
>      return qht_lookup_custom(&tb_ctx.htable, &desc, h, tb_lookup_cmp);
>  }
>
> diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c
> index db2d28f8d3..3423cf74db 100644
> --- a/accel/tcg/translate-all.c
> +++ b/accel/tcg/translate-all.c
> @@ -1129,6 +1129,7 @@ static bool tb_cmp(const void *ap, const void *bp)
>          a->flags == b->flags &&
>          (tb_cflags(a) & CF_HASH_MASK) == (tb_cflags(b) & CF_HASH_MASK) &&
>          a->trace_vcpu_dstate == b->trace_vcpu_dstate &&
> +        a->plugin_mask == b->plugin_mask &&
>          a->page_addr[0] == b->page_addr[0] &&
>          a->page_addr[1] == b->page_addr[1];
>  }
> @@ -1444,7 +1445,7 @@ static void do_tb_phys_invalidate(TranslationBlock *tb, bool rm_from_page_list)
>      /* remove the TB from the hash list */
>      phys_pc = tb->page_addr[0] + (tb->pc & ~TARGET_PAGE_MASK);
>      h = tb_hash_func(phys_pc, tb->pc, tb->flags, tb_cflags(tb) & CF_HASH_MASK,
> -                     tb->trace_vcpu_dstate);
> +                     tb->trace_vcpu_dstate, tb->plugin_mask);
>      if (!(tb->cflags & CF_NOCACHE) &&
>          !qht_remove(&tb_ctx.htable, tb, h)) {
>          return;
> @@ -1640,7 +1641,7 @@ tb_link_page(TranslationBlock *tb, tb_page_addr_t phys_pc,
>
>          /* add in the hash table */
>          h = tb_hash_func(phys_pc, tb->pc, tb->flags, tb->cflags & CF_HASH_MASK,
> -                         tb->trace_vcpu_dstate);
> +                         tb->trace_vcpu_dstate, tb->plugin_mask);
>          qht_insert(&tb_ctx.htable, tb, h, &existing_tb);
>
>          /* remove TB from the page(s) if we couldn't insert it */
> @@ -1712,6 +1713,7 @@ TranslationBlock *tb_gen_code(CPUState *cpu,
>      tb->cflags = cflags;
>      tb->trace_vcpu_dstate = *cpu->trace_dstate;
>      tcg_ctx->tb_cflags = cflags;
> +    tb->plugin_mask = *cpu->plugin_mask;
>
>  #ifdef CONFIG_PROFILER
>      /* includes aborted translations because of exceptions */


--
Alex Bennée

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

* Re: [Qemu-devel] [RFC 18/48] tcg: add memory callbacks for plugins (WIP)
  2018-10-25 17:20 ` [Qemu-devel] [RFC 18/48] tcg: add memory callbacks for plugins (WIP) Emilio G. Cota
@ 2018-11-23 16:55   ` Alex Bennée
  0 siblings, 0 replies; 132+ messages in thread
From: Alex Bennée @ 2018-11-23 16:55 UTC (permalink / raw)
  To: Emilio G. Cota
  Cc: qemu-devel, Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Stefan Hajnoczi


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

> XXX: store hostaddr from non-i386 TCG backends
> XXX: what hostaddr to return for I/O accesses?
> XXX: what hostaddr to return for cross-page accesses?
>
> Here the trickiest feature is passing the host address to
> memory callbacks that request it. Perhaps it would be more
> appropriate to pass a "physical" address to plugins, but since
> in QEMU host addr ~= guest physical, I'm going with that for
> simplicity.
>
> To keep the implementation simple we piggy-back on the TLB fast path,
> and thus can only provide the host address _after_ memory accesses
> have occurred. For the slow path, it's a bit tedious because there
> are many places to update, but it's fairly simple.
>
> However, note that cross-page accesses are tricky, since the
> access might be to non-contiguous host addresses. So I'm punting
> on that and just passing NULL.
>
> Signed-off-by: Emilio G. Cota <cota@braap.org>
> ---
>  accel/tcg/atomic_template.h               |  8 ++++-
>  accel/tcg/softmmu_template.h              | 39 ++++++++++++++++++++
>  include/exec/cpu-defs.h                   |  2 ++
>  include/exec/cpu_ldst_template.h          | 43 +++++++++++++++--------
>  include/exec/cpu_ldst_useronly_template.h | 42 +++++++++++++++-------
>  tcg/tcg-op.h                              |  5 +++
>  tcg/tcg.h                                 |  4 +++
>  tcg/i386/tcg-target.inc.c                 |  5 +++
>  tcg/tcg-op.c                              | 37 ++++++++++++++-----
>  tcg/tcg.c                                 |  3 ++
>  10 files changed, 152 insertions(+), 36 deletions(-)
>
> diff --git a/accel/tcg/atomic_template.h b/accel/tcg/atomic_template.h
> index b13318c1ce..3de34dc462 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
> @@ -66,17 +67,22 @@
>      trace_guest_mem_before_exec(ENV_GET_CPU(env), addr, info | TRACE_MEM_ST); \
>  } while (0)
>
> -# define ATOMIC_TRACE_RMW_POST                                          \
> +# define ATOMIC_TRACE_RMW_POST do {                                            \
> +  qemu_plugin_vcpu_mem_cb(ENV_GET_CPU(env), addr, haddr, info);                \
> +  qemu_plugin_vcpu_mem_cb(ENV_GET_CPU(env), addr, haddr, info | TRACE_MEM_ST); \
> +} while (0)
>
>  # define ATOMIC_TRACE_LD_PRE                                    \
>      trace_guest_mem_before_exec(ENV_GET_CPU(env), addr, info)
>
>  # define ATOMIC_TRACE_LD_POST                                           \
> +    qemu_plugin_vcpu_mem_cb(ENV_GET_CPU(env), addr, haddr, info)
>
>  # define ATOMIC_TRACE_ST_PRE                                    \
>      trace_guest_mem_before_exec(ENV_GET_CPU(env), addr, info)
>
>  # define ATOMIC_TRACE_ST_POST                                           \
> +    qemu_plugin_vcpu_mem_cb(ENV_GET_CPU(env), addr, haddr, info)
>
>  #endif /* ATOMIC_TRACE_RMW_PRE */
>
> diff --git a/accel/tcg/softmmu_template.h b/accel/tcg/softmmu_template.h
> index b0adea045e..f6d2f60b81 100644
> --- a/accel/tcg/softmmu_template.h
> +++ b/accel/tcg/softmmu_template.h
> @@ -103,6 +103,11 @@ static inline DATA_TYPE glue(io_read, SUFFIX)(CPUArchState *env,
>                                                MMUAccessType access_type)
>  {
>      CPUIOTLBEntry *iotlbentry = &env->iotlb[mmu_idx][index];
> +
> +    /* XXX Any sensible choice other than NULL? */
> +    if (tcg_ctx->plugin_mem_cb) {
> +        env->hostaddr = NULL;
> +    }

This is more argument for getting the softmmu de-macrofiction in first.


--
Alex Bennée

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

* Re: [Qemu-devel] [RFC 19/48] translate-all: notify plugin code of tb_flush
  2018-10-25 17:20 ` [Qemu-devel] [RFC 19/48] translate-all: notify plugin code of tb_flush Emilio G. Cota
@ 2018-11-23 17:00   ` Alex Bennée
  2018-11-23 23:15     ` Emilio G. Cota
  0 siblings, 1 reply; 132+ messages in thread
From: Alex Bennée @ 2018-11-23 17:00 UTC (permalink / raw)
  To: Emilio G. Cota
  Cc: qemu-devel, Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Stefan Hajnoczi


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

> 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 3423cf74db..1690e3fd5b 100644
> --- a/accel/tcg/translate-all.c
> +++ b/accel/tcg/translate-all.c
> @@ -1233,6 +1233,8 @@ static gboolean tb_host_size_iter(gpointer key, gpointer value, gpointer data)
>  /* flush all the translation blocks */
>  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.
> @@ -1240,6 +1242,7 @@ 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();
> @@ -1265,6 +1268,9 @@ void do_tb_flush(CPUState *cpu, run_on_cpu_data tb_flush_count)
>
>  done:
>      mmap_unlock();
> +    if (did_flush) {
> +        qemu_plugin_flush_cb();
> +    }

Are we introducing a race here?

What is the purpose of letting the plugin know a flush has occurred?

It shouldn't have any knowledge of the details of liveliness of the
translated code and if it still exits or not. If all it wants to do is
look at the counts then I think we can provide a simpler less abuse-able
way to do this.

>  }
>
>  void tb_flush(CPUState *cpu)


--
Alex Bennée

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

* Re: [Qemu-devel] [RFC 20/48] *-user: notify plugin of exit
  2018-10-25 17:20 ` [Qemu-devel] [RFC 20/48] *-user: notify plugin of exit Emilio G. Cota
@ 2018-11-23 17:01   ` Alex Bennée
  0 siblings, 0 replies; 132+ messages in thread
From: Alex Bennée @ 2018-11-23 17:01 UTC (permalink / raw)
  To: Emilio G. Cota
  Cc: qemu-devel, Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Stefan Hajnoczi


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

> Signed-off-by: Emilio G. Cota <cota@braap.org>

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

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


--
Alex Bennée

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

* Re: [Qemu-devel] [RFC 21/48] *-user: plugin syscalls
  2018-10-25 17:20 ` [Qemu-devel] [RFC 21/48] *-user: plugin syscalls Emilio G. Cota
@ 2018-11-23 17:04   ` Alex Bennée
  2018-11-24  0:03     ` Emilio G. Cota
  0 siblings, 1 reply; 132+ messages in thread
From: Alex Bennée @ 2018-11-23 17:04 UTC (permalink / raw)
  To: Emilio G. Cota
  Cc: qemu-devel, Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Stefan Hajnoczi


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

> Signed-off-by: Emilio G. Cota <cota@braap.org>
> ---
>  bsd-user/syscall.c   | 9 +++++++++
>  linux-user/syscall.c | 3 +++
>  2 files changed, 12 insertions(+)
>
> diff --git a/bsd-user/syscall.c b/bsd-user/syscall.c
> index b7818af450..4993f81b2b 100644
> --- a/bsd-user/syscall.c
> +++ b/bsd-user/syscall.c
> @@ -323,6 +323,8 @@ abi_long do_freebsd_syscall(void *cpu_env, int num, abi_long arg1,
>      gemu_log("freebsd syscall %d\n", num);
>  #endif
>      trace_guest_user_syscall(cpu, num, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8);
> +    qemu_plugin_vcpu_syscall(cpu, num, arg1, arg2, arg3, arg4, arg5, arg6, arg7,
> +                             arg8);

I think we discussed this on my series about avoiding this sort of
duplication by providing a wrapper for trace points that are also plugin
hooks. So something like:

       trace_and_plugin_guest_user_syscall(...)

Although it's probably worth keeping the trace names grep-able so maybe:

       plug_trace_guest_user_syscall(...)

?
>      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 ae3c0dfef7..a6d17a9f37 100644
> --- a/linux-user/syscall.c
> +++ b/linux-user/syscall.c
> @@ -11232,6 +11232,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);
> @@ -11244,5 +11246,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;
>  }


--
Alex Bennée

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

* Re: [Qemu-devel] [RFC 22/48] cpu: hook plugin vcpu events
  2018-10-25 17:20 ` [Qemu-devel] [RFC 22/48] cpu: hook plugin vcpu events Emilio G. Cota
@ 2018-11-23 17:10   ` Alex Bennée
  2018-11-23 23:48     ` Emilio G. Cota
  0 siblings, 1 reply; 132+ messages in thread
From: Alex Bennée @ 2018-11-23 17:10 UTC (permalink / raw)
  To: Emilio G. Cota
  Cc: qemu-devel, Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Stefan Hajnoczi


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

> 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 28e39f045a..3efe89354d 100644
> --- a/cpus.c
> +++ b/cpus.c
> @@ -43,6 +43,7 @@
>  #include "exec/exec-all.h"
>
>  #include "qemu/thread.h"
> +#include "qemu/plugin.h"
>  #include "sysemu/cpus.h"
>  #include "sysemu/qtest.h"
>  #include "qemu/main-loop.h"
> @@ -1322,12 +1323,21 @@ static void qemu_tcg_rr_wait_io_event(CPUState *cpu)
>
>  static void qemu_wait_io_event(CPUState *cpu)
>  {
> +    bool asleep = false;
> +

slept?

>      g_assert(cpu_mutex_locked(cpu));
>      g_assert(!qemu_mutex_iothread_locked());
>
>      while (cpu_thread_is_idle(cpu)) {
> +        if (!asleep) {
> +            asleep = true;
> +            qemu_plugin_vcpu_idle_cb(cpu);
> +        }
>          qemu_cond_wait(&cpu->halt_cond, &cpu->lock);
>      }
> +    if (asleep) {
> +        qemu_plugin_vcpu_resume_cb(cpu);
> +    }

I wonder if having two hooks is too much? What might a plugin want to do
before we go into idle sleep?

It feels like we are exposing too much of the guts of TCG to the plugin
here as wait_io could be for any number of internal reasons other than
the actual emulation blocking for IO through a WFI or something. If a
plugin really wants to track such things shouldn't it be hooking to the
guest sleep points?

If idle sleeps really are that important maybe we could just report our
sleep time on resume - so a single hook but passing a bit more
information?

>
>  #ifdef _WIN32
>      /* Eat dummy APC queued by qemu_cpu_kick_thread.  */
> diff --git a/exec.c b/exec.c
> index cd171adb93..71fc76f55e 100644
> --- a/exec.c
> +++ b/exec.c
> @@ -967,6 +967,8 @@ void cpu_exec_realizefn(CPUState *cpu, Error **errp)
>      }
>      tlb_init(cpu);
>
> +    qemu_plugin_vcpu_init_hook(cpu);
> +
>  #ifndef CONFIG_USER_ONLY
>      if (qdev_get_vmsd(DEVICE(cpu)) == NULL) {
>          vmstate_register(NULL, cpu->cpu_index, &vmstate_cpu_common, cpu);
> diff --git a/qom/cpu.c b/qom/cpu.c
> index d1e6ecae03..062817c03b 100644
> --- a/qom/cpu.c
> +++ b/qom/cpu.c
> @@ -32,6 +32,7 @@
>  #include "hw/boards.h"
>  #include "hw/qdev-properties.h"
>  #include "trace-root.h"
> +#include "qemu/plugin.h"
>
>  CPUInterruptHandler cpu_interrupt_handler;
>
> @@ -353,6 +354,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);
>  }


--
Alex Bennée

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

* Re: [Qemu-devel] [RFC 13/48] xxhash: add qemu_xxhash8
  2018-11-22 17:15   ` Alex Bennée
@ 2018-11-23 22:34     ` Emilio G. Cota
  0 siblings, 0 replies; 132+ messages in thread
From: Emilio G. Cota @ 2018-11-23 22:34 UTC (permalink / raw)
  To: Alex Bennée
  Cc: qemu-devel, Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Stefan Hajnoczi

On Thu, Nov 22, 2018 at 17:15:20 +0000, Alex Bennée wrote:
> 
> Emilio G. Cota <cota@braap.org> writes:
> 
> > It will be used for TB hashing soon.
> >
> > Signed-off-by: Emilio G. Cota <cota@braap.org>
> > ---
> >  include/qemu/xxhash.h | 40 +++++++++++++++++++++++++++-------------
> >  1 file changed, 27 insertions(+), 13 deletions(-)
> >
> > diff --git a/include/qemu/xxhash.h b/include/qemu/xxhash.h
> > index fe35dde328..450427eeaa 100644
> > --- a/include/qemu/xxhash.h
> > +++ b/include/qemu/xxhash.h
> > @@ -49,7 +49,8 @@
> >   * contiguous in memory.
> >   */
> >  static inline uint32_t
> > -qemu_xxhash7(uint64_t ab, uint64_t cd, uint32_t e, uint32_t f, uint32_t g)
> > +qemu_xxhash8(uint64_t ab, uint64_t cd, uint32_t e, uint32_t f, uint32_t g,
> > +             uint32_t h)
> 
> As we've expanded to bigger and bigger hashes why are everything after
> cd passed as 32 bit values? Isn't this just generating extra register
> pressure or is the compiler smart enough to figure it out?

The latter -- the compiler does a good job with constant propagation.

> >  {
> >      uint32_t v1 = QEMU_XXHASH_SEED + PRIME32_1 + PRIME32_2;
> >      uint32_t v2 = QEMU_XXHASH_SEED + PRIME32_2;
> > @@ -77,17 +78,24 @@ qemu_xxhash7(uint64_t ab, uint64_t cd, uint32_t e, uint32_t f, uint32_t g)
> >      v4 = rol32(v4, 13);
> >      v4 *= PRIME32_1;
> >
> > -    h32 = rol32(v1, 1) + rol32(v2, 7) + rol32(v3, 12) + rol32(v4, 18);
> > -    h32 += 28;
> > +    v1 += e * PRIME32_2;
> > +    v1 = rol32(v1, 13);
> > +    v1 *= PRIME32_1;
(snip)
> How do we validate we haven't broken the distribution of the original
> xxhash as we've extended it?

We weren't testing for that, so this is a valid concern.

I just added a test to my xxhash repo to compare our version against
the original:
  https://github.com/cota/xxhash/tree/qemu

Turns out that to exactly match the original we just have to swap our
a/b and c/d pairs. I don't see how this could cause a loss of randomness,
but just to be safe I'll send a for-4.0 patch so that our output matches
the original one.

Thanks,

		Emilio

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

* Re: [Qemu-devel] [RFC 19/48] translate-all: notify plugin code of tb_flush
  2018-11-23 17:00   ` Alex Bennée
@ 2018-11-23 23:15     ` Emilio G. Cota
  2018-11-26 11:02       ` Alex Bennée
  0 siblings, 1 reply; 132+ messages in thread
From: Emilio G. Cota @ 2018-11-23 23:15 UTC (permalink / raw)
  To: Alex Bennée
  Cc: qemu-devel, Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Stefan Hajnoczi

On Fri, Nov 23, 2018 at 17:00:59 +0000, Alex Bennée wrote:
> 
> Emilio G. Cota <cota@braap.org> writes:
> 
> > 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 3423cf74db..1690e3fd5b 100644
> > --- a/accel/tcg/translate-all.c
> > +++ b/accel/tcg/translate-all.c
> > @@ -1233,6 +1233,8 @@ static gboolean tb_host_size_iter(gpointer key, gpointer value, gpointer data)
> >  /* flush all the translation blocks */
> >  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.
> > @@ -1240,6 +1242,7 @@ 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();
> > @@ -1265,6 +1268,9 @@ void do_tb_flush(CPUState *cpu, run_on_cpu_data tb_flush_count)
> >
> >  done:
> >      mmap_unlock();
> > +    if (did_flush) {
> > +        qemu_plugin_flush_cb();
> > +    }
> 
> Are we introducing a race here?

A race, how? We're in an async safe environment here, i.e. no other
vCPU is running.

> What is the purpose of letting the plugin know a flush has occurred?

Plugins might allocate per-TB data that then they get passed each
time the TB is executed (via the *userdata pointer). For example,
in a simulator we'd allocate a per-TB struct that describes the
guest instructions, after having disassembled them at translate time.

It is therefore useful for plugins to know when all TB's have been
flushed, so that they can then free that per-TB data.

> It shouldn't have any knowledge of the details of liveliness of the
> translated code and if it still exits or not. If all it wants to do is
> look at the counts then I think we can provide a simpler less abuse-able
> way to do this.

I'm confused. What does "look at the counts" mean here?

To reiterate, plugins should have a way to know when a TB doesn't
exist any longer, so that they can reclaim memory.

Thanks,

		Emilio

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

* Re: [Qemu-devel] [RFC 10/48] exec: export do_tb_flush
  2018-11-22 17:09   ` Alex Bennée
@ 2018-11-23 23:19     ` Emilio G. Cota
  2018-11-26 11:11       ` Alex Bennée
  0 siblings, 1 reply; 132+ messages in thread
From: Emilio G. Cota @ 2018-11-23 23:19 UTC (permalink / raw)
  To: Alex Bennée
  Cc: qemu-devel, Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Stefan Hajnoczi

On Thu, Nov 22, 2018 at 17:09:22 +0000, Alex Bennée wrote:
> 
> Emilio G. Cota <cota@braap.org> writes:
> 
> > This will be used by plugin code to flush the code cache as well
> > as doing other bookkeeping in a safe work environment.
> 
> This seems a little excessive given the plugin code could just call
> tb_flush() directly. Wouldn't calling tb_flush after scheduling the
> plugin_destroy be enough?
> 
> If there is a race condition here maybe we could build some sort of
> awareness into tb_flush as to the current run state. But having two
> entry points to this rather fundamental action seems likely to either be
> misused or misunderstood.

We have to make sure that no callback left in the generated code is
called once a plugin has been uninstalled. To me, using the same safe
work window to both flush the TB and uninstall the plugin seems the
simplest way to do this.

Thanks,

		Emilio

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

* Re: [Qemu-devel] [RFC 22/48] cpu: hook plugin vcpu events
  2018-11-23 17:10   ` Alex Bennée
@ 2018-11-23 23:48     ` Emilio G. Cota
  2018-11-26 11:17       ` Alex Bennée
  0 siblings, 1 reply; 132+ messages in thread
From: Emilio G. Cota @ 2018-11-23 23:48 UTC (permalink / raw)
  To: Alex Bennée
  Cc: qemu-devel, Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Stefan Hajnoczi

On Fri, Nov 23, 2018 at 17:10:53 +0000, Alex Bennée wrote:
> Emilio G. Cota <cota@braap.org> writes:
(snip)
> > @@ -1322,12 +1323,21 @@ static void qemu_tcg_rr_wait_io_event(CPUState *cpu)
> >
> >  static void qemu_wait_io_event(CPUState *cpu)
> >  {
> > +    bool asleep = false;
> > +
> 
> slept?

Changed to slept, thanks.

> >      g_assert(cpu_mutex_locked(cpu));
> >      g_assert(!qemu_mutex_iothread_locked());
> >
> >      while (cpu_thread_is_idle(cpu)) {
> > +        if (!asleep) {
> > +            asleep = true;
> > +            qemu_plugin_vcpu_idle_cb(cpu);
> > +        }
> >          qemu_cond_wait(&cpu->halt_cond, &cpu->lock);
> >      }
> > +    if (asleep) {
> > +        qemu_plugin_vcpu_resume_cb(cpu);
> > +    }
> 
> I wonder if having two hooks is too much? What might a plugin want to do
> before we go into idle sleep?
> 
> It feels like we are exposing too much of the guts of TCG to the plugin
> here as wait_io could be for any number of internal reasons other than
> the actual emulation blocking for IO through a WFI or something. If a
> plugin really wants to track such things shouldn't it be hooking to the
> guest sleep points?
> 
> If idle sleeps really are that important maybe we could just report our
> sleep time on resume - so a single hook but passing a bit more
> information?

I don't have all the use cases for this figured out. For now I'm using
this in plugins as a way to know when a vCPU is for sure idle, which
is used in memory reclamation schemes such as RCU.

What are the "guest sleep points" you mention? Are they target-agnostic?

Thanks,

		Emilio

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

* Re: [Qemu-devel] [RFC 16/48] tcg: add plugin_mask to TB hash
  2018-11-23 16:52   ` Alex Bennée
@ 2018-11-24  0:02     ` Emilio G. Cota
  0 siblings, 0 replies; 132+ messages in thread
From: Emilio G. Cota @ 2018-11-24  0:02 UTC (permalink / raw)
  To: Alex Bennée
  Cc: qemu-devel, Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Stefan Hajnoczi

On Fri, Nov 23, 2018 at 16:52:31 +0000, Alex Bennée wrote:
> 
> Emilio G. Cota <cota@braap.org> writes:
> 
> > Signed-off-by: Emilio G. Cota <cota@braap.org>
> 
> This commit message needs more description. What is this plugin mask
> for? Are we extending the TB flags with a mask of loaded plugins so we
> can correctly identify what code was generated under what plugins?

This is similar to the bitmap for trace events, which we added to the
TB hash so that when the TCG events have no subscribers left, we just
regenerate the TBs.

It's true though that in this RFC there are no good use cases for this,
because the callbacks that affect performance are all unconditionally
embedded in the generated code. So at least for now we could drop
this patch.

Thanks,

		Emilio

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

* Re: [Qemu-devel] [RFC 21/48] *-user: plugin syscalls
  2018-11-23 17:04   ` Alex Bennée
@ 2018-11-24  0:03     ` Emilio G. Cota
  0 siblings, 0 replies; 132+ messages in thread
From: Emilio G. Cota @ 2018-11-24  0:03 UTC (permalink / raw)
  To: Alex Bennée
  Cc: qemu-devel, Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Stefan Hajnoczi

On Fri, Nov 23, 2018 at 17:04:28 +0000, Alex Bennée wrote:
> 
> Emilio G. Cota <cota@braap.org> writes:
> 
> > Signed-off-by: Emilio G. Cota <cota@braap.org>
> > ---
> >  bsd-user/syscall.c   | 9 +++++++++
> >  linux-user/syscall.c | 3 +++
> >  2 files changed, 12 insertions(+)
> >
> > diff --git a/bsd-user/syscall.c b/bsd-user/syscall.c
> > index b7818af450..4993f81b2b 100644
> > --- a/bsd-user/syscall.c
> > +++ b/bsd-user/syscall.c
> > @@ -323,6 +323,8 @@ abi_long do_freebsd_syscall(void *cpu_env, int num, abi_long arg1,
> >      gemu_log("freebsd syscall %d\n", num);
> >  #endif
> >      trace_guest_user_syscall(cpu, num, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8);
> > +    qemu_plugin_vcpu_syscall(cpu, num, arg1, arg2, arg3, arg4, arg5, arg6, arg7,
> > +                             arg8);
> 
> I think we discussed this on my series about avoiding this sort of
> duplication by providing a wrapper for trace points that are also plugin
> hooks. So something like:
> 
>        trace_and_plugin_guest_user_syscall(...)
> 
> Although it's probably worth keeping the trace names grep-able so maybe:
> 
>        plug_trace_guest_user_syscall(...)
> 
> ?

Yes, I remember that discussion and agree that a common function here
would be best.

		Emilio

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

* Re: [Qemu-devel] [RFC 12/48] atomic_template: define pre/post macros
  2018-11-22 17:12   ` Alex Bennée
@ 2018-11-24  0:09     ` Emilio G. Cota
  2018-11-26 11:21       ` Alex Bennée
  0 siblings, 1 reply; 132+ messages in thread
From: Emilio G. Cota @ 2018-11-24  0:09 UTC (permalink / raw)
  To: Alex Bennée
  Cc: qemu-devel, Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Stefan Hajnoczi

On Thu, Nov 22, 2018 at 17:12:34 +0000, Alex Bennée wrote:
> 
> Emilio G. Cota <cota@braap.org> writes:
> 
> > In preparation for plugin support.
> >
> > Signed-off-by: Emilio G. Cota <cota@braap.org>
> 
> More macros for the macro-god. I guess this works but I wonder if it's
> possible to do a clean-up ala softfloat and the experimental softmmu
> re-factor that makes this less a mess of macros?

Heh, point taken.

I'll try to fix this once (and if!) the series graduates from RFC
to proper patchset. BTW I'm planning to pick rth's softfloat
cleanup soon, so that should help.

Thanks,

		Emilio

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

* Re: [Qemu-devel] [RFC 19/48] translate-all: notify plugin code of tb_flush
  2018-11-23 23:15     ` Emilio G. Cota
@ 2018-11-26 11:02       ` Alex Bennée
  2018-11-26 21:53         ` Emilio G. Cota
  0 siblings, 1 reply; 132+ messages in thread
From: Alex Bennée @ 2018-11-26 11:02 UTC (permalink / raw)
  To: Emilio G. Cota
  Cc: qemu-devel, Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Stefan Hajnoczi


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

> On Fri, Nov 23, 2018 at 17:00:59 +0000, Alex Bennée wrote:
>>
>> Emilio G. Cota <cota@braap.org> writes:
>>
>> > 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 3423cf74db..1690e3fd5b 100644
>> > --- a/accel/tcg/translate-all.c
>> > +++ b/accel/tcg/translate-all.c
>> > @@ -1233,6 +1233,8 @@ static gboolean tb_host_size_iter(gpointer key, gpointer value, gpointer data)
>> >  /* flush all the translation blocks */
>> >  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.
>> > @@ -1240,6 +1242,7 @@ 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();
>> > @@ -1265,6 +1268,9 @@ void do_tb_flush(CPUState *cpu, run_on_cpu_data tb_flush_count)
>> >
>> >  done:
>> >      mmap_unlock();
>> > +    if (did_flush) {
>> > +        qemu_plugin_flush_cb();
>> > +    }
>>
>> Are we introducing a race here?
>
> A race, how? We're in an async safe environment here, i.e. no other
> vCPU is running.

You are right, I was thrown by the mmap_lock/unlocks - but I guess even
they aren't needed now if tb flush is always in an exclusive context.

>
>> What is the purpose of letting the plugin know a flush has occurred?
>
> Plugins might allocate per-TB data that then they get passed each
> time the TB is executed (via the *userdata pointer). For example,
> in a simulator we'd allocate a per-TB struct that describes the
> guest instructions, after having disassembled them at translate time.
>
> It is therefore useful for plugins to know when all TB's have been
> flushed, so that they can then free that per-TB data.

Would they need a generation count propagated here or would it just be
flush anything translation related at this point?

>> It shouldn't have any knowledge of the details of liveliness of the
>> translated code and if it still exits or not. If all it wants to do is
>> look at the counts then I think we can provide a simpler less abuse-able
>> way to do this.
>
> I'm confused. What does "look at the counts" mean here?
>
> To reiterate, plugins should have a way to know when a TB doesn't
> exist any longer, so that they can reclaim memory.

OK that makes sense. Could you expand the commit message just to explain
the use case?

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

--
Alex Bennée

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

* Re: [Qemu-devel] [RFC 10/48] exec: export do_tb_flush
  2018-11-23 23:19     ` Emilio G. Cota
@ 2018-11-26 11:11       ` Alex Bennée
  2018-11-26 23:56         ` Emilio G. Cota
  0 siblings, 1 reply; 132+ messages in thread
From: Alex Bennée @ 2018-11-26 11:11 UTC (permalink / raw)
  To: Emilio G. Cota
  Cc: qemu-devel, Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Stefan Hajnoczi


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

> On Thu, Nov 22, 2018 at 17:09:22 +0000, Alex Bennée wrote:
>>
>> Emilio G. Cota <cota@braap.org> writes:
>>
>> > This will be used by plugin code to flush the code cache as well
>> > as doing other bookkeeping in a safe work environment.
>>
>> This seems a little excessive given the plugin code could just call
>> tb_flush() directly. Wouldn't calling tb_flush after scheduling the
>> plugin_destroy be enough?
>>
>> If there is a race condition here maybe we could build some sort of
>> awareness into tb_flush as to the current run state. But having two
>> entry points to this rather fundamental action seems likely to either be
>> misused or misunderstood.
>
> We have to make sure that no callback left in the generated code is
> called once a plugin has been uninstalled. To me, using the same safe
> work window to both flush the TB and uninstall the plugin seems the
> simplest way to do this.

I still think making tb_flush() aware that it can run in an exclusive
period would be a better solution than exposing two functions for the
operation. So tb_flush could be something like:

  void tb_flush(CPUState *cpu)
  {
      if (tcg_enabled()) {
          unsigned tb_flush_count = atomic_mb_read(&tb_ctx.tb_flush_count);
          if (cpu_current_and_exclusive(cpu)) {
              do_tb_flush(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));
          }
      }
  }

Or possibly push that logic down into async_safe_run_on_cpu()?

--
Alex Bennée

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

* Re: [Qemu-devel] [RFC 22/48] cpu: hook plugin vcpu events
  2018-11-23 23:48     ` Emilio G. Cota
@ 2018-11-26 11:17       ` Alex Bennée
  2018-11-27  1:25         ` Emilio G. Cota
  0 siblings, 1 reply; 132+ messages in thread
From: Alex Bennée @ 2018-11-26 11:17 UTC (permalink / raw)
  To: Emilio G. Cota
  Cc: qemu-devel, Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Stefan Hajnoczi


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

> On Fri, Nov 23, 2018 at 17:10:53 +0000, Alex Bennée wrote:
>> Emilio G. Cota <cota@braap.org> writes:
> (snip)
>> > @@ -1322,12 +1323,21 @@ static void qemu_tcg_rr_wait_io_event(CPUState *cpu)
>> >
>> >  static void qemu_wait_io_event(CPUState *cpu)
>> >  {
>> > +    bool asleep = false;
>> > +
>>
>> slept?
>
> Changed to slept, thanks.
>
>> >      g_assert(cpu_mutex_locked(cpu));
>> >      g_assert(!qemu_mutex_iothread_locked());
>> >
>> >      while (cpu_thread_is_idle(cpu)) {
>> > +        if (!asleep) {
>> > +            asleep = true;
>> > +            qemu_plugin_vcpu_idle_cb(cpu);
>> > +        }
>> >          qemu_cond_wait(&cpu->halt_cond, &cpu->lock);
>> >      }
>> > +    if (asleep) {
>> > +        qemu_plugin_vcpu_resume_cb(cpu);
>> > +    }
>>
>> I wonder if having two hooks is too much? What might a plugin want to do
>> before we go into idle sleep?
>>
>> It feels like we are exposing too much of the guts of TCG to the plugin
>> here as wait_io could be for any number of internal reasons other than
>> the actual emulation blocking for IO through a WFI or something. If a
>> plugin really wants to track such things shouldn't it be hooking to the
>> guest sleep points?
>>
>> If idle sleeps really are that important maybe we could just report our
>> sleep time on resume - so a single hook but passing a bit more
>> information?
>
> I don't have all the use cases for this figured out. For now I'm using
> this in plugins as a way to know when a vCPU is for sure idle, which
> is used in memory reclamation schemes such as RCU.

Couldn't we achieve the same by scheduling async or safe async work on
the vCPU? Maybe we would expose this to the plugin as a "run callback
when idle" function.

> What are the "guest sleep points" you mention? Are they
> target-agnostic?

I mean we can arrive here for a variety of reasons - not all of which
are triggered by the guest going to sleep. But that isn't your current
use case.

They aren't target agnostic as not all guests try to fully model their
"sleeping" instructions. Some just end up busy spinning until the event
they should have been sleeping until happens.

>
> Thanks,
>
> 		Emilio


--
Alex Bennée

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

* Re: [Qemu-devel] [RFC 12/48] atomic_template: define pre/post macros
  2018-11-24  0:09     ` Emilio G. Cota
@ 2018-11-26 11:21       ` Alex Bennée
  2018-11-27  1:16         ` Emilio G. Cota
  0 siblings, 1 reply; 132+ messages in thread
From: Alex Bennée @ 2018-11-26 11:21 UTC (permalink / raw)
  To: Emilio G. Cota
  Cc: qemu-devel, Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Stefan Hajnoczi


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

> On Thu, Nov 22, 2018 at 17:12:34 +0000, Alex Bennée wrote:
>>
>> Emilio G. Cota <cota@braap.org> writes:
>>
>> > In preparation for plugin support.
>> >
>> > Signed-off-by: Emilio G. Cota <cota@braap.org>
>>
>> More macros for the macro-god. I guess this works but I wonder if it's
>> possible to do a clean-up ala softfloat and the experimental softmmu
>> re-factor that makes this less a mess of macros?
>
> Heh, point taken.
>
> I'll try to fix this once (and if!) the series graduates from RFC
> to proper patchset. BTW I'm planning to pick rth's softfloat
> cleanup soon, so that should help.

Yeah I was in the middle of re-spinning the softmmu demacro patches but
ran into a bug I didn't get time to track down. I'll get it re-spun once
I've finished going through this.

I assume the rth's softfloat cleanup is for hardfloat support? I stand
ready to review the next version. We should get it in well before the
4.0 soft freeze (next release).

>
> Thanks,
>
> 		Emilio


--
Alex Bennée

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

* Re: [Qemu-devel] [RFC 23/48] translator: add plugin_insn argument to translate_insn
  2018-10-25 17:20 ` [Qemu-devel] [RFC 23/48] translator: add plugin_insn argument to translate_insn Emilio G. Cota
@ 2018-11-26 14:52   ` Alex Bennée
  2018-11-26 18:27     ` Richard Henderson
  0 siblings, 1 reply; 132+ messages in thread
From: Alex Bennée @ 2018-11-26 14:52 UTC (permalink / raw)
  To: Emilio G. Cota
  Cc: qemu-devel, Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Stefan Hajnoczi


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

> Signed-off-by: Emilio G. Cota <cota@braap.org>
> ---
>  include/exec/translator.h   | 4 +++-
>  accel/tcg/translator.c      | 4 ++--
>  target/alpha/translate.c    | 3 ++-
>  target/arm/translate-a64.c  | 3 ++-
>  target/arm/translate.c      | 6 ++++--
>  target/hppa/translate.c     | 3 ++-
>  target/i386/translate.c     | 3 ++-
>  target/m68k/translate.c     | 3 ++-
>  target/mips/translate.c     | 3 ++-
>  target/openrisc/translate.c | 3 ++-
>  target/ppc/translate.c      | 3 ++-
>  target/riscv/translate.c    | 3 ++-
>  target/s390x/translate.c    | 3 ++-
>  target/sh4/translate.c      | 3 ++-
>  target/sparc/translate.c    | 3 ++-
>  target/xtensa/translate.c   | 3 ++-
>  16 files changed, 35 insertions(+), 18 deletions(-)
>
> diff --git a/include/exec/translator.h b/include/exec/translator.h
> index 71e7b2c347..a28147b3dd 100644
> --- a/include/exec/translator.h
> +++ b/include/exec/translator.h
> @@ -20,6 +20,7 @@
>
>
>  #include "exec/exec-all.h"
> +#include "qemu/plugin.h"
>  #include "tcg/tcg.h"
>
>
> @@ -112,7 +113,8 @@ typedef struct TranslatorOps {
>      void (*insn_start)(DisasContextBase *db, CPUState *cpu);
>      bool (*breakpoint_check)(DisasContextBase *db, CPUState *cpu,
>                               const CPUBreakpoint *bp);
> -    void (*translate_insn)(DisasContextBase *db, CPUState *cpu);
> +    void (*translate_insn)(DisasContextBase *db, CPUState *cpu,
> +                           struct qemu_plugin_insn *plugin_insn);

I'm not convinced this is the best way to go about it. We end up having
to sprinkle the plugin calls into each decoder rather than keeping all
the infrastructure in the common main loop. However the common loop will
need to know the total number of bytes decoded so we could change the
declaration to:

  int (*translate_insn)(DisasContextBase *db, CPUState *cpu);

and return the number of bytes decoded. It would mean a minor
inefficiency in having to re-read the instruction bytes into a buffer in
preparation for passing to the plugin but it would all at least be in
one place.

--
Alex Bennée

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

* Re: [Qemu-devel] [RFC 38/48] translator: implement 2-pass translation
  2018-10-25 17:20 ` [Qemu-devel] [RFC 38/48] translator: implement " Emilio G. Cota
@ 2018-11-26 15:16   ` Alex Bennée
  2018-11-27  2:16     ` Emilio G. Cota
  0 siblings, 1 reply; 132+ messages in thread
From: Alex Bennée @ 2018-11-26 15:16 UTC (permalink / raw)
  To: Emilio G. Cota
  Cc: qemu-devel, Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Stefan Hajnoczi, Richard Henderson


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

> The second pass only occurs when a plugin has subscribed to
> TB translation events.
>
> Signed-off-by: Emilio G. Cota <cota@braap.org>
> ---
>  tcg/tcg.h              |  8 ++++
>  accel/tcg/translator.c | 91 +++++++++++++++++++++++++++++++++++++++++-
>  2 files changed, 97 insertions(+), 2 deletions(-)
>
> diff --git a/tcg/tcg.h b/tcg/tcg.h
> index d5afe25c97..479b57d65f 100644
> --- a/tcg/tcg.h
> +++ b/tcg/tcg.h
> @@ -720,6 +720,14 @@ struct TCGContext {
>
>      TCGLabel *exitreq_label;
>
<snip>
>      }
>
> +    if (tb_trans_cb && first_pass) {
> +        qemu_plugin_tb_trans_cb(cpu, plugin_tb);
> +        first_pass = false;
> +        goto translate;
> +    }
> +

So the only reason we are doing this two pass tango is to ensure the
plugin can insert TCG ops before the actual translation has occurred?

I think we can do better, especially as the internal structures of
TCGops are implemented as a list so ops and be inserted before and after
other ops. This is currently only done by the optimiser at the moment,
see:

  TCGOp *tcg_op_insert_before(TCGContext *s, TCGOp *op, TCGOpcode opc, int narg);
  TCGOp *tcg_op_insert_after(TCGContext *s, TCGOp *op, TCGOpcode opc, int narg);

and all the base tcg ops end up going to tcg_emit_op which just appends
to the tail. But if we can come up with a neater way to track the op
used before the current translated expression we could do away with two
phases translation completely.

>      /* Emit code to exit the TB, as indicated by db->is_jmp.  */
>      ops->tb_stop(db, cpu);
>      gen_tb_end(db->tb, db->num_insns - bp_insn);


--
Alex Bennée

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

* Re: [Qemu-devel] [RFC 23/48] translator: add plugin_insn argument to translate_insn
  2018-11-26 14:52   ` Alex Bennée
@ 2018-11-26 18:27     ` Richard Henderson
  2018-11-26 18:56       ` Alex Bennée
  2018-11-26 19:07       ` Emilio G. Cota
  0 siblings, 2 replies; 132+ messages in thread
From: Richard Henderson @ 2018-11-26 18:27 UTC (permalink / raw)
  To: Alex Bennée, Emilio G. Cota
  Cc: Stefan Hajnoczi, Peter Maydell, qemu-devel, Pavel Dovgalyuk,
	Lluís Vilanova

On 11/26/18 6:52 AM, Alex Bennée wrote:
> I'm not convinced this is the best way to go about it. We end up having
> to sprinkle the plugin calls into each decoder rather than keeping all
> the infrastructure in the common main loop. However the common loop will
> need to know the total number of bytes decoded so we could change the
> declaration to:
> 
>   int (*translate_insn)(DisasContextBase *db, CPUState *cpu);
> 
> and return the number of bytes decoded. 

Returning the number of bytes is more difficult than simply just

    old_pc = db->pc_next;
    opc->translate_insn(db, cpu);
    bytes = db->pc_next - old_pc;

requiring no target changes at all.


r~

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

* Re: [Qemu-devel] [RFC 23/48] translator: add plugin_insn argument to translate_insn
  2018-11-26 18:27     ` Richard Henderson
@ 2018-11-26 18:56       ` Alex Bennée
  2018-11-26 19:19         ` Richard Henderson
  2018-11-26 19:07       ` Emilio G. Cota
  1 sibling, 1 reply; 132+ messages in thread
From: Alex Bennée @ 2018-11-26 18:56 UTC (permalink / raw)
  To: Richard Henderson
  Cc: Emilio G. Cota, Stefan Hajnoczi, Peter Maydell, qemu-devel,
	Pavel Dovgalyuk, Lluís Vilanova

On Mon, 26 Nov 2018, 18:27 Richard Henderson <richard.henderson@linaro.org
wrote:

> On 11/26/18 6:52 AM, Alex Bennée wrote:
> > I'm not convinced this is the best way to go about it. We end up having
> > to sprinkle the plugin calls into each decoder rather than keeping all
> > the infrastructure in the common main loop. However the common loop will
> > need to know the total number of bytes decoded so we could change the
> > declaration to:
> >
> >   int (*translate_insn)(DisasContextBase *db, CPUState *cpu);
> >
> > and return the number of bytes decoded.
>
> Returning the number of bytes is more difficult than simply just
>
>     old_pc = db->pc_next;
>     opc->translate_insn(db, cpu);
>     bytes = db->pc_next - old_pc;
>
> requiring no target changes at all.
>

If that's always true then great, but what happens with direct branches?

>
> r~
>

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

* Re: [Qemu-devel] [RFC 23/48] translator: add plugin_insn argument to translate_insn
  2018-11-26 18:27     ` Richard Henderson
  2018-11-26 18:56       ` Alex Bennée
@ 2018-11-26 19:07       ` Emilio G. Cota
  2018-11-26 19:30         ` Richard Henderson
  2018-11-27 13:08         ` Pavel Dovgalyuk
  1 sibling, 2 replies; 132+ messages in thread
From: Emilio G. Cota @ 2018-11-26 19:07 UTC (permalink / raw)
  To: Richard Henderson
  Cc: Alex Bennée, Stefan Hajnoczi, Peter Maydell, qemu-devel,
	Pavel Dovgalyuk, Lluís Vilanova

On Mon, Nov 26, 2018 at 10:27:12 -0800, Richard Henderson wrote:
> On 11/26/18 6:52 AM, Alex Bennée wrote:
> > I'm not convinced this is the best way to go about it. We end up having
> > to sprinkle the plugin calls into each decoder rather than keeping all
> > the infrastructure in the common main loop. However the common loop will
> > need to know the total number of bytes decoded so we could change the
> > declaration to:
> > 
> >   int (*translate_insn)(DisasContextBase *db, CPUState *cpu);
> > 
> > and return the number of bytes decoded. 
> 
> Returning the number of bytes is more difficult than simply just
> 
>     old_pc = db->pc_next;
>     opc->translate_insn(db, cpu);
>     bytes = db->pc_next - old_pc;
> 
> requiring no target changes at all.

The main reason why I added the qemu_plugin_insn_append calls
was to avoid reading the instructions twice from guest memory,
because I was worried that doing so might somehow alter the
guest's execution, e.g. what if we read a cross-page instruction,
and both pages mapped to the same TLB entry? We'd end up having
more TLB misses because instrumentation was enabled.

If you think that's not really a concern, we could just re-do
the reads in the translator loop and get the size as above.

Thanks,

		Emilio

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

* Re: [Qemu-devel] [RFC 23/48] translator: add plugin_insn argument to translate_insn
  2018-11-26 18:56       ` Alex Bennée
@ 2018-11-26 19:19         ` Richard Henderson
  0 siblings, 0 replies; 132+ messages in thread
From: Richard Henderson @ 2018-11-26 19:19 UTC (permalink / raw)
  To: Alex Bennée
  Cc: Emilio G. Cota, Stefan Hajnoczi, Peter Maydell, qemu-devel,
	Pavel Dovgalyuk, Lluís Vilanova

On 11/26/18 10:56 AM, Alex Bennée wrote:
> 
> 
> On Mon, 26 Nov 2018, 18:27 Richard Henderson <richard.henderson@linaro.org
> <mailto:richard.henderson@linaro.org> wrote:
> 
>     On 11/26/18 6:52 AM, Alex Bennée wrote:
>     > I'm not convinced this is the best way to go about it. We end up having
>     > to sprinkle the plugin calls into each decoder rather than keeping all
>     > the infrastructure in the common main loop. However the common loop will
>     > need to know the total number of bytes decoded so we could change the
>     > declaration to:
>     >
>     >   int (*translate_insn)(DisasContextBase *db, CPUState *cpu);
>     >
>     > and return the number of bytes decoded.
> 
>     Returning the number of bytes is more difficult than simply just
> 
>         old_pc = db->pc_next;
>         opc->translate_insn(db, cpu);
>         bytes = db->pc_next - old_pc;
> 
>     requiring no target changes at all.
> 
> 
> If that's always true then great, but what happens with direct branches?

pc_next is still updated by the size of the branch, not it's destination;
db->is_jmp will be != DISAS_NEXT, ending the TB.


r~

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

* Re: [Qemu-devel] [RFC 23/48] translator: add plugin_insn argument to translate_insn
  2018-11-26 19:07       ` Emilio G. Cota
@ 2018-11-26 19:30         ` Richard Henderson
  2018-11-27  1:38           ` Emilio G. Cota
  2018-11-27 13:08         ` Pavel Dovgalyuk
  1 sibling, 1 reply; 132+ messages in thread
From: Richard Henderson @ 2018-11-26 19:30 UTC (permalink / raw)
  To: Emilio G. Cota
  Cc: Alex Bennée, Stefan Hajnoczi, Peter Maydell, qemu-devel,
	Pavel Dovgalyuk, Lluís Vilanova

On 11/26/18 11:07 AM, Emilio G. Cota wrote:
> The main reason why I added the qemu_plugin_insn_append calls
> was to avoid reading the instructions twice from guest memory,
> because I was worried that doing so might somehow alter the
> guest's execution, e.g. what if we read a cross-page instruction,
> and both pages mapped to the same TLB entry? We'd end up having
> more TLB misses because instrumentation was enabled.

A better solution for this, I think is to change direct calls from

  cpu_ldl_code(env, pc);
to
  translator_ldl_code(dc_base, env, pc);

instead of passing around a new argument separate from DisasContextBase?


r~

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

* Re: [Qemu-devel] [RFC 19/48] translate-all: notify plugin code of tb_flush
  2018-11-26 11:02       ` Alex Bennée
@ 2018-11-26 21:53         ` Emilio G. Cota
  0 siblings, 0 replies; 132+ messages in thread
From: Emilio G. Cota @ 2018-11-26 21:53 UTC (permalink / raw)
  To: Alex Bennée
  Cc: qemu-devel, Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Stefan Hajnoczi

On Mon, Nov 26, 2018 at 11:02:24 +0000, Alex Bennée wrote:
> Emilio G. Cota <cota@braap.org> writes:
> > On Fri, Nov 23, 2018 at 17:00:59 +0000, Alex Bennée wrote:
> >> What is the purpose of letting the plugin know a flush has occurred?
> >
> > Plugins might allocate per-TB data that then they get passed each
> > time the TB is executed (via the *userdata pointer). For example,
> > in a simulator we'd allocate a per-TB struct that describes the
> > guest instructions, after having disassembled them at translate time.
> >
> > It is therefore useful for plugins to know when all TB's have been
> > flushed, so that they can then free that per-TB data.
> 
> Would they need a generation count propagated here or would it just be
> flush anything translation related at this point?

The callback is guaranteed to be called in an exclusive context, so
plugins can just rely on that; since no code execution can happen at
that time, there's no need to pass further info, since the plugins
can free that data right from the callback.

> >> It shouldn't have any knowledge of the details of liveliness of the
> >> translated code and if it still exits or not. If all it wants to do is
> >> look at the counts then I think we can provide a simpler less abuse-able
> >> way to do this.
> >
> > I'm confused. What does "look at the counts" mean here?
> >
> > To reiterate, plugins should have a way to know when a TB doesn't
> > exist any longer, so that they can reclaim memory.
>
> OK that makes sense. Could you expand the commit message just to explain
> the use case?
>
> Reviewed-by: Alex Bennée <alex.bennee@linaro.org>

Updated the commit message with:

    translate-all: notify plugin code of tb_flush

    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.

Thanks,

                Emilio

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

* Re: [Qemu-devel] [RFC 10/48] exec: export do_tb_flush
  2018-11-26 11:11       ` Alex Bennée
@ 2018-11-26 23:56         ` Emilio G. Cota
  0 siblings, 0 replies; 132+ messages in thread
From: Emilio G. Cota @ 2018-11-26 23:56 UTC (permalink / raw)
  To: Alex Bennée
  Cc: qemu-devel, Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Stefan Hajnoczi

On Mon, Nov 26, 2018 at 11:11:53 +0000, Alex Bennée wrote:
> 
> Emilio G. Cota <cota@braap.org> writes:
> 
> > On Thu, Nov 22, 2018 at 17:09:22 +0000, Alex Bennée wrote:
> >>
> >> Emilio G. Cota <cota@braap.org> writes:
> >>
> >> > This will be used by plugin code to flush the code cache as well
> >> > as doing other bookkeeping in a safe work environment.
> >>
> >> This seems a little excessive given the plugin code could just call
> >> tb_flush() directly. Wouldn't calling tb_flush after scheduling the
> >> plugin_destroy be enough?
> >>
> >> If there is a race condition here maybe we could build some sort of
> >> awareness into tb_flush as to the current run state. But having two
> >> entry points to this rather fundamental action seems likely to either be
> >> misused or misunderstood.
> >
> > We have to make sure that no callback left in the generated code is
> > called once a plugin has been uninstalled. To me, using the same safe
> > work window to both flush the TB and uninstall the plugin seems the
> > simplest way to do this.
> 
> I still think making tb_flush() aware that it can run in an exclusive
> period would be a better solution than exposing two functions for the
> operation. So tb_flush could be something like:
> 
>   void tb_flush(CPUState *cpu)
>   {
>       if (tcg_enabled()) {
>           unsigned tb_flush_count = atomic_mb_read(&tb_ctx.tb_flush_count);
>           if (cpu_current_and_exclusive(cpu)) {
>               do_tb_flush(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));
>           }
>       }
>   }
> 
> Or possibly push that logic down into async_safe_run_on_cpu()?

The latter option would be much harder, because in async_safe_run_on_cpu
we always queue the work and kick the CPU (which could be ourselves).
IOW the job is always asynchronous, as the name implies.

I've thus implemented the former in v2, as follows (I'm using a hole
in struct CPUState to add the bool):

@@ -1277,8 +1277,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));
+        }
     }
 }

+++ b/cpus-common.c
@@ -386,7 +386,9 @@ static void process_queued_cpu_work_locked(CPUState *cpu)
                 qemu_mutex_unlock_iothread();
             }
             start_exclusive();
+            cpu->in_exclusive_work_context = true;
             wi->func(cpu, wi->data);
+            cpu->in_exclusive_work_context = false;
             end_exclusive();

I've also fixed a couple of unrelated bugs when uninstalling a plugin
with memory callbacks enabled.

Thanks,

		Emilio

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

* Re: [Qemu-devel] [RFC 12/48] atomic_template: define pre/post macros
  2018-11-26 11:21       ` Alex Bennée
@ 2018-11-27  1:16         ` Emilio G. Cota
  0 siblings, 0 replies; 132+ messages in thread
From: Emilio G. Cota @ 2018-11-27  1:16 UTC (permalink / raw)
  To: Alex Bennée
  Cc: qemu-devel, Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Stefan Hajnoczi

On Mon, Nov 26, 2018 at 11:21:13 +0000, Alex Bennée wrote:
> 
> Emilio G. Cota <cota@braap.org> writes:
> 
> > On Thu, Nov 22, 2018 at 17:12:34 +0000, Alex Bennée wrote:
> >>
> >> Emilio G. Cota <cota@braap.org> writes:
> >>
> >> > In preparation for plugin support.
> >> >
> >> > Signed-off-by: Emilio G. Cota <cota@braap.org>
> >>
> >> More macros for the macro-god. I guess this works but I wonder if it's
> >> possible to do a clean-up ala softfloat and the experimental softmmu
> >> re-factor that makes this less a mess of macros?
> >
> > Heh, point taken.
> >
> > I'll try to fix this once (and if!) the series graduates from RFC
> > to proper patchset. BTW I'm planning to pick rth's softfloat
> > cleanup soon, so that should help.
> 
> Yeah I was in the middle of re-spinning the softmmu demacro patches but
> ran into a bug I didn't get time to track down. I'll get it re-spun once
> I've finished going through this.

Nice!

> I assume the rth's softfloat cleanup is for hardfloat support? I stand
> ready to review the next version. We should get it in well before the
> 4.0 soft freeze (next release).

Yes I meant hardfloat, not softfloat :P

I submitted v6 over the weekend. You'll be happy to hear that after
applying rth's cleanups, the series only has four macros left,
the largest of which only takes 8 lines :-)

		Emilio

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

* Re: [Qemu-devel] [RFC 22/48] cpu: hook plugin vcpu events
  2018-11-26 11:17       ` Alex Bennée
@ 2018-11-27  1:25         ` Emilio G. Cota
  0 siblings, 0 replies; 132+ messages in thread
From: Emilio G. Cota @ 2018-11-27  1:25 UTC (permalink / raw)
  To: Alex Bennée
  Cc: qemu-devel, Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Stefan Hajnoczi

On Mon, Nov 26, 2018 at 11:17:27 +0000, Alex Bennée wrote:
> 
> Emilio G. Cota <cota@braap.org> writes:
> 
> > On Fri, Nov 23, 2018 at 17:10:53 +0000, Alex Bennée wrote:
> >> Emilio G. Cota <cota@braap.org> writes:
> > (snip)
> >> > @@ -1322,12 +1323,21 @@ static void qemu_tcg_rr_wait_io_event(CPUState *cpu)
> >> >
> >> >  static void qemu_wait_io_event(CPUState *cpu)
> >> >  {
> >> > +    bool asleep = false;
> >> > +
> >>
> >> slept?
> >
> > Changed to slept, thanks.
> >
> >> >      g_assert(cpu_mutex_locked(cpu));
> >> >      g_assert(!qemu_mutex_iothread_locked());
> >> >
> >> >      while (cpu_thread_is_idle(cpu)) {
> >> > +        if (!asleep) {
> >> > +            asleep = true;
> >> > +            qemu_plugin_vcpu_idle_cb(cpu);
> >> > +        }
> >> >          qemu_cond_wait(&cpu->halt_cond, &cpu->lock);
> >> >      }
> >> > +    if (asleep) {
> >> > +        qemu_plugin_vcpu_resume_cb(cpu);
> >> > +    }
> >>
> >> I wonder if having two hooks is too much? What might a plugin want to do
> >> before we go into idle sleep?
> >>
> >> It feels like we are exposing too much of the guts of TCG to the plugin
> >> here as wait_io could be for any number of internal reasons other than
> >> the actual emulation blocking for IO through a WFI or something. If a
> >> plugin really wants to track such things shouldn't it be hooking to the
> >> guest sleep points?
> >>
> >> If idle sleeps really are that important maybe we could just report our
> >> sleep time on resume - so a single hook but passing a bit more
> >> information?
> >
> > I don't have all the use cases for this figured out. For now I'm using
> > this in plugins as a way to know when a vCPU is for sure idle, which
> > is used in memory reclamation schemes such as RCU.
> 
> Couldn't we achieve the same by scheduling async or safe async work on
> the vCPU? Maybe we would expose this to the plugin as a "run callback
> when idle" function.

I'm not sure I understand the first question. Do you mean to schedule
regular work just to make the CPU idle every now and then?
Letting the CPU idle whenever it wants to is fine, so I don't see
the need to force those transitions.

BTW I just thought of a different use case for this. Imagine a plugin
is estimating how much power a CPU is consuming; if the CPU is idle,
the power model would bring down the voltage/freq and account for that.
Of course the model would also want to track the instructions being
executed, which is a nice segue to your point below.

> > What are the "guest sleep points" you mention? Are they
> > target-agnostic?
> 
> I mean we can arrive here for a variety of reasons - not all of which
> are triggered by the guest going to sleep. But that isn't your current
> use case.
> 
> They aren't target agnostic as not all guests try to fully model their
> "sleeping" instructions. Some just end up busy spinning until the event
> they should have been sleeping until happens.

I see, so you mean something like a "pause" instruction in the guest.
Plugins that needed such level of detail (like the imaginary power
model plugin I mentioned above) would have to track these
instructions as well. IOW, this would be orthogonal to the "idle"
callback as implemented in this series.

		Emilio

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

* Re: [Qemu-devel] [RFC 23/48] translator: add plugin_insn argument to translate_insn
  2018-11-26 19:30         ` Richard Henderson
@ 2018-11-27  1:38           ` Emilio G. Cota
  2018-11-28  0:54             ` Emilio G. Cota
  0 siblings, 1 reply; 132+ messages in thread
From: Emilio G. Cota @ 2018-11-27  1:38 UTC (permalink / raw)
  To: Richard Henderson
  Cc: Alex Bennée, Stefan Hajnoczi, Peter Maydell, qemu-devel,
	Pavel Dovgalyuk, Lluís Vilanova

On Mon, Nov 26, 2018 at 11:30:25 -0800, Richard Henderson wrote:
> On 11/26/18 11:07 AM, Emilio G. Cota wrote:
> > The main reason why I added the qemu_plugin_insn_append calls
> > was to avoid reading the instructions twice from guest memory,
> > because I was worried that doing so might somehow alter the
> > guest's execution, e.g. what if we read a cross-page instruction,
> > and both pages mapped to the same TLB entry? We'd end up having
> > more TLB misses because instrumentation was enabled.
> 
> A better solution for this, I think is to change direct calls from
> 
>   cpu_ldl_code(env, pc);
> to
>   translator_ldl_code(dc_base, env, pc);
> 
> instead of passing around a new argument separate from DisasContextBase?

I think this + diff'ing pc_next should work to figure out the
contents and size of each instruction.

I'll do it this way in v2.

Thanks,

		Emilio

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

* Re: [Qemu-devel] [RFC 38/48] translator: implement 2-pass translation
  2018-11-26 15:16   ` Alex Bennée
@ 2018-11-27  2:16     ` Emilio G. Cota
  2018-11-27 14:48       ` Alex Bennée
  0 siblings, 1 reply; 132+ messages in thread
From: Emilio G. Cota @ 2018-11-27  2:16 UTC (permalink / raw)
  To: Alex Bennée
  Cc: qemu-devel, Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Stefan Hajnoczi, Richard Henderson

On Mon, Nov 26, 2018 at 15:16:00 +0000, Alex Bennée wrote:
> Emilio G. Cota <cota@braap.org> writes:
(snip)
> > +    if (tb_trans_cb && first_pass) {
> > +        qemu_plugin_tb_trans_cb(cpu, plugin_tb);
> > +        first_pass = false;
> > +        goto translate;
> > +    }
> 
> So the only reason we are doing this two pass tango is to ensure the
> plugin can insert TCG ops before the actual translation has occurred?

Not only. The idea is to provide plugins with well-defined TBs,
i.e. the instruction sizes and contents can be queried by the plugin
before the plugin decides how/where to instrument the TB.

Since in the targets we generate TCG code and also generate
host code in a single shot (TranslatorOps.translate_insn),
the 2-pass approach is a workaround to first get the
well-defined TB, and in the second pass inject the instrumentation
in the appropriate places.

This is a bit of a waste but given that it only happens at
translate time, it can have negligible performance impact --
I measured a 0.13% gmean slowdown for SPEC06int.

> I think we can do better, especially as the internal structures of
> TCGops are implemented as a list so ops and be inserted before and after
> other ops. This is currently only done by the optimiser at the moment,
> see:
> 
>   TCGOp *tcg_op_insert_before(TCGContext *s, TCGOp *op, TCGOpcode opc, int narg);
>   TCGOp *tcg_op_insert_after(TCGContext *s, TCGOp *op, TCGOpcode opc, int narg);
> 
> and all the base tcg ops end up going to tcg_emit_op which just appends
> to the tail. But if we can come up with a neater way to track the op
> used before the current translated expression we could do away with two
> phases translation completely.

This list of ops is generated via TranslatorOps.translate_insn.
Unfortunately, this function also defines the guest insns that form the TB.
Decoupling the two actions ("define the TB" and "translate to TCG ops")
would be ideal, but I didn't want to rewrite all the target translators
in QEMU, and opted instead for the 2-pass approach as a compromise.

Thanks,

		Emilio

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

* Re: [Qemu-devel] [RFC 41/48] configure: add --enable-plugins
  2018-10-25 17:20 ` [Qemu-devel] [RFC 41/48] configure: add --enable-plugins Emilio G. Cota
@ 2018-11-27 12:11   ` Alex Bennée
  2018-11-27 17:08     ` Emilio G. Cota
  2018-11-27 12:43   ` Roman Bolshakov
  1 sibling, 1 reply; 132+ messages in thread
From: Alex Bennée @ 2018-11-27 12:11 UTC (permalink / raw)
  To: Emilio G. Cota
  Cc: qemu-devel, Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Stefan Hajnoczi


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

> For now only add it for ELF platforms, since we rely on the linker's
> --dynamic-list flag to pass a list of symbols to be exported to the
> executable. An alternative would be to use -rdynamic, but that would
> expose all of QEMU's objects to plugins.
>
> I have no experience with non-ELF systems but I suspect adding support
> for those should be pretty easy.
>
> Signed-off-by: Emilio G. Cota <cota@braap.org>

As far as the configure logic is concerned:

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

I'm not quite following what is so special about the dynamic-list
symbols compared to the rest of the symbols in the binary. when I do
readelf -s they are all specified as GLOBAL DEFAULT.

Perhaps this will become clearer to me once I get to the implementation
of the plugins later in the series?


> ---
>  configure | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 51 insertions(+)
>
> diff --git a/configure b/configure
> index 03bf719ca7..78e86098e5 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
>
> @@ -477,6 +478,7 @@ libxml2=""
>  docker="no"
>  debug_mutex="no"
>  libpmem=""
> +plugins="no"
>
>  # cross compilers defaults, can be overridden with --cross-cc-ARCH
>  cross_cc_aarch64="aarch64-linux-gnu-gcc"
> @@ -1443,6 +1445,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"
> @@ -1633,6 +1639,8 @@ Advanced options (experts only):
>                             xen pv domain builder
>    --enable-debug-stack-usage
>                             track the maximum stack usage of stacks created by qemu_alloc_stack
> +  --enable-plugins
> +                           enable plugins via shared library loading
>
>  Optional features, enabled with --enable-FEATURE and
>  disabled with --disable-FEATURE, default is enabled if available:
> @@ -5204,6 +5212,42 @@ 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
> +
> +if compile_prog "" "-Wl,--dynamic-list=$TMPTXT" ; then
> +  ld_dynamic_list="yes"
> +else
> +  if test "$plugins" = "yes" ; 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) used for this purpose."
> +  fi
> +fi
> +
>  ########################################
>  # See if 16-byte vector operations are supported.
>  # Even without a vector unit the compiler may expand these.
> @@ -6091,6 +6135,7 @@ echo "VxHS block device $vxhs"
>  echo "capstone          $capstone"
>  echo "docker            $docker"
>  echo "libpmem support   $libpmem"
> +echo "plugin support    $plugins"
>
>  if test "$sdl_too_old" = "yes"; then
>  echo "-> Your SDL version is too old - please upgrade to have SDL support"
> @@ -6849,6 +6894,12 @@ if test "$libpmem" = "yes" ; then
>    echo "CONFIG_LIBPMEM=y" >> $config_host_mak
>  fi
>
> +if test "$plugins" = "yes" ; then
> +    echo "CONFIG_PLUGINS=y" >> $config_host_mak
> +    LIBS="-ldl $LIBS"
> +    LDFLAGS="-Wl,--dynamic-list=\$(SRC_PATH)/qemu-plugins.symbols $LDFLAGS"
> +fi
> +
>  if test "$tcg_interpreter" = "yes"; then
>    QEMU_INCLUDES="-iquote \$(SRC_PATH)/tcg/tci $QEMU_INCLUDES"
>  elif test "$ARCH" = "sparc64" ; then


--
Alex Bennée

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

* Re: [Qemu-devel] [RFC 41/48] configure: add --enable-plugins
  2018-10-25 17:20 ` [Qemu-devel] [RFC 41/48] configure: add --enable-plugins Emilio G. Cota
  2018-11-27 12:11   ` Alex Bennée
@ 2018-11-27 12:43   ` Roman Bolshakov
  2018-11-27 23:13     ` Emilio G. Cota
  1 sibling, 1 reply; 132+ messages in thread
From: Roman Bolshakov @ 2018-11-27 12:43 UTC (permalink / raw)
  To: Emilio G. Cota
  Cc: qemu-devel, Peter Maydell, Alex Bennée, Lluís Vilanova,
	Pavel Dovgalyuk, Stefan Hajnoczi

On Thu, Oct 25, 2018 at 01:20:50PM -0400, Emilio G. Cota wrote:
> For now only add it for ELF platforms, since we rely on the linker's
> --dynamic-list flag to pass a list of symbols to be exported to the
> executable. An alternative would be to use -rdynamic, but that would
> expose all of QEMU's objects to plugins.
> 
> I have no experience with non-ELF systems but I suspect adding support
> for those should be pretty easy.
> 
> Signed-off-by: Emilio G. Cota <cota@braap.org>
> ---
>  configure | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 51 insertions(+)
> 
> diff --git a/configure b/configure
> index 03bf719ca7..78e86098e5 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
>  
> @@ -477,6 +478,7 @@ libxml2=""
>  docker="no"
>  debug_mutex="no"
>  libpmem=""
> +plugins="no"
>  
>  # cross compilers defaults, can be overridden with --cross-cc-ARCH
>  cross_cc_aarch64="aarch64-linux-gnu-gcc"
> @@ -1443,6 +1445,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"
> @@ -1633,6 +1639,8 @@ Advanced options (experts only):
>                             xen pv domain builder
>    --enable-debug-stack-usage
>                             track the maximum stack usage of stacks created by qemu_alloc_stack
> +  --enable-plugins
> +                           enable plugins via shared library loading
>  
>  Optional features, enabled with --enable-FEATURE and
>  disabled with --disable-FEATURE, default is enabled if available:
> @@ -5204,6 +5212,42 @@ 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
> +
> +if compile_prog "" "-Wl,--dynamic-list=$TMPTXT" ; then
> +  ld_dynamic_list="yes"
> +else
> +  if test "$plugins" = "yes" ; 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) used for this purpose."
> +  fi
> +fi
> +
>  ########################################
>  # See if 16-byte vector operations are supported.
>  # Even without a vector unit the compiler may expand these.
> @@ -6091,6 +6135,7 @@ echo "VxHS block device $vxhs"
>  echo "capstone          $capstone"
>  echo "docker            $docker"
>  echo "libpmem support   $libpmem"
> +echo "plugin support    $plugins"
>  
>  if test "$sdl_too_old" = "yes"; then
>  echo "-> Your SDL version is too old - please upgrade to have SDL support"
> @@ -6849,6 +6894,12 @@ if test "$libpmem" = "yes" ; then
>    echo "CONFIG_LIBPMEM=y" >> $config_host_mak
>  fi
>  
> +if test "$plugins" = "yes" ; then
> +    echo "CONFIG_PLUGINS=y" >> $config_host_mak
> +    LIBS="-ldl $LIBS"
> +    LDFLAGS="-Wl,--dynamic-list=\$(SRC_PATH)/qemu-plugins.symbols $LDFLAGS"
> +fi
> +
>  if test "$tcg_interpreter" = "yes"; then
>    QEMU_INCLUDES="-iquote \$(SRC_PATH)/tcg/tci $QEMU_INCLUDES"
>  elif test "$ARCH" = "sparc64" ; then
> -- 
> 2.17.1
> 
> 

ld64 on macOS has similar -exported_symbols_list option. Here's the reference:

     -exported_symbols_list filename
	The specified filename contains a list of global symbol names
	that will remain as global symbols in the output file.  All other
	global symbols will be treated as if they were marked as
	__private_extern__ (aka visibility=hidden) and will not be global in
	the output file. The symbol names listed in filename must be one per
	line.  Leading and trailing white space are not part of the symbol
	name.  Lines starting with # are ignored, as are lines with only white
	space.  Some wildcards (similar to shell file matching) are supported.
	The * matches zero or more characters.  The ?  matches one character.
	[abc] matches one character which must be an 'a', 'b', or 'c'.
	[a-z] matches any single lower case letter from 'a' to 'z'.


I can try your branch if you add support of the linker flag or send required
changes via GitHub.

Best regards,
Roman

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

* Re: [Qemu-devel] [RFC 23/48] translator: add plugin_insn argument to translate_insn
  2018-11-26 19:07       ` Emilio G. Cota
  2018-11-26 19:30         ` Richard Henderson
@ 2018-11-27 13:08         ` Pavel Dovgalyuk
  1 sibling, 0 replies; 132+ messages in thread
From: Pavel Dovgalyuk @ 2018-11-27 13:08 UTC (permalink / raw)
  To: 'Emilio G. Cota', 'Richard Henderson'
  Cc: 'Alex Bennée', 'Stefan Hajnoczi',
	'Peter Maydell',
	qemu-devel, 'Lluís Vilanova'

> From: Emilio G. Cota [mailto:cota@braap.org]
> On Mon, Nov 26, 2018 at 10:27:12 -0800, Richard Henderson wrote:
> > On 11/26/18 6:52 AM, Alex Bennée wrote:
> > > I'm not convinced this is the best way to go about it. We end up having
> > > to sprinkle the plugin calls into each decoder rather than keeping all
> > > the infrastructure in the common main loop. However the common loop will
> > > need to know the total number of bytes decoded so we could change the
> > > declaration to:
> > >
> > >   int (*translate_insn)(DisasContextBase *db, CPUState *cpu);
> > >
> > > and return the number of bytes decoded.
> >
> > Returning the number of bytes is more difficult than simply just
> >
> >     old_pc = db->pc_next;
> >     opc->translate_insn(db, cpu);
> >     bytes = db->pc_next - old_pc;
> >
> > requiring no target changes at all.
> 
> The main reason why I added the qemu_plugin_insn_append calls
> was to avoid reading the instructions twice from guest memory,
> because I was worried that doing so might somehow alter the
> guest's execution, e.g. what if we read a cross-page instruction,
> and both pages mapped to the same TLB entry? We'd end up having
> more TLB misses because instrumentation was enabled.

In our plugins we use cpu_debug_rw function.
But I think that your example with mapping of the page and simultaneous
unmapping of the previous is impossible, because both pages should
be available to the translator for creating the TB.
The translation immediately interrupted with TLB miss and repeated
again after mapping. It means that the cross-page instruction
should not be unmapped until it completely executes.

Pavel Dovgalyuk

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

* Re: [Qemu-devel] [RFC 38/48] translator: implement 2-pass translation
  2018-11-27  2:16     ` Emilio G. Cota
@ 2018-11-27 14:48       ` Alex Bennée
  2018-11-27 19:06         ` Emilio G. Cota
  0 siblings, 1 reply; 132+ messages in thread
From: Alex Bennée @ 2018-11-27 14:48 UTC (permalink / raw)
  To: Emilio G. Cota
  Cc: qemu-devel, Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Stefan Hajnoczi, Richard Henderson


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

> On Mon, Nov 26, 2018 at 15:16:00 +0000, Alex Bennée wrote:
>> Emilio G. Cota <cota@braap.org> writes:
> (snip)
>> > +    if (tb_trans_cb && first_pass) {
>> > +        qemu_plugin_tb_trans_cb(cpu, plugin_tb);
>> > +        first_pass = false;
>> > +        goto translate;
>> > +    }
>>
>> So the only reason we are doing this two pass tango is to ensure the
>> plugin can insert TCG ops before the actual translation has occurred?
>
> Not only. The idea is to provide plugins with well-defined TBs,
> i.e. the instruction sizes and contents can be queried by the plugin
> before the plugin decides how/where to instrument the TB.

Hmmm, this seems a little to close to internal knowledge of the TCG. Is
the idea that a plugin might make a different decision based on the
number of a particular type of instruction in the translation block?

This seems like it would get broken if we wanted to implement other
types of TranslationBlock (e.g. hot-blocks with multiple exits for the
non-hot case).

That said looking at the examples using it so far it doesn't seem to be
doing more than looking at the total number of instructions for a given
translation block.

> Since in the targets we generate TCG code and also generate
> host code in a single shot (TranslatorOps.translate_insn),
> the 2-pass approach is a workaround to first get the
> well-defined TB, and in the second pass inject the instrumentation
> in the appropriate places.

Richard's suggestion of providing a central translator_ldl_code could
keep the book keeping of each instruction location and contents in the
core translator. With a little tweaking to the TCG we could then insert
our instrumentation at the end of the pass with all the knowledge we
want to export to the plugin.

Inserting instrumentation after instructions have executed will be
trickier though due to reasons Peter mentioned on IRC.

> This is a bit of a waste but given that it only happens at
> translate time, it can have negligible performance impact --
> I measured a 0.13% gmean slowdown for SPEC06int.

I'm less concerned about efficiency as complicating the code, especially
if we are baking in concepts that restrict our freedom to change code
generation around going forward.

>
>> I think we can do better, especially as the internal structures of
>> TCGops are implemented as a list so ops and be inserted before and after
>> other ops. This is currently only done by the optimiser at the moment,
>> see:
>>
>>   TCGOp *tcg_op_insert_before(TCGContext *s, TCGOp *op, TCGOpcode opc, int narg);
>>   TCGOp *tcg_op_insert_after(TCGContext *s, TCGOp *op, TCGOpcode opc, int narg);
>>
>> and all the base tcg ops end up going to tcg_emit_op which just appends
>> to the tail. But if we can come up with a neater way to track the op
>> used before the current translated expression we could do away with two
>> phases translation completely.
>
> This list of ops is generated via TranslatorOps.translate_insn.
> Unfortunately, this function also defines the guest insns that form the TB.
> Decoupling the two actions ("define the TB" and "translate to TCG ops")
> would be ideal, but I didn't want to rewrite all the target translators
> in QEMU, and opted instead for the 2-pass approach as a compromise.

I don't quite follow. When we've done all our translation into TCG ops
haven't we by definition defined the TB?

Maybe the interface shouldn't be per-insn and per-TB but just an
arbitrary chunk of instructions. We could call the plugin with a list of
instructions with some opaque data that can be passed back to the plugin
APIs to allow insertion of instrumentation at the appropriate points.
The initial implementation would be a single-pass and called after the
TCG op generation. An instruction counter plugin would then be free to
insert counter instrumentation as frequently or infrequently as it
wants. These chunks wouldn't have to be tied to the internals of TCG and
in the worst case we could just inform the plugin in 1 insn chunks
without having to change the API?

What do you think?


--
Alex Bennée

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

* Re: [Qemu-devel] [RFC 41/48] configure: add --enable-plugins
  2018-11-27 12:11   ` Alex Bennée
@ 2018-11-27 17:08     ` Emilio G. Cota
  0 siblings, 0 replies; 132+ messages in thread
From: Emilio G. Cota @ 2018-11-27 17:08 UTC (permalink / raw)
  To: Alex Bennée
  Cc: qemu-devel, Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Stefan Hajnoczi

On Tue, Nov 27, 2018 at 12:11:32 +0000, Alex Bennée wrote:
> 
> Emilio G. Cota <cota@braap.org> writes:
> 
> > For now only add it for ELF platforms, since we rely on the linker's
> > --dynamic-list flag to pass a list of symbols to be exported to the
> > executable. An alternative would be to use -rdynamic, but that would
> > expose all of QEMU's objects to plugins.
> >
> > I have no experience with non-ELF systems but I suspect adding support
> > for those should be pretty easy.
> >
> > Signed-off-by: Emilio G. Cota <cota@braap.org>
> 
> As far as the configure logic is concerned:
> 
> Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
> 
> I'm not quite following what is so special about the dynamic-list
> symbols compared to the rest of the symbols in the binary. when I do
> readelf -s they are all specified as GLOBAL DEFAULT.
> 
> Perhaps this will become clearer to me once I get to the implementation
> of the plugins later in the series?

You should look at `readelf --dyn-syms's output. There you'll see
that the only defined function symbols are qemu_plugin_* ones (plus
_init and _fini).

The goal is to prevent plugins from accessing symbols outside of
the plugin API. For instance, if from plugin 'foo' I try to call
qht_init (after having added the necessary declarations in foo.c),
at dlopen time I get:
  plugin_load: libfoo.so: undefined symbol: qht_init

The alternative is to export all symbols; we can do this by
passing --export-dynamic to the linker instead of an explicit
--dynamic-list. We can then access *any* global symbol in the
QEMU executable. The previous example now works past dlopen, i.e.
I can call qht_init from my plugin:
  ERROR:/data/src/qemu/util/qht.c:401:qht_init: assertion failed: (cmp)

Thanks,

		Emilio

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

* Re: [Qemu-devel] [RFC 45/48] plugin: lockstep execution support
  2018-10-25 17:20 ` [Qemu-devel] [RFC 45/48] plugin: " Emilio G. Cota
@ 2018-11-27 18:20   ` Alex Bennée
  2018-11-27 19:19     ` Emilio G. Cota
  0 siblings, 1 reply; 132+ messages in thread
From: Alex Bennée @ 2018-11-27 18:20 UTC (permalink / raw)
  To: Emilio G. Cota
  Cc: qemu-devel, Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Stefan Hajnoczi


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

> Signed-off-by: Emilio G. Cota <cota@braap.org>

There are no users of this for now so I don't think this qualifies for a
first cut of the plugin API. Is the lockstep support only their for
plugins? Is there any practical use that isn't handled by non-MTTCG
round-robin and icount type scenarios?

> ---
>  include/qemu/plugin-api.h |  7 +++++++
>  include/qemu/plugin.h     |  5 +++++
>  cpus.c                    |  1 +
>  plugin.c                  | 35 +++++++++++++++++++++++++++++++++++
>  qemu-plugins.symbols      |  3 +++
>  5 files changed, 51 insertions(+)
>
> diff --git a/include/qemu/plugin-api.h b/include/qemu/plugin-api.h
> index 076353a2d2..5062e20e08 100644
> --- a/include/qemu/plugin-api.h
> +++ b/include/qemu/plugin-api.h
> @@ -227,6 +227,13 @@ typedef int64_t (*qemu_plugin_clock_func_t)(void);
>  bool qemu_plugin_register_virtual_clock(qemu_plugin_id_t id,
>                                          qemu_plugin_clock_func_t clock);
>
> +void qemu_plugin_enable_lockstep_execution(void);
> +
> +void qemu_plugin_register_lockstep_cb(qemu_plugin_id_t id,
> +                                      qemu_plugin_simple_cb_t cb);
> +
> +void qemu_plugin_end_time_slice(void);
> +
>  /* returns -1 in user-mode */
>  int qemu_plugin_n_vcpus(void);
>
> diff --git a/include/qemu/plugin.h b/include/qemu/plugin.h
> index 617161329f..c19071bdbe 100644
> --- a/include/qemu/plugin.h
> +++ b/include/qemu/plugin.h
> @@ -58,6 +58,7 @@ enum qemu_plugin_event {
>      QEMU_PLUGIN_EV_VCPU_RESUME,
>      QEMU_PLUGIN_EV_VCPU_SYSCALL,
>      QEMU_PLUGIN_EV_VCPU_SYSCALL_RET,
> +    QEMU_PLUGIN_EV_LOCKSTEP,
>      QEMU_PLUGIN_EV_FLUSH,
>      QEMU_PLUGIN_EV_ATEXIT,
>      QEMU_PLUGIN_EV_MAX,
> @@ -194,6 +195,7 @@ void qemu_plugin_atexit_cb(void);
>
>  void qemu_plugin_add_dyn_cb_arr(struct qemu_plugin_dyn_cb_arr *arr);
>  int64_t plugin_get_clock(void);
> +void plugin_lockstep_cb(void);
>
>  #else /* !CONFIG_PLUGINS */
>
> @@ -237,6 +239,9 @@ static inline
>  void qemu_plugin_add_dyn_cb_arr(struct qemu_plugin_dyn_cb_arr *arr)
>  { }
>
> +static inline void plugin_lockstep_cb(void)
> +{ }
> +
>  int64_t plugin_get_clock(void);
>
>  #endif /* !CONFIG_PLUGINS */
> diff --git a/cpus.c b/cpus.c
> index a446632a5c..8f490d1b11 100644
> --- a/cpus.c
> +++ b/cpus.c
> @@ -1359,6 +1359,7 @@ static void lockstep_check_stop(CPUState *cpu)
>              /* wake up all waiting cpus */
>              lockstep_ongoing_wakeup = true;
>              n_lockstep_running_cpus = n_lockstep_cpus;
> +            plugin_lockstep_cb();
>              qemu_mutex_unlock(&lockstep_lock);
>              cpu_mutex_unlock(cpu);
>              for (i = 0; i < n_lockstep_cpus; i++) {
> diff --git a/plugin.c b/plugin.c
> index 291767f2bb..117f303249 100644
> --- a/plugin.c
> +++ b/plugin.c
> @@ -472,6 +472,7 @@ static void plugin_cb__simple(enum qemu_plugin_event ev)
>
>      switch (ev) {
>      case QEMU_PLUGIN_EV_FLUSH:
> +    case QEMU_PLUGIN_EV_LOCKSTEP:
>          QLIST_FOREACH_SAFE_RCU(cb, &plugin.cb_lists[ev], entry, next) {
>              qemu_plugin_simple_cb_t func = cb->f.simple;
>
> @@ -1043,6 +1044,40 @@ int64_t plugin_get_clock(void)
>  }
>  #endif
>
> +/*
> + * We manage the CPU state changes; the plugin will control the length of the
> + * execution windows.
> + */
> +void qemu_plugin_enable_lockstep_execution(void)
> +{
> +#ifdef CONFIG_USER_ONLY
> +    abort();
> +#else
> +    cpu_lockstep_enable();
> +#endif
> +}
> +
> +void qemu_plugin_end_time_slice(void)
> +{
> +#ifdef CONFIG_USER_ONLY
> +    abort();
> +#else
> +    g_assert(current_cpu);
> +    cpu_lockstep_request_stop(current_cpu);
> +#endif
> +}
> +
> +void qemu_plugin_register_lockstep_cb(qemu_plugin_id_t id,
> +                                      qemu_plugin_simple_cb_t cb)
> +{
> +    plugin_register_cb(id, QEMU_PLUGIN_EV_LOCKSTEP, cb);
> +}
> +
> +void plugin_lockstep_cb(void)
> +{
> +    plugin_cb__simple(QEMU_PLUGIN_EV_LOCKSTEP);
> +}
> +
>  static void __attribute__((__constructor__)) plugin_init(void)
>  {
>      int i;
> diff --git a/qemu-plugins.symbols b/qemu-plugins.symbols
> index 93587b07e1..a3268a40c7 100644
> --- a/qemu-plugins.symbols
> +++ b/qemu-plugins.symbols
> @@ -1,5 +1,8 @@
>  {
>    qemu_plugin_uninstall;
> +  qemu_plugin_enable_lockstep_execution;
> +  qemu_plugin_end_time_slice;
> +  qemu_plugin_register_lockstep_cb;
>    qemu_plugin_register_vcpu_init_cb;
>    qemu_plugin_register_vcpu_exit_cb;
>    qemu_plugin_register_vcpu_idle_cb;


--
Alex Bennée

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

* Re: [Qemu-devel] [RFC 47/48] plugin: support guest hooks
  2018-10-25 17:20 ` [Qemu-devel] [RFC 47/48] plugin: support guest hooks Emilio G. Cota
@ 2018-11-27 18:28   ` Alex Bennée
  0 siblings, 0 replies; 132+ messages in thread
From: Alex Bennée @ 2018-11-27 18:28 UTC (permalink / raw)
  To: Emilio G. Cota
  Cc: qemu-devel, Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Stefan Hajnoczi


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

> These "hooks" are callbacks from the guest to plugins. This is
> useful when we need guest-host communication, for instance to
> signal the beginning/end of a certain "region of interest" in
> the guest program. Simulators typically would use "magic"
> instructions for this, but that is painful to maintain across
> ISAs. Instead, we use plugin-chan PCI device through which we can
> relay guest messages to the host.

I suspect the solution for this is either a virtio socket that may (or
may not) be located on a PCI bus. However definitely out of scope for a
first cut of the plugin API, especially without an example usage.

Android has a similar guest-aware channel although the host side was
outside of QEMU and QEMU's part was just to expose a socket on the host
side that adb could talk to.

>
> Signed-off-by: Emilio G. Cota <cota@braap.org>
> ---
>  include/qemu/plugin-api.h |  6 ++++++
>  include/qemu/plugin.h     |  2 ++
>  plugin.c                  | 13 +++++++++++++
>  qemu-plugins.symbols      |  1 +
>  4 files changed, 22 insertions(+)
>
> diff --git a/include/qemu/plugin-api.h b/include/qemu/plugin-api.h
> index 5062e20e08..a5faff6a2a 100644
> --- a/include/qemu/plugin-api.h
> +++ b/include/qemu/plugin-api.h
> @@ -234,6 +234,12 @@ void qemu_plugin_register_lockstep_cb(qemu_plugin_id_t id,
>
>  void qemu_plugin_end_time_slice(void);
>
> +typedef void (*qemu_plugin_hook_cb_t)(uint32_t cmd, const void *data,
> +                                      size_t size);
> +
> +void qemu_plugin_register_hook_cb(qemu_plugin_id_t id,
> +                                  qemu_plugin_hook_cb_t cb);
> +
>  /* returns -1 in user-mode */
>  int qemu_plugin_n_vcpus(void);
>
> diff --git a/include/qemu/plugin.h b/include/qemu/plugin.h
> index f3c18d1032..ced265a127 100644
> --- a/include/qemu/plugin.h
> +++ b/include/qemu/plugin.h
> @@ -61,12 +61,14 @@ enum qemu_plugin_event {
>      QEMU_PLUGIN_EV_LOCKSTEP,
>      QEMU_PLUGIN_EV_FLUSH,
>      QEMU_PLUGIN_EV_ATEXIT,
> +    QEMU_PLUGIN_EV_HOOK,
>      QEMU_PLUGIN_EV_MAX,
>  };
>
>  union qemu_plugin_cb_sig {
>      qemu_plugin_simple_cb_t          simple;
>      qemu_plugin_udata_cb_t           udata;
> +    qemu_plugin_hook_cb_t            hook;
>      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;
> diff --git a/plugin.c b/plugin.c
> index 2bbc14e2f3..4004451bdb 100644
> --- a/plugin.c
> +++ b/plugin.c
> @@ -1078,8 +1078,21 @@ void plugin_lockstep_cb(void)
>      plugin_cb__simple(QEMU_PLUGIN_EV_LOCKSTEP);
>  }
>
> +void qemu_plugin_register_hook_cb(qemu_plugin_id_t id, qemu_plugin_hook_cb_t cb)
> +{
> +    plugin_register_cb(id, QEMU_PLUGIN_EV_HOOK, cb);
> +}
> +
>  void plugin_chan_xmit(uint32_t cmd, const void *data, size_t size)
>  {
> +    struct qemu_plugin_cb *cb, *next;
> +    enum qemu_plugin_event ev = QEMU_PLUGIN_EV_HOOK;
> +
> +    QLIST_FOREACH_SAFE_RCU(cb, &plugin.cb_lists[ev], entry, next) {
> +        qemu_plugin_hook_cb_t func = cb->f.hook;
> +
> +        func(cmd, data, size);
> +    }
>  }
>
>  static void __attribute__((__constructor__)) plugin_init(void)
> diff --git a/qemu-plugins.symbols b/qemu-plugins.symbols
> index a3268a40c7..2e17693da8 100644
> --- a/qemu-plugins.symbols
> +++ b/qemu-plugins.symbols
> @@ -31,6 +31,7 @@
>    qemu_plugin_mem_is_big_endian;
>    qemu_plugin_mem_is_store;
>    qemu_plugin_vcpu_for_each;
> +  qemu_plugin_register_hook_cb;
>    qemu_plugin_n_vcpus;
>    qemu_plugin_n_max_vcpus;
>  };


--
Alex Bennée

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

* Re: [Qemu-devel] [RFC 38/48] translator: implement 2-pass translation
  2018-11-27 14:48       ` Alex Bennée
@ 2018-11-27 19:06         ` Emilio G. Cota
  2018-11-28  2:30           ` Emilio G. Cota
  0 siblings, 1 reply; 132+ messages in thread
From: Emilio G. Cota @ 2018-11-27 19:06 UTC (permalink / raw)
  To: Alex Bennée
  Cc: qemu-devel, Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Stefan Hajnoczi, Richard Henderson

On Tue, Nov 27, 2018 at 14:48:11 +0000, Alex Bennée wrote:
> Emilio G. Cota <cota@braap.org> writes:
> > On Mon, Nov 26, 2018 at 15:16:00 +0000, Alex Bennée wrote:
> >> Emilio G. Cota <cota@braap.org> writes:
> > (snip)
> >> > +    if (tb_trans_cb && first_pass) {
> >> > +        qemu_plugin_tb_trans_cb(cpu, plugin_tb);
> >> > +        first_pass = false;
> >> > +        goto translate;
> >> > +    }
> >>
> >> So the only reason we are doing this two pass tango is to ensure the
> >> plugin can insert TCG ops before the actual translation has occurred?
> >
> > Not only. The idea is to provide plugins with well-defined TBs,
> > i.e. the instruction sizes and contents can be queried by the plugin
> > before the plugin decides how/where to instrument the TB.
> 
> Hmmm, this seems a little to close to internal knowledge of the TCG.

As far as plugins are concerned, a "TB" is a sequence of instructions
that will (unless there are exceptions) execute in sequence. That is,
single-entry and single-exit.
QEMU is free to cut those in any way it wants, and there's no need
for a 1:1 mapping between the "TBs" exported to plugins and
the "TranslationBlock"s we manage internally in QEMU.

I thought about calling them "basic blocks", but then that could
confuse users because not all TB's meet the definition of basic blocks,
that is TBs might end in a non-branch instruction, whereas basic
blocks don't.

So I kept the TB name, but note that all that plugins can assume
about TBs, is that they are single-entry and single-exit, that's it.
Different QEMU releases will cut TB's differently, and plugins
will cope with that perfectly fine. IOW, this imposes no
restrictions on TCG's implementation.

> Is the idea that a plugin might make a different decision based on the
> number of a particular type of instruction in the translation block?

Plugins will make their decisions based on the TB's contents.
For that, they need to know what instructions form the TB,
and be able to disassemble them.

> This seems like it would get broken if we wanted to implement other
> types of TranslationBlock (e.g. hot-blocks with multiple exits for the
> non-hot case).

Again, let's dissociate struct TranslationBlock vs. what we export;
let's call the latter "plugin TB's" for discussion's sake.

If we implemented single-entry, multiple-exit traces, we could implement
that in any way we wanted (e.g. expanding TranslationBlock, or grouping
them into TranslationTraces or whatever we called them). Plugins
would them be exposed to an interface similar to what Pin/DynamoRIO
offer, that is, plugins can subscribe to "Trace" translation events,
where Traces are lists of "plugin TB's".

Besides, I'm OK with having an API that we can break in the future.
(Pin/DynamoRIO do it all the time.)

> That said looking at the examples using it so far it doesn't seem to be
> doing more than looking at the total number of instructions for a given
> translation block.

OK, so I'm appending a more complicated example, where we use capstone
to look at the instructions in a TB at translation time. (Just
for illustration purposes, we then register an empty callback)

> > Since in the targets we generate TCG code and also generate
> > host code in a single shot (TranslatorOps.translate_insn),
> > the 2-pass approach is a workaround to first get the
> > well-defined TB, and in the second pass inject the instrumentation
> > in the appropriate places.
> 
> Richard's suggestion of providing a central translator_ldl_code could
> keep the book keeping of each instruction location and contents in the
> core translator.

Yes, at least for most targets I think that will work.

> With a little tweaking to the TCG we could then insert
> our instrumentation at the end of the pass with all the knowledge we
> want to export to the plugin.

After .translate_insn has returned for the last instruction, how
do we insert the instrumentation that the plugin wants--say, a TB
callback at the beginning of the TB, memory callbacks for the
2nd instruction, and an insn callback before the 3rd instruction
executes?

I don't see how we could achieve that with "a little tweaking"
instead of a 2nd pass, but I'd love to be wrong =)

> Inserting instrumentation after instructions have executed will be
> trickier though due to reasons Peter mentioned on IRC.

Particularly the last instruction in a TB; by the time we return
from .translate_insn, all code we insert will most likely be dead.

> > This is a bit of a waste but given that it only happens at
> > translate time, it can have negligible performance impact --
> > I measured a 0.13% gmean slowdown for SPEC06int.
> 
> I'm less concerned about efficiency as complicating the code, especially
> if we are baking in concepts that restrict our freedom to change code
> generation around going forward.

Agreed. I don't think exposing for now "plugin TB"s, i.e. single-entry,
single-exit blocks of insns restricts our future designs. And if all
else fails, we should reserve the right to break the API (e.g. via
new version numbers).

> >> I think we can do better, especially as the internal structures of
> >> TCGops are implemented as a list so ops and be inserted before and after
> >> other ops. This is currently only done by the optimiser at the moment,
> >> see:
> >>
> >>   TCGOp *tcg_op_insert_before(TCGContext *s, TCGOp *op, TCGOpcode opc, int narg);
> >>   TCGOp *tcg_op_insert_after(TCGContext *s, TCGOp *op, TCGOpcode opc, int narg);
> >>
> >> and all the base tcg ops end up going to tcg_emit_op which just appends
> >> to the tail. But if we can come up with a neater way to track the op
> >> used before the current translated expression we could do away with two
> >> phases translation completely.
> >
> > This list of ops is generated via TranslatorOps.translate_insn.
> > Unfortunately, this function also defines the guest insns that form the TB.
> > Decoupling the two actions ("define the TB" and "translate to TCG ops")
> > would be ideal, but I didn't want to rewrite all the target translators
> > in QEMU, and opted instead for the 2-pass approach as a compromise.
> 
> I don't quite follow. When we've done all our translation into TCG ops
> haven't we by definition defined the TB?

Yes, that's precisely my point.

The part that's missing is that once the TB is defined, we want to
insert instrumentation. Unfortunately, the "TCG ops" we get after
the 1st pass (no instrumentation), are very different from the list
of "TCG ops" that we get after the 2nd pass (after having injected
instrumentation). Could we get the same output of the 2nd pass,
just by using the output of the 1st and the list of injection points?
It's probably possible, but it seems very hard to do. (Think for
instance of memory callbacks, and further the complication of when
they use helpers).

The only reasonable way to do this I think would be to leave behind
"placeholder" TCG ops, that then we could scan to add further TCG ops.
But you'll agree with me that the 2nd pass is simpler :P

> Maybe the interface shouldn't be per-insn and per-TB but just an
> arbitrary chunk of instructions. We could call the plugin with a list of
> instructions with some opaque data that can be passed back to the plugin
> APIs to allow insertion of instrumentation at the appropriate points.

This is what this series implements. It just happens that these
chunks match our internal translation blocks, but there's no need for
that (and for now, no good reason for them not to match.)

> The initial implementation would be a single-pass and called after the
> TCG op generation. An instruction counter plugin would then be free to
> insert counter instrumentation as frequently or infrequently as it
> wants. These chunks wouldn't have to be tied to the internals of TCG and
> in the worst case we could just inform the plugin in 1 insn chunks
> without having to change the API?
> 
> What do you think?

With a single pass, all you can do is to add a callback with a descriptor
of what just executed. So the "instruction counter" example would work OK.

But what about a plugin that needed only memory accesses performed
by, say, xchg instructions? It'd have to subscribe to *all* memory
accesses, because after a TB is generated we cannot go back to
instrument something (due to the single pass), and then somehow figure
out a way to discard non-xchg-generated memory accesses at run-time.

So having instruction-grained injection is very valuable, and it's
not surprising that Pin/DynamoRIO provide that in their API.
With this series I'm trying to provide something similar.

Thanks,

		Emilio

---
#include <inttypes.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>

#include <capstone/capstone.h>
#include <qemu-plugin.h>

struct tb {
	size_t n_insns;
};

static csh cap_handle;
static cs_insn *cap_insn;

static void vcpu_tb_exec(unsigned int cpu_index, void *udata)
{ }

static void vcpu_tb_trans(qemu_plugin_id_t id, unsigned int cpu_index, struct qemu_plugin_tb *tb)
{
	struct tb *desc;
	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);
		size_t size = qemu_plugin_insn_size(insn);
		const uint8_t *code = qemu_plugin_insn_data(insn);
		uint64_t offset = 0;
		bool success;

		success = cs_disasm_iter(cap_handle, &code, &size, &offset, cap_insn);
		assert(success);
	}
	desc = malloc(sizeof(*desc));
	assert(desc);
	desc->n_insns = n;

	qemu_plugin_register_vcpu_tb_exec_cb(tb, vcpu_tb_exec,
					     QEMU_PLUGIN_CB_NO_REGS, desc);
}

QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, int argc, char **argv)
{
	if (cs_open(CS_ARCH_X86, CS_MODE_64, &cap_handle) != CS_ERR_OK) {
		return -1;
	}
	cap_insn = cs_malloc(cap_handle);
	if (cap_insn == NULL) {
		return -1;
	}
	qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans);
	return 0;
}

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

* Re: [Qemu-devel] [RFC 45/48] plugin: lockstep execution support
  2018-11-27 18:20   ` Alex Bennée
@ 2018-11-27 19:19     ` Emilio G. Cota
  0 siblings, 0 replies; 132+ messages in thread
From: Emilio G. Cota @ 2018-11-27 19:19 UTC (permalink / raw)
  To: Alex Bennée
  Cc: qemu-devel, Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Stefan Hajnoczi

On Tue, Nov 27, 2018 at 18:20:25 +0000, Alex Bennée wrote:
> 
> Emilio G. Cota <cota@braap.org> writes:
> 
> > Signed-off-by: Emilio G. Cota <cota@braap.org>
> 
> There are no users of this for now so I don't think this qualifies for a
> first cut of the plugin API.

Fair enough. It was more as an example that plugins are not just
for instrumentation purposes. Another example of this is the use of
plugins to control the guest's clock -- for instance, there was
this series
  https://lists.gnu.org/archive/html/qemu-devel/2017-02/msg03028.html
that implemented a module to control the guest's clock over sockets.
Instead, a plugin can just be loaded to take control, and the plugin
is free to interact with the outer world in whatever way it
wants (sockets, pipes, etc.). So having a plugin infrastructure
can make adding those features much easier to implement (FWIW,
that patch never landed on master).

> Is the lockstep support only their for
> plugins? Is there any practical use that isn't handled by non-MTTCG
> round-robin and icount type scenarios?

This is a compromise between icount and MTTCG by limiting the latter's
skew among vCPUs. So you don't get full determinism, but get closer to
it without giving up parallelism.

I think this feature could be added without plugins, by for instance
defaulting to an "MTTCG icount-like" behaviour. That is, the
time window of each CPU would expire after N instructions executed.

But I didn't have a use case for that; my use case is to control
the time windows from the plugins, since in my simulator the
plugin controls the guest clock, and instructions have different
latencies.

So yes, feel free to skip this patch!

Thanks,

		Emilio

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

* Re: [Qemu-devel] [RFC 41/48] configure: add --enable-plugins
  2018-11-27 12:43   ` Roman Bolshakov
@ 2018-11-27 23:13     ` Emilio G. Cota
  2018-11-28 10:43       ` Roman Bolshakov
  0 siblings, 1 reply; 132+ messages in thread
From: Emilio G. Cota @ 2018-11-27 23:13 UTC (permalink / raw)
  To: Roman Bolshakov
  Cc: qemu-devel, Peter Maydell, Alex Bennée, Lluís Vilanova,
	Pavel Dovgalyuk, Stefan Hajnoczi

On Tue, Nov 27, 2018 at 15:43:52 +0300, Roman Bolshakov wrote:
> ld64 on macOS has similar -exported_symbols_list option. Here's the reference:
> 
>      -exported_symbols_list filename
> 	The specified filename contains a list of global symbol names
> 	that will remain as global symbols in the output file.  All other
> 	global symbols will be treated as if they were marked as
> 	__private_extern__ (aka visibility=hidden) and will not be global in
> 	the output file. The symbol names listed in filename must be one per
> 	line.  Leading and trailing white space are not part of the symbol
> 	name.  Lines starting with # are ignored, as are lines with only white
> 	space.  Some wildcards (similar to shell file matching) are supported.
> 	The * matches zero or more characters.  The ?  matches one character.
> 	[abc] matches one character which must be an 'a', 'b', or 'c'.
> 	[a-z] matches any single lower case letter from 'a' to 'z'.
> 
> 
> I can try your branch if you add support of the linker flag or send required
> changes via GitHub.

Can you please try this branch? I added a commit with the appended.
  https://github.com/cota/qemu/tree/plugin-v2

You can inspect the symbols in the final binary with
`readelf --dyn-syms' or `nm -D'.

Thanks,

		Emilio

---
diff --git a/configure b/configure
index fe9707d951..3dc9c9697b 100755
--- a/configure
+++ b/configure
@@ -5176,15 +5176,31 @@ int main(void)
 }
 EOF
 
+ld_dynamic_list="no"
 if compile_prog "" "-Wl,--dynamic-list=$TMPTXT" ; then
   ld_dynamic_list="yes"
-else
-  if test "$plugins" = "yes" ; 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) used for this purpose."
-  fi
+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
 
 ########################################
@@ -6827,7 +6843,18 @@ fi
 if test "$plugins" = "yes" ; then
     echo "CONFIG_PLUGINS=y" >> $config_host_mak
     LIBS="-ldl $LIBS"
-    LDFLAGS="-Wl,--dynamic-list=\$(SRC_PATH)/qemu-plugins.symbols $LDFLAGS"
+    if test "$ld_dynamic_list" = "yes" ; then
+	LDFLAGS="-Wl,--dynamic-list=\$(SRC_PATH)/qemu-plugins.symbols $LDFLAGS"
+    elif test "$ld_exported_symbols_list" = "yes" ; then
+	ld64_symbols=qemu-plugins-ld64.symbols
+	echo "# Automatically generated by configure - do not modify" > $ld64_symbols
+	cat "$source_path/qemu-plugins.symbols" | grep qemu_ | sed 's/;//g' >> $ld64_symbols
+	LDFLAGS="-Wl,-exported_symbols_list,\$(BUILD_DIR)/$ld64_symbols $LDFLAGS"
+    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

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

* Re: [Qemu-devel] [RFC 23/48] translator: add plugin_insn argument to translate_insn
  2018-11-27  1:38           ` Emilio G. Cota
@ 2018-11-28  0:54             ` Emilio G. Cota
  2018-11-28  1:12               ` Emilio G. Cota
  2018-11-28 12:40               ` Alex Bennée
  0 siblings, 2 replies; 132+ messages in thread
From: Emilio G. Cota @ 2018-11-28  0:54 UTC (permalink / raw)
  To: Richard Henderson
  Cc: Peter Maydell, Stefan Hajnoczi, qemu-devel, Pavel Dovgalyuk,
	Alex Bennée, Lluís Vilanova

On Mon, Nov 26, 2018 at 20:38:25 -0500, Emilio G. Cota wrote:
> On Mon, Nov 26, 2018 at 11:30:25 -0800, Richard Henderson wrote:
> > On 11/26/18 11:07 AM, Emilio G. Cota wrote:
> > > The main reason why I added the qemu_plugin_insn_append calls
> > > was to avoid reading the instructions twice from guest memory,
> > > because I was worried that doing so might somehow alter the
> > > guest's execution, e.g. what if we read a cross-page instruction,
> > > and both pages mapped to the same TLB entry? We'd end up having
> > > more TLB misses because instrumentation was enabled.
> > 
> > A better solution for this, I think is to change direct calls from
> > 
> >   cpu_ldl_code(env, pc);
> > to
> >   translator_ldl_code(dc_base, env, pc);
> > 
> > instead of passing around a new argument separate from DisasContextBase?
> 
> I think this + diff'ing pc_next should work to figure out the
> contents and size of each instruction.

I just tried doing things this way.

For some targets like i386, the translator_ld* helpers work
great; the instruction contents are copied, and through
the helpers we get the sizes of the instructions as well.

For ARM though (and maybe others, I haven't gone
through all of them yet), arm_ldl_code does the following:

/* Load an instruction and return it in the standard little-endian order */
static inline uint32_t arm_ldl_code(CPUARMState *env, target_ulong addr,
                                    bool sctlr_b)
{
    uint32_t insn = cpu_ldl_code(env, addr);
    if (bswap_code(sctlr_b)) {
        return bswap32(insn);
    }
    return insn;
}

To avoid altering the signature of .translate_insn, I've modified
arm_ldl_code directly, as follows:

     uint32_t insn = cpu_ldl_code(env, addr);
+
     if (bswap_code(sctlr_b)) {
-        return bswap32(insn);
+        insn = bswap32(insn);
+    }
+    if (tcg_ctx->plugin_insn) {
+        qemu_plugin_insn_append(tcg_ctx->plugin_insn, &insn, sizeof(insn));
     }
     return insn;
 }

(NB. tcg_ctx->plugin_insn is updated by translator_loop
on every iteration.)

Let me know if you think I should do this differently.

Thanks,

		Emilio

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

* Re: [Qemu-devel] [RFC 23/48] translator: add plugin_insn argument to translate_insn
  2018-11-28  0:54             ` Emilio G. Cota
@ 2018-11-28  1:12               ` Emilio G. Cota
  2018-11-28 12:40               ` Alex Bennée
  1 sibling, 0 replies; 132+ messages in thread
From: Emilio G. Cota @ 2018-11-28  1:12 UTC (permalink / raw)
  To: Richard Henderson
  Cc: Peter Maydell, Stefan Hajnoczi, qemu-devel, Pavel Dovgalyuk,
	Alex Bennée, Lluís Vilanova

On Tue, Nov 27, 2018 at 19:54:02 -0500, Emilio G. Cota wrote:
> To avoid altering the signature of .translate_insn, I've modified
> arm_ldl_code directly, as follows:
> 
>      uint32_t insn = cpu_ldl_code(env, addr);
> +
>      if (bswap_code(sctlr_b)) {
> -        return bswap32(insn);
> +        insn = bswap32(insn);
> +    }
> +    if (tcg_ctx->plugin_insn) {
> +        qemu_plugin_insn_append(tcg_ctx->plugin_insn, &insn, sizeof(insn));
>      }
>      return insn;
>  }

Turns out it got even more complicated with thumb, since instructions
can be 16 or 32 bits.

I ended up with the appended (qemu_plugin_insn_append() returns
when the first argument is NULL).

		Emilio

diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c
index 88195ab949..e6caaff976 100644
--- a/target/arm/translate-a64.c
+++ b/target/arm/translate-a64.c
@@ -38,6 +38,7 @@
 #include "trace-tcg.h"
 #include "translate-a64.h"
 #include "qemu/atomic128.h"
+#include "qemu/plugin.h"
 
 static TCGv_i64 cpu_X[32];
 static TCGv_i64 cpu_pc;
@@ -13321,6 +13322,7 @@ static void disas_a64_insn(CPUARMState *env, DisasContext *s)
     uint32_t insn;
 
     insn = arm_ldl_code(env, s->pc, s->sctlr_b);
+    qemu_plugin_insn_append(tcg_ctx->plugin_insn, &insn, sizeof(insn));
     s->insn = insn;
     s->pc += 4;
 
diff --git a/target/arm/translate.c b/target/arm/translate.c
index 7c4675ffd8..7523257b85 100644
--- a/target/arm/translate.c
+++ b/target/arm/translate.c
@@ -36,6 +36,7 @@
 
 #include "trace-tcg.h"
 #include "exec/log.h"
+#include "qemu/plugin.h"
 
 
 #define ENABLE_ARCH_4T    arm_dc_feature(s, ARM_FEATURE_V4T)
@@ -13234,6 +13235,7 @@ static void arm_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu)
     }
 
     insn = arm_ldl_code(env, dc->pc, dc->sctlr_b);
+    qemu_plugin_insn_append(tcg_ctx->plugin_insn, &insn, sizeof(insn));
     dc->insn = insn;
     dc->pc += 4;
     disas_arm_insn(dc, insn);
@@ -13304,11 +13306,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;
+
+        qemu_plugin_insn_append(tcg_ctx->plugin_insn, &insn16, sizeof(insn16));
+    } else {
         uint32_t insn2 = arm_lduw_code(env, dc->pc, dc->sctlr_b);
 
         insn = insn << 16 | insn2;
         dc->pc += 2;
+        qemu_plugin_insn_append(tcg_ctx->plugin_insn, &insn, sizeof(insn));
     }
     dc->insn = insn;
 

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

* Re: [Qemu-devel] [RFC 38/48] translator: implement 2-pass translation
  2018-11-27 19:06         ` Emilio G. Cota
@ 2018-11-28  2:30           ` Emilio G. Cota
  2018-11-28 12:50             ` Alex Bennée
  0 siblings, 1 reply; 132+ messages in thread
From: Emilio G. Cota @ 2018-11-28  2:30 UTC (permalink / raw)
  To: Alex Bennée
  Cc: qemu-devel, Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Stefan Hajnoczi, Richard Henderson

On Tue, Nov 27, 2018 at 14:06:57 -0500, Emilio G. Cota wrote:
> On Tue, Nov 27, 2018 at 14:48:11 +0000, Alex Bennée wrote:
> > With a little tweaking to the TCG we could then insert
> > our instrumentation at the end of the pass with all the knowledge we
> > want to export to the plugin.
> 
> After .translate_insn has returned for the last instruction, how
> do we insert the instrumentation that the plugin wants--say, a TB
> callback at the beginning of the TB, memory callbacks for the
> 2nd instruction, and an insn callback before the 3rd instruction
> executes?
> 
> I don't see how we could achieve that with "a little tweaking"
> instead of a 2nd pass, but I'd love to be wrong =)

(snip)
> > I don't quite follow. When we've done all our translation into TCG ops
> > haven't we by definition defined the TB?
> 
> Yes, that's precisely my point.
> 
> The part that's missing is that once the TB is defined, we want to
> insert instrumentation. Unfortunately, the "TCG ops" we get after
> the 1st pass (no instrumentation), are very different from the list
> of "TCG ops" that we get after the 2nd pass (after having injected
> instrumentation). Could we get the same output of the 2nd pass,
> just by using the output of the 1st and the list of injection points?
> It's probably possible, but it seems very hard to do. (Think for
> instance of memory callbacks, and further the complication of when
> they use helpers).
> 
> The only reasonable way to do this I think would be to leave behind
> "placeholder" TCG ops, that then we could scan to add further TCG ops.
> But you'll agree with me that the 2nd pass is simpler :P

It might not be that much simpler after all!

I am exploring the approach you suggested, that is IIUC to do a
single pass and then walk the list of Ops, adding (and
reordering) Ops based on what the plugins have requested.

Contrary to what I wrote earlier today (quoted above), this
approach seems quite promising, and certainly less ugly
than doing the 2 passes.

I just wrote some code to go over the list and add TB callbacks,
which go right before the first insn_start Op. The code is hack-ish
in that we first generate the TCG ops we need, which get added to
the end of the ops list, and then we go over those and move them
to where we want them to be (before insn_start, in this case).
But it works and it's less of a hack than doing the whole 2nd pass.

Insn callbacks will be trivial to implement this way; memory
callbacks should be harder because there are several qemu_ld/st
opcodes, but it should be doable; last, memory instrumentation
of helpers might actually be easier than with the 2 passes, because here
we just have to look for a Call TCG op to know whether a guest
instruction uses helpers, and if it does we can wrap the call
with the helpers to generate the setting/resetting of
CPUState.plugin_mem_cbs.

I'll try to do what's in the previous paragraph tomorrow -- I'm
appending what I did so far.

Thanks,

		Emilio
---
diff --git a/accel/tcg/translator.c b/accel/tcg/translator.c
index ee9e179e14..232f645cd4 100644
--- a/accel/tcg/translator.c
+++ b/accel/tcg/translator.c
@@ -18,6 +18,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
@@ -142,6 +143,11 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
     gen_tb_end(db->tb, db->num_insns - bp_insn);
 
     if (plugin_enabled) {
+        /* collect the plugins' instrumentation */
+        qemu_plugin_tb_trans_cb(cpu, &tcg_ctx->plugin_tb);
+        /* inject instrumentation */
+        qemu_plugin_gen_inject();
+        /* done */
         tcg_ctx->plugin_insn = NULL;
     }
 
diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c
index 75f182be37..cb5dbadc3c 100644
--- a/accel/tcg/plugin-gen.c
+++ b/accel/tcg/plugin-gen.c
@@ -1,4 +1,5 @@
 #include "qemu/osdep.h"
+#include "qemu/queue.h"
 #include "cpu.h"
 #include "tcg/tcg.h"
 #include "tcg/tcg-op.h"
@@ -169,8 +170,61 @@ static void gen_vcpu_udata_cb(struct qemu_plugin_dyn_cb *cb)
     tcg_temp_free_i32(cpu_index);
 }
 
-void qemu_plugin_gen_vcpu_udata_callbacks(struct qemu_plugin_dyn_cb_arr *arr)
+/* check that @a comes before @b */
+static inline void ops_check(const TCGOp *a, const TCGOp *b)
 {
+    const TCGOp *op;
+    bool seen_a = false;
+    bool seen_b = false;
+
+    tcg_debug_assert(a != b);
+    QTAILQ_FOREACH(op, &tcg_ctx->ops, link) {
+        if (op == a) {
+            tcg_debug_assert(!seen_b);
+            seen_a = true;
+        } else if (op == b) {
+            tcg_debug_assert(seen_a);
+            seen_b = true;
+            break;
+        }
+    }
+}
+
+/*
+ * Move ops from @from to @dest.
+ * @from must come after @dest in the list.
+ */
+static void ops_move(TCGOp *dest, TCGOp *from)
+{
+    TCGOp *curr;
+
+#ifdef CONFIG_DEBUG_TCG
+    ops_check(dest, from);
+#endif
+
+   if (QTAILQ_NEXT(dest, link) == from) {
+        /* nothing to do */
+        return;
+    }
+    curr = from;
+    do {
+        TCGOp *next = QTAILQ_NEXT(curr, link);
+
+        /*
+         * This could be done more efficiently since (@from,end) will
+         * remain linked together, but most likely we just have a few
+         * elements, so this is simple enough.
+         */
+        QTAILQ_REMOVE(&tcg_ctx->ops, curr, link);
+        QTAILQ_INSERT_AFTER(&tcg_ctx->ops, dest, curr, link);
+        dest = curr;
+        curr = next;
+    } while (curr);
+}
+
+static void inject_vcpu_udata_callbacks(struct qemu_plugin_dyn_cb_arr *arr, TCGOp *dest)
+{
+    TCGOp *last_op = tcg_last_op();
     size_t i;
 
     for (i = 0; i < arr->n; i++) {
@@ -187,6 +241,10 @@ void qemu_plugin_gen_vcpu_udata_callbacks(struct qemu_plugin_dyn_cb_arr *arr)
             g_assert_not_reached();
         }
     }
+    g_assert(tcg_last_op() != last_op);
+    last_op = QTAILQ_NEXT(last_op, link);
+    g_assert(last_op);
+    ops_move(dest, last_op);
 }
 
 /*
@@ -228,3 +286,26 @@ void qemu_plugin_gen_disable_mem_helpers(void)
                                                         plugin_mem_cbs));
     tcg_temp_free_ptr(ptr);
 }
+
+void qemu_plugin_gen_inject(void)
+{
+    struct qemu_plugin_tb *plugin_tb = &tcg_ctx->plugin_tb;
+
+    /* TB exec callbacks */
+    if (plugin_tb->cbs.n) {
+        TCGOp *op;
+
+        /* Insert the callbacks right before the first insn_start */
+        QTAILQ_FOREACH(op, &tcg_ctx->ops, link) {
+            if (op->opc == INDEX_op_insn_start) {
+                break;
+            }
+        }
+        /* a TB without insn_start? Something went wrong */
+        g_assert(op);
+        op = QTAILQ_PREV(op, TCGOpHead, link);
+        /* we should have called gen_tb_start before the 1st insn */
+        g_assert(op);
+        inject_vcpu_udata_callbacks(&plugin_tb->cbs, op);
+    }
+}

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

* Re: [Qemu-devel] [RFC 41/48] configure: add --enable-plugins
  2018-11-27 23:13     ` Emilio G. Cota
@ 2018-11-28 10:43       ` Roman Bolshakov
  2018-11-28 17:23         ` Emilio G. Cota
  0 siblings, 1 reply; 132+ messages in thread
From: Roman Bolshakov @ 2018-11-28 10:43 UTC (permalink / raw)
  To: Emilio G. Cota
  Cc: Peter Maydell, Stefan Hajnoczi, qemu-devel, Pavel Dovgalyuk,
	Alex Bennée, Lluís Vilanova

On Tue, Nov 27, 2018 at 06:13:57PM -0500, Emilio G. Cota wrote:
> On Tue, Nov 27, 2018 at 15:43:52 +0300, Roman Bolshakov wrote:
> > ld64 on macOS has similar -exported_symbols_list option. Here's the reference:
> > 
> >      -exported_symbols_list filename
> > 	The specified filename contains a list of global symbol names
> > 	that will remain as global symbols in the output file.  All other
> > 	global symbols will be treated as if they were marked as
> > 	__private_extern__ (aka visibility=hidden) and will not be global in
> > 	the output file. The symbol names listed in filename must be one per
> > 	line.  Leading and trailing white space are not part of the symbol
> > 	name.  Lines starting with # are ignored, as are lines with only white
> > 	space.  Some wildcards (similar to shell file matching) are supported.
> > 	The * matches zero or more characters.  The ?  matches one character.
> > 	[abc] matches one character which must be an 'a', 'b', or 'c'.
> > 	[a-z] matches any single lower case letter from 'a' to 'z'.
> > 
> > 
> > I can try your branch if you add support of the linker flag or send required
> > changes via GitHub.
> 
> Can you please try this branch? I added a commit with the appended.
>   https://github.com/cota/qemu/tree/plugin-v2
> 
> You can inspect the symbols in the final binary with
> `readelf --dyn-syms' or `nm -D'.
> 
> Thanks,
> 
> 		Emilio
> 
> ---
> diff --git a/configure b/configure
> index fe9707d951..3dc9c9697b 100755
> --- a/configure
> +++ b/configure
> @@ -5176,15 +5176,31 @@ int main(void)
>  }
>  EOF
>  
> +ld_dynamic_list="no"
>  if compile_prog "" "-Wl,--dynamic-list=$TMPTXT" ; then
>    ld_dynamic_list="yes"
> -else
> -  if test "$plugins" = "yes" ; 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) used for this purpose."
> -  fi
> +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
>  
>  ########################################
> @@ -6827,7 +6843,18 @@ fi
>  if test "$plugins" = "yes" ; then
>      echo "CONFIG_PLUGINS=y" >> $config_host_mak
>      LIBS="-ldl $LIBS"
> -    LDFLAGS="-Wl,--dynamic-list=\$(SRC_PATH)/qemu-plugins.symbols $LDFLAGS"
> +    if test "$ld_dynamic_list" = "yes" ; then
> +	LDFLAGS="-Wl,--dynamic-list=\$(SRC_PATH)/qemu-plugins.symbols $LDFLAGS"
> +    elif test "$ld_exported_symbols_list" = "yes" ; then
> +	ld64_symbols=qemu-plugins-ld64.symbols
> +	echo "# Automatically generated by configure - do not modify" > $ld64_symbols
> +	cat "$source_path/qemu-plugins.symbols" | grep qemu_ | sed 's/;//g' >> $ld64_symbols
> +	LDFLAGS="-Wl,-exported_symbols_list,\$(BUILD_DIR)/$ld64_symbols $LDFLAGS"
> +    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
> 

Hi Emilio,

The test of -exported_symbols_list fails:
Undefined symbols for architecture x86_64:
  "foo", referenced from:
       -exported_symbol[s_list] command line option
            (maybe you meant: _foo)

All functions on macOS are prefixed with underscore [1]:
"The name of a symbol representing a function that conforms to standard C
calling conventions is the name of the function with an underscore prefix.
Thus, the name of the symbol representing the function main would be _main."

So it can be fixed by prepending foo and qemu-plugins-ld64.symbols with
underscore:

diff --git a/configure b/configure
index 3dc9c9697b..85dd15732c 100755
--- a/configure
+++ b/configure
@@ -5185,7 +5185,7 @@ fi
 # See if -exported_symbols_list is supported by the linker

 cat > $TMPTXT <<EOF
-  foo
+  _foo
 EOF

 ld_exported_symbols_list="no"
@@ -6848,7 +6848,7 @@ if test "$plugins" = "yes" ; then
     elif test "$ld_exported_symbols_list" = "yes" ; then
        ld64_symbols=qemu-plugins-ld64.symbols
        echo "# Automatically generated by configure - do not modify" > $ld64_symbols
-       cat "$source_path/qemu-plugins.symbols" | grep qemu_ | sed 's/;//g' >> $ld64_symbols
+        cat "$source_path/qemu-plugins.symbols" | sed -nE 's/^([[:space:]]+)(.+);/\1_\2/p' >> $ld64_symbols
        LDFLAGS="-Wl,-exported_symbols_list,\$(BUILD_DIR)/$ld64_symbols $LDFLAGS"
     else
        error_exit \

--

Then if I print globally defined functions I can see it in
config-temp/qemu-conf.exe:

nm -g -U config-temp/qemu-conf.exe
0000000100000f50 T _foo

qemu-ga fails to link because it doesn't have symbols declared in
qemu-plugins-ld64.symbols. Perhaps "-Wl,-exported_symbols_list" should
be applied only to qemu-system?

[1] https://developer.apple.com/library/archive/documentation/DeveloperTools/Conceptual/MachOTopics/1-Articles/executing_files.html#//apple_ref/doc/uid/TP40001829-97182-TPXREF112

Best regards,
Roman

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

* Re: [Qemu-devel] [RFC 48/48] plugin: add a couple of very simple examples
  2018-10-25 17:20 ` [Qemu-devel] [RFC 48/48] plugin: add a couple of very simple examples Emilio G. Cota
  2018-10-29 10:59   ` Pavel Dovgalyuk
@ 2018-11-28 11:28   ` Alex Bennée
  2018-11-28 14:48     ` Emilio G. Cota
  2018-11-29 20:45   ` Roman Bolshakov
  2 siblings, 1 reply; 132+ messages in thread
From: Alex Bennée @ 2018-11-28 11:28 UTC (permalink / raw)
  To: Emilio G. Cota
  Cc: qemu-devel, Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Stefan Hajnoczi


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

> Signed-off-by: Emilio G. Cota <cota@braap.org>
> ---
>  plugin-examples/bbcount_avgsize_racy.c | 50 ++++++++++++++++++++++
>  plugin-examples/mem_count_racy_both.c  | 58 ++++++++++++++++++++++++++
>  plugin-examples/Makefile               | 31 ++++++++++++++

So I think we need to be putting these somewhere else and also building
the examples by default. As plugins only make sense with tcg guests
maybe a layout like:

  tcg/plugins/plugin.c
  tcg/plugins/examples/

>  3 files changed, 139 insertions(+)
>  create mode 100644 plugin-examples/bbcount_avgsize_racy.c
>  create mode 100644 plugin-examples/mem_count_racy_both.c
>  create mode 100644 plugin-examples/Makefile
>
> diff --git a/plugin-examples/bbcount_avgsize_racy.c b/plugin-examples/bbcount_avgsize_racy.c
> new file mode 100644
> index 0000000000..ccdf96c1fa
> --- /dev/null
> +++ b/plugin-examples/bbcount_avgsize_racy.c
> @@ -0,0 +1,50 @@
> +#include <inttypes.h>
> +#include <assert.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <unistd.h>
> +#include <stdio.h>
> +
> +#include <qemu-plugin.h>

-#include <qemu-plugin.h>
+#include <plugin-api.h>

> +
> +static uint64_t bb_count;
> +static uint64_t insn_count;
> +const char *filename;
> +static int stdout_fd;
> +
> +static void plugin_exit(qemu_plugin_id_t id, void *p)
> +{
> +    dprintf(stdout_fd, "insns: %" PRIu64", bb: %" PRIu64 ", "
> +            "avg insns/bb: %.2f\n",
> +            insn_count, bb_count, (double)insn_count / bb_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);
> +
> +    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)
> +{
> +    /* plugin_exit might write to stdout after stdout has been closed */
> +    stdout_fd = dup(STDOUT_FILENO);
> +    assert(stdout_fd);
> +
> +    qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans);
> +    qemu_plugin_register_atexit_cb(id, plugin_exit, NULL);
> +    return 0;
> +}
> diff --git a/plugin-examples/mem_count_racy_both.c b/plugin-examples/mem_count_racy_both.c
> new file mode 100644
> index 0000000000..a47f2025bf
> --- /dev/null
> +++ b/plugin-examples/mem_count_racy_both.c
> @@ -0,0 +1,58 @@
> +#include <inttypes.h>
> +#include <assert.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <unistd.h>
> +#include <stdio.h>
> +
> +#include <qemu-plugin.h>

-#include <qemu-plugin.h>
+#include <plugin-api.h>

> +
> +static uint64_t mem_count;
> +static int stdout_fd;
> +static bool do_inline;
> +
> +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_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,
> +                                                 QEMU_PLUGIN_INLINE_ADD_U64,
> +                                                 &mem_count, 1);
> +        } else {
> +            qemu_plugin_register_vcpu_mem_cb(insn, vcpu_mem,
> +                                             QEMU_PLUGIN_CB_NO_REGS, NULL);
> +        }
> +    }
> +}
> +
> +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;
> +    }
> +    /* plugin_exit might write to stdout after stdout has been closed */
> +    stdout_fd = dup(STDOUT_FILENO);
> +    assert(stdout_fd);
> +
> +    qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans);
> +    qemu_plugin_register_atexit_cb(id, plugin_exit, NULL);
> +    return 0;
> +}
> diff --git a/plugin-examples/Makefile b/plugin-examples/Makefile
> new file mode 100644
> index 0000000000..71bbcda7a8
> --- /dev/null
> +++ b/plugin-examples/Makefile
> @@ -0,0 +1,31 @@
> +NAMES :=
> +NAMES += bbcount_avgsize_racy
> +NAMES += mem_count_racy_both
> +
> +SONAMES := $(addsuffix .so,$(addprefix lib,$(NAMES)))
> +
> +# QEMU installed path, set by --prefix during configure
> +QEMU_PATH ?= /data/src/qemu-inst/plugin-test

I guess we should use SRC_PATH (or maybe QEMU_SRC_PATH) if we want the
Makefile to be both invoked as a sub-make from a normal QEMU build and
to be used as an example for out-of-tree builds.

> +
> +CC := gcc
> +CFLAGS :=
> +CFLAGS += -O2 -Werror -Wall
> +CFLAGS += -Wundef -Wwrite-strings -Wmissing-prototypes
> +CFLAGS += -Wstrict-prototypes -Wredundant-decls
> +CFLAGS += -fno-strict-aliasing -fno-common -fwrapv
> +CFLAGS += -I$(QEMU_PATH)/include

-CFLAGS += -I$(QEMU_PATH)/include
+CFLAGS += -I$(QEMU_PATH)/include/qemu

--
Alex Bennée

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

* Re: [Qemu-devel] [RFC 23/48] translator: add plugin_insn argument to translate_insn
  2018-11-28  0:54             ` Emilio G. Cota
  2018-11-28  1:12               ` Emilio G. Cota
@ 2018-11-28 12:40               ` Alex Bennée
  2018-11-28 14:43                 ` Emilio G. Cota
  1 sibling, 1 reply; 132+ messages in thread
From: Alex Bennée @ 2018-11-28 12:40 UTC (permalink / raw)
  To: Emilio G. Cota
  Cc: Richard Henderson, Peter Maydell, Stefan Hajnoczi, qemu-devel,
	Pavel Dovgalyuk, Lluís Vilanova


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

> On Mon, Nov 26, 2018 at 20:38:25 -0500, Emilio G. Cota wrote:
>> On Mon, Nov 26, 2018 at 11:30:25 -0800, Richard Henderson wrote:
>> > On 11/26/18 11:07 AM, Emilio G. Cota wrote:
>> > > The main reason why I added the qemu_plugin_insn_append calls
>> > > was to avoid reading the instructions twice from guest memory,
>> > > because I was worried that doing so might somehow alter the
>> > > guest's execution, e.g. what if we read a cross-page instruction,
>> > > and both pages mapped to the same TLB entry? We'd end up having
>> > > more TLB misses because instrumentation was enabled.
>> >
>> > A better solution for this, I think is to change direct calls from
>> >
>> >   cpu_ldl_code(env, pc);
>> > to
>> >   translator_ldl_code(dc_base, env, pc);
>> >
>> > instead of passing around a new argument separate from DisasContextBase?
>>
>> I think this + diff'ing pc_next should work to figure out the
>> contents and size of each instruction.
>
> I just tried doing things this way.
>
> For some targets like i386, the translator_ld* helpers work
> great; the instruction contents are copied, and through
> the helpers we get the sizes of the instructions as well.
>
> For ARM though (and maybe others, I haven't gone
> through all of them yet), arm_ldl_code does the following:
>
> /* Load an instruction and return it in the standard little-endian order */
> static inline uint32_t arm_ldl_code(CPUARMState *env, target_ulong addr,
>                                     bool sctlr_b)
> {
>     uint32_t insn = cpu_ldl_code(env, addr);
>     if (bswap_code(sctlr_b)) {
>         return bswap32(insn);
>     }
>     return insn;
> }
>
> To avoid altering the signature of .translate_insn, I've modified
> arm_ldl_code directly, as follows:
>
>      uint32_t insn = cpu_ldl_code(env, addr);
> +
>      if (bswap_code(sctlr_b)) {
> -        return bswap32(insn);
> +        insn = bswap32(insn);
> +    }
> +    if (tcg_ctx->plugin_insn) {
> +        qemu_plugin_insn_append(tcg_ctx->plugin_insn, &insn, sizeof(insn));
>      }
>      return insn;
>  }
>
> (NB. tcg_ctx->plugin_insn is updated by translator_loop
> on every iteration.)
>
> Let me know if you think I should do this differently.

I was envisioning something more like the following so all the plugin
gubins could be kept in the core code:

accel/tcg/translator.c    | 25 +++++++++++++++++++++++++
include/exec/translator.h | 10 ++++++++++
target/arm/arm_ldst.h     | 16 +++-------------

modified   accel/tcg/translator.c
@@ -10,6 +10,7 @@
 #include "qemu/osdep.h"
 #include "qemu-common.h"
 #include "qemu/error-report.h"
+#include "qemu/bswap.h"
 #include "cpu.h"
 #include "tcg/tcg.h"
 #include "tcg/tcg-op.h"
@@ -18,6 +19,30 @@
 #include "exec/log.h"
 #include "exec/translator.h"
 #include "exec/plugin-gen.h"
+#include "exec/cpu_ldst.h"
+
+/*
+ * Translator Code Load Helpers
+ */
+
+uint16_t translator_ld16(CPUArchState *env, target_ulong ptr, bool bswap)
+{
+    uint16_t insn = cpu_lduw_code(env, ptr);
+    if (bswap) {
+        insn = bswap16(insn);
+    }
+    return insn;
+}
+
+uint32_t translator_ld32(CPUArchState *env, target_ulong ptr, bool bswap)
+{
+    uint32_t insn = cpu_ldl_code(env, ptr);
+    if (bswap) {
+        insn = bswap32(insn);
+    }
+    return insn;
+}
+

 /* Pairs with tcg_clear_temp_count.
    To be called by #TranslatorOps.{translate_insn,tb_stop} if
modified   include/exec/translator.h
@@ -147,4 +147,14 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,

 void translator_loop_temp_check(DisasContextBase *db);

+/*
+ * Translator Code Load Helpers
+ *
+ * These allow the core translator code to track where code is being
+ * loaded from.
+ */
+
+uint16_t translator_ld16(CPUArchState *env, target_ulong ptr, bool bswap);
+uint32_t translator_ld32(CPUArchState *env, target_ulong ptr, bool bswap);
+
 #endif  /* EXEC__TRANSLATOR_H */
modified   target/arm/arm_ldst.h
@@ -20,25 +20,19 @@
 #ifndef ARM_LDST_H
 #define ARM_LDST_H

-#include "exec/cpu_ldst.h"
-#include "qemu/bswap.h"
+#include "exec/translator.h"

 /* Load an instruction and return it in the standard little-endian order */
 static inline uint32_t arm_ldl_code(CPUARMState *env, target_ulong addr,
                                     bool sctlr_b)
 {
-    uint32_t insn = cpu_ldl_code(env, addr);
-    if (bswap_code(sctlr_b)) {
-        return bswap32(insn);
-    }
-    return insn;
+    return translator_ld32(env, addr, bswap_code(sctlr_b));
 }

 /* Ditto, for a halfword (Thumb) instruction */
 static inline uint16_t arm_lduw_code(CPUARMState *env, target_ulong addr,
                                      bool sctlr_b)
 {
-    uint16_t insn;
 #ifndef CONFIG_USER_ONLY
     /* In big-endian (BE32) mode, adjacent Thumb instructions have been swapped
        within each word.  Undo that now.  */
@@ -46,11 +40,7 @@ static inline uint16_t arm_lduw_code(CPUARMState *env, target_ulong addr,
         addr ^= 2;
     }
 #endif
-    insn = cpu_lduw_code(env, addr);
-    if (bswap_code(sctlr_b)) {
-        return bswap16(insn);
-    }
-    return insn;
+    return translator_ld16(env, addr, bswap_code(sctlr_b));
 }

 #endif

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

* Re: [Qemu-devel] [RFC 38/48] translator: implement 2-pass translation
  2018-11-28  2:30           ` Emilio G. Cota
@ 2018-11-28 12:50             ` Alex Bennée
  2018-11-28 15:03               ` Emilio G. Cota
  0 siblings, 1 reply; 132+ messages in thread
From: Alex Bennée @ 2018-11-28 12:50 UTC (permalink / raw)
  To: Emilio G. Cota
  Cc: qemu-devel, Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Stefan Hajnoczi, Richard Henderson


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

> On Tue, Nov 27, 2018 at 14:06:57 -0500, Emilio G. Cota wrote:
>> On Tue, Nov 27, 2018 at 14:48:11 +0000, Alex Bennée wrote:
>> > With a little tweaking to the TCG we could then insert
>> > our instrumentation at the end of the pass with all the knowledge we
>> > want to export to the plugin.
>>
>> After .translate_insn has returned for the last instruction, how
>> do we insert the instrumentation that the plugin wants--say, a TB
>> callback at the beginning of the TB, memory callbacks for the
>> 2nd instruction, and an insn callback before the 3rd instruction
>> executes?
>>
>> I don't see how we could achieve that with "a little tweaking"
>> instead of a 2nd pass, but I'd love to be wrong =)
>
> (snip)
>> > I don't quite follow. When we've done all our translation into TCG ops
>> > haven't we by definition defined the TB?
>>
>> Yes, that's precisely my point.
>>
>> The part that's missing is that once the TB is defined, we want to
>> insert instrumentation. Unfortunately, the "TCG ops" we get after
>> the 1st pass (no instrumentation), are very different from the list
>> of "TCG ops" that we get after the 2nd pass (after having injected
>> instrumentation). Could we get the same output of the 2nd pass,
>> just by using the output of the 1st and the list of injection points?
>> It's probably possible, but it seems very hard to do. (Think for
>> instance of memory callbacks, and further the complication of when
>> they use helpers).
>>
>> The only reasonable way to do this I think would be to leave behind
>> "placeholder" TCG ops, that then we could scan to add further TCG ops.
>> But you'll agree with me that the 2nd pass is simpler :P
>
> It might not be that much simpler after all!
>
> I am exploring the approach you suggested, that is IIUC to do a
> single pass and then walk the list of Ops, adding (and
> reordering) Ops based on what the plugins have requested.
>
> Contrary to what I wrote earlier today (quoted above), this
> approach seems quite promising, and certainly less ugly
> than doing the 2 passes.
>
> I just wrote some code to go over the list and add TB callbacks,
> which go right before the first insn_start Op. The code is hack-ish
> in that we first generate the TCG ops we need, which get added to
> the end of the ops list, and then we go over those and move them
> to where we want them to be (before insn_start, in this case).
> But it works and it's less of a hack than doing the whole 2nd pass.

But we should be able to insert the ops directly in the right place.
That is the whole point of being a list right ;-)

> Insn callbacks will be trivial to implement this way; memory
> callbacks should be harder because there are several qemu_ld/st
> opcodes, but it should be doable;

I was thinking about this last night. I wonder if we need to tag the
memory tcg ops so we can find them afterwards during the insertion
phase - but maybe the opcode itself provides enough information.

> last, memory instrumentation
> of helpers might actually be easier than with the 2 passes, because here
> we just have to look for a Call TCG op to know whether a guest
> instruction uses helpers, and if it does we can wrap the call
> with the helpers to generate the setting/resetting of
> CPUState.plugin_mem_cbs.

So merging the two helper calls into one from the target code?

>
> I'll try to do what's in the previous paragraph tomorrow -- I'm
> appending what I did so far.
>
> Thanks,
>
> 		Emilio
> ---
> diff --git a/accel/tcg/translator.c b/accel/tcg/translator.c
> index ee9e179e14..232f645cd4 100644
> --- a/accel/tcg/translator.c
> +++ b/accel/tcg/translator.c
> @@ -18,6 +18,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
> @@ -142,6 +143,11 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
>      gen_tb_end(db->tb, db->num_insns - bp_insn);
>
>      if (plugin_enabled) {
> +        /* collect the plugins' instrumentation */
> +        qemu_plugin_tb_trans_cb(cpu, &tcg_ctx->plugin_tb);
> +        /* inject instrumentation */
> +        qemu_plugin_gen_inject();
> +        /* done */
>          tcg_ctx->plugin_insn = NULL;
>      }
>
> diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c
> index 75f182be37..cb5dbadc3c 100644
> --- a/accel/tcg/plugin-gen.c
> +++ b/accel/tcg/plugin-gen.c
> @@ -1,4 +1,5 @@
>  #include "qemu/osdep.h"
> +#include "qemu/queue.h"
>  #include "cpu.h"
>  #include "tcg/tcg.h"
>  #include "tcg/tcg-op.h"
> @@ -169,8 +170,61 @@ static void gen_vcpu_udata_cb(struct qemu_plugin_dyn_cb *cb)
>      tcg_temp_free_i32(cpu_index);
>  }
>
> -void qemu_plugin_gen_vcpu_udata_callbacks(struct qemu_plugin_dyn_cb_arr *arr)
> +/* check that @a comes before @b */
> +static inline void ops_check(const TCGOp *a, const TCGOp *b)
>  {
> +    const TCGOp *op;
> +    bool seen_a = false;
> +    bool seen_b = false;
> +
> +    tcg_debug_assert(a != b);
> +    QTAILQ_FOREACH(op, &tcg_ctx->ops, link) {
> +        if (op == a) {
> +            tcg_debug_assert(!seen_b);
> +            seen_a = true;
> +        } else if (op == b) {
> +            tcg_debug_assert(seen_a);
> +            seen_b = true;
> +            break;
> +        }
> +    }
> +}
> +
> +/*
> + * Move ops from @from to @dest.
> + * @from must come after @dest in the list.
> + */
> +static void ops_move(TCGOp *dest, TCGOp *from)
> +{
> +    TCGOp *curr;
> +
> +#ifdef CONFIG_DEBUG_TCG
> +    ops_check(dest, from);
> +#endif
> +
> +   if (QTAILQ_NEXT(dest, link) == from) {
> +        /* nothing to do */
> +        return;
> +    }
> +    curr = from;
> +    do {
> +        TCGOp *next = QTAILQ_NEXT(curr, link);
> +
> +        /*
> +         * This could be done more efficiently since (@from,end) will
> +         * remain linked together, but most likely we just have a few
> +         * elements, so this is simple enough.
> +         */
> +        QTAILQ_REMOVE(&tcg_ctx->ops, curr, link);
> +        QTAILQ_INSERT_AFTER(&tcg_ctx->ops, dest, curr, link);
> +        dest = curr;
> +        curr = next;
> +    } while (curr);
> +}
> +
> +static void inject_vcpu_udata_callbacks(struct qemu_plugin_dyn_cb_arr *arr, TCGOp *dest)
> +{
> +    TCGOp *last_op = tcg_last_op();
>      size_t i;
>
>      for (i = 0; i < arr->n; i++) {
> @@ -187,6 +241,10 @@ void qemu_plugin_gen_vcpu_udata_callbacks(struct qemu_plugin_dyn_cb_arr *arr)
>              g_assert_not_reached();
>          }
>      }
> +    g_assert(tcg_last_op() != last_op);
> +    last_op = QTAILQ_NEXT(last_op, link);
> +    g_assert(last_op);
> +    ops_move(dest, last_op);
>  }
>
>  /*
> @@ -228,3 +286,26 @@ void qemu_plugin_gen_disable_mem_helpers(void)
>                                                          plugin_mem_cbs));
>      tcg_temp_free_ptr(ptr);
>  }
> +
> +void qemu_plugin_gen_inject(void)
> +{
> +    struct qemu_plugin_tb *plugin_tb = &tcg_ctx->plugin_tb;
> +
> +    /* TB exec callbacks */
> +    if (plugin_tb->cbs.n) {
> +        TCGOp *op;
> +
> +        /* Insert the callbacks right before the first insn_start */
> +        QTAILQ_FOREACH(op, &tcg_ctx->ops, link) {
> +            if (op->opc == INDEX_op_insn_start) {
> +                break;
> +            }
> +        }
> +        /* a TB without insn_start? Something went wrong */
> +        g_assert(op);
> +        op = QTAILQ_PREV(op, TCGOpHead, link);
> +        /* we should have called gen_tb_start before the 1st insn */
> +        g_assert(op);
> +        inject_vcpu_udata_callbacks(&plugin_tb->cbs, op);
> +    }
> +}


--
Alex Bennée

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

* Re: [Qemu-devel] [RFC 23/48] translator: add plugin_insn argument to translate_insn
  2018-11-28 12:40               ` Alex Bennée
@ 2018-11-28 14:43                 ` Emilio G. Cota
  0 siblings, 0 replies; 132+ messages in thread
From: Emilio G. Cota @ 2018-11-28 14:43 UTC (permalink / raw)
  To: Alex Bennée
  Cc: Richard Henderson, Peter Maydell, Stefan Hajnoczi, qemu-devel,
	Pavel Dovgalyuk, Lluís Vilanova

On Wed, Nov 28, 2018 at 12:40:23 +0000, Alex Bennée wrote:
> I was envisioning something more like the following so all the plugin
> gubins could be kept in the core code:
(snip)
>  static inline uint32_t arm_ldl_code(CPUARMState *env, target_ulong addr,
>                                      bool sctlr_b)
>  {
> -    uint32_t insn = cpu_ldl_code(env, addr);
> -    if (bswap_code(sctlr_b)) {
> -        return bswap32(insn);
> -    }
> -    return insn;
> +    return translator_ld32(env, addr, bswap_code(sctlr_b));
>  }
> 
>  /* Ditto, for a halfword (Thumb) instruction */
>  static inline uint16_t arm_lduw_code(CPUARMState *env, target_ulong addr,
>                                       bool sctlr_b)
>  {
> -    uint16_t insn;
>  #ifndef CONFIG_USER_ONLY
>      /* In big-endian (BE32) mode, adjacent Thumb instructions have been swapped
>         within each word.  Undo that now.  */
> @@ -46,11 +40,7 @@ static inline uint16_t arm_lduw_code(CPUARMState *env, target_ulong addr,
>          addr ^= 2;
>      }
>  #endif
> -    insn = cpu_lduw_code(env, addr);
> -    if (bswap_code(sctlr_b)) {
> -        return bswap16(insn);
> -    }
> -    return insn;
> +    return translator_ld16(env, addr, bswap_code(sctlr_b));
>  }

I like this, thanks.

However, for Thumb I think we still need to call qemu_plugin_insn_append
directly:

@@ -13304,11 +13306,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;
+
+        qemu_plugin_insn_append(tcg_ctx->plugin_insn, &insn16, sizeof(insn16));
+    } else {
         uint32_t insn2 = arm_lduw_code(env, dc->pc, dc->sctlr_b);

         insn = insn << 16 | insn2;
         dc->pc += 2;
+        qemu_plugin_insn_append(tcg_ctx->plugin_insn, &insn, sizeof(insn));
     }
 
Otherwise we might mess up the contents of 32-bit insns.

Thanks,

		E.

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

* Re: [Qemu-devel] [RFC 48/48] plugin: add a couple of very simple examples
  2018-11-28 11:28   ` Alex Bennée
@ 2018-11-28 14:48     ` Emilio G. Cota
  0 siblings, 0 replies; 132+ messages in thread
From: Emilio G. Cota @ 2018-11-28 14:48 UTC (permalink / raw)
  To: Alex Bennée
  Cc: qemu-devel, Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Stefan Hajnoczi

On Wed, Nov 28, 2018 at 11:28:11 +0000, Alex Bennée wrote:
> 
> Emilio G. Cota <cota@braap.org> writes:
> 
> > Signed-off-by: Emilio G. Cota <cota@braap.org>
> > ---
> >  plugin-examples/bbcount_avgsize_racy.c | 50 ++++++++++++++++++++++
> >  plugin-examples/mem_count_racy_both.c  | 58 ++++++++++++++++++++++++++
> >  plugin-examples/Makefile               | 31 ++++++++++++++
> 
> So I think we need to be putting these somewhere else and also building
> the examples by default. As plugins only make sense with tcg guests

Most of plugin functionality is related to TCG, but some of
it is not. For instance, one could use a plugin to just control
the guest clock, and nothing else. This would work with KVM.

This is why I think $SRC_PATH/plugin.c makes sense.

Wrt the examples, I just included them here for reviewing purposes.
If we end up adding them, I think tests/plugin[s] would be an appropriate
place for them.

> > +# QEMU installed path, set by --prefix during configure
> > +QEMU_PATH ?= /data/src/qemu-inst/plugin-test
> 
> I guess we should use SRC_PATH (or maybe QEMU_SRC_PATH) if we want the
> Makefile to be both invoked as a sub-make from a normal QEMU build and
> to be used as an example for out-of-tree builds.

(As I said, this was a quick hack just to get the RFC out :P)

Thanks,

		Emilio

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

* Re: [Qemu-devel] [RFC 38/48] translator: implement 2-pass translation
  2018-11-28 12:50             ` Alex Bennée
@ 2018-11-28 15:03               ` Emilio G. Cota
  0 siblings, 0 replies; 132+ messages in thread
From: Emilio G. Cota @ 2018-11-28 15:03 UTC (permalink / raw)
  To: Alex Bennée
  Cc: qemu-devel, Pavel Dovgalyuk, Lluís Vilanova, Peter Maydell,
	Stefan Hajnoczi, Richard Henderson

On Wed, Nov 28, 2018 at 12:50:23 +0000, Alex Bennée wrote:
> Emilio G. Cota <cota@braap.org> writes:
> > I just wrote some code to go over the list and add TB callbacks,
> > which go right before the first insn_start Op. The code is hack-ish
> > in that we first generate the TCG ops we need, which get added to
> > the end of the ops list, and then we go over those and move them
> > to where we want them to be (before insn_start, in this case).
> > But it works and it's less of a hack than doing the whole 2nd pass.
> 
> But we should be able to insert the ops directly in the right place.
> That is the whole point of being a list right ;-)

Right, it's just hard sometimes to know exactly where to insert.

> > Insn callbacks will be trivial to implement this way; memory
> > callbacks should be harder because there are several qemu_ld/st
> > opcodes, but it should be doable;
> 
> I was thinking about this last night. I wonder if we need to tag the
> memory tcg ops so we can find them afterwards during the insertion
> phase - but maybe the opcode itself provides enough information.

We should be able to extract the info from the memop argument,
I think.

> > last, memory instrumentation
> > of helpers might actually be easier than with the 2 passes, because here
> > we just have to look for a Call TCG op to know whether a guest
> > instruction uses helpers, and if it does we can wrap the call
> > with the helpers to generate the setting/resetting of
> > CPUState.plugin_mem_cbs.
> 
> So merging the two helper calls into one from the target code?

Actually we don't need helpers to set/reset CPUState.plugin_mem_cbs;
we do that in TCG directly. So here we could just add the "set"
code right after "insn_start", and the "reset" code at the very
end of the translation (right before tb_exit/goto_tb etc).
The "reset" might still be dead code, but that is unavoidable
because the helper might do a longjmp. But we can fix that by
resetting the variable when returning from the jump.

Thanks,

		E.

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

* Re: [Qemu-devel] [RFC 41/48] configure: add --enable-plugins
  2018-11-28 10:43       ` Roman Bolshakov
@ 2018-11-28 17:23         ` Emilio G. Cota
  2018-11-29  9:57           ` Roman Bolshakov
  0 siblings, 1 reply; 132+ messages in thread
From: Emilio G. Cota @ 2018-11-28 17:23 UTC (permalink / raw)
  To: Roman Bolshakov
  Cc: Peter Maydell, Stefan Hajnoczi, qemu-devel, Pavel Dovgalyuk,
	Alex Bennée, Lluís Vilanova

On Wed, Nov 28, 2018 at 13:43:30 +0300, Roman Bolshakov wrote:
> The test of -exported_symbols_list fails:
> Undefined symbols for architecture x86_64:
>   "foo", referenced from:
>        -exported_symbol[s_list] command line option
>             (maybe you meant: _foo)
> 
> All functions on macOS are prefixed with underscore [1]:
> "The name of a symbol representing a function that conforms to standard C
> calling conventions is the name of the function with an underscore prefix.
> Thus, the name of the symbol representing the function main would be _main."
> 
> So it can be fixed by prepending foo and qemu-plugins-ld64.symbols with
> underscore:

Ah I see, thanks!

(snip)
> qemu-ga fails to link because it doesn't have symbols declared in
> qemu-plugins-ld64.symbols. Perhaps "-Wl,-exported_symbols_list" should
> be applied only to qemu-system?

I just pushed to the same github branch the appended fixup.
The idea is to only add the linker flags when the output
binary links plugin.o.

Can you please test?

Thanks,

		Emilio

---
commit 8f45416b79765b66e5ce0fca7db93b97bbcfcfbb
Author: Emilio G. Cota <cota@braap.org>
Date:   Wed Nov 28 12:11:23 2018 -0500

    configure: ld64 fixup
    
    And copy the file to the build dir also for !ld64.
    
    Signed-off-by: Emilio G. Cota <cota@braap.org>

diff --git a/Makefile.target b/Makefile.target
index 719699696d..7ea17d71cb 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -107,7 +107,23 @@ obj-y += target/$(TARGET_BASE_ARCH)/
 obj-y += disas.o
 obj-$(call notempty,$(TARGET_XML_FILES)) += gdbstub-xml.o
 
-obj-$(CONFIG_PLUGINS) += plugin.o
+ifdef CONFIG_PLUGINS
+obj-y += plugin.o
+# Abuse -libs suffix to only link with --dynamic-list/-exported_symbols_list
+# when the final binary includes the plugin object.
+#
+# Note that simply setting LDFLAGS is not enough: we build binaries that
+# never link plugin.o, and the linker might fail (at least ld64 does)
+# if the symbols in the list are not in the output binary.
+ ifdef CONFIG_HAS_LD_DYNAMIC_LIST
+ plugin.o-libs := -Wl,--dynamic-list=$(BUILD_DIR)/qemu-plugins-ld.symbols
+ else
+  ifdef CONFIG_HAS_LD_EXPORTED_SYMBOLS_LIST
+  plugin.o-libs := \
+	-Wl,-exported_symbols_list,$(BUILD_DIR)/qemu-plugins-ld64.symbols
+  endif
+ endif
+endif
 
 #########################################################
 # Linux user emulator target
diff --git a/configure b/configure
index 3dc9c9697b..395acf831e 100755
--- a/configure
+++ b/configure
@@ -5185,7 +5185,7 @@ fi
 # See if -exported_symbols_list is supported by the linker
 
 cat > $TMPTXT <<EOF
-  foo
+  _foo
 EOF
 
 ld_exported_symbols_list="no"
@@ -6843,13 +6843,17 @@ fi
 if test "$plugins" = "yes" ; then
     echo "CONFIG_PLUGINS=y" >> $config_host_mak
     LIBS="-ldl $LIBS"
+    # Copy the export object list to the build dir
     if test "$ld_dynamic_list" = "yes" ; then
-	LDFLAGS="-Wl,--dynamic-list=\$(SRC_PATH)/qemu-plugins.symbols $LDFLAGS"
+	echo "CONFIG_HAS_LD_DYNAMIC_LIST=yes" >> $config_host_mak
+	ld_symbols=qemu-plugins-ld.symbols
+	cp "$source_path/qemu-plugins.symbols" $ld_symbols
     elif test "$ld_exported_symbols_list" = "yes" ; then
+	echo "CONFIG_HAS_LD_EXPORTED_SYMBOLS_LIST=yes" >> $config_host_mak
 	ld64_symbols=qemu-plugins-ld64.symbols
 	echo "# Automatically generated by configure - do not modify" > $ld64_symbols
-	cat "$source_path/qemu-plugins.symbols" | grep qemu_ | sed 's/;//g' >> $ld64_symbols
-	LDFLAGS="-Wl,-exported_symbols_list,\$(BUILD_DIR)/$ld64_symbols $LDFLAGS"
+	grep 'qemu_' "$source_path/qemu-plugins.symbols" | sed 's/;//g' | \
+	    sed -E 's/^\s*(.*)/_\1/' >> $ld64_symbols
     else
 	error_exit \
 	    "If \$plugins=yes, either \$ld_dynamic_list or " \

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

* Re: [Qemu-devel] [RFC 41/48] configure: add --enable-plugins
  2018-11-28 17:23         ` Emilio G. Cota
@ 2018-11-29  9:57           ` Roman Bolshakov
  2018-11-29 17:00             ` Emilio G. Cota
  0 siblings, 1 reply; 132+ messages in thread
From: Roman Bolshakov @ 2018-11-29  9:57 UTC (permalink / raw)
  To: Emilio G. Cota
  Cc: Peter Maydell, Stefan Hajnoczi, qemu-devel, Pavel Dovgalyuk,
	Alex Bennée, Lluís Vilanova

On Wed, Nov 28, 2018 at 12:23:32PM -0500, Emilio G. Cota wrote:
> On Wed, Nov 28, 2018 at 13:43:30 +0300, Roman Bolshakov wrote:
> > qemu-ga fails to link because it doesn't have symbols declared in
> > qemu-plugins-ld64.symbols. Perhaps "-Wl,-exported_symbols_list" should
> > be applied only to qemu-system?
> 
> I just pushed to the same github branch the appended fixup.
> The idea is to only add the linker flags when the output
> binary links plugin.o.
> 
> Can you please test?
> 
> Thanks,
> 
> 		Emilio
> 
> ---
> commit 8f45416b79765b66e5ce0fca7db93b97bbcfcfbb
> Author: Emilio G. Cota <cota@braap.org>
> Date:   Wed Nov 28 12:11:23 2018 -0500
> 
>     configure: ld64 fixup
>     
>     And copy the file to the build dir also for !ld64.
>     
>     Signed-off-by: Emilio G. Cota <cota@braap.org>
> 
> diff --git a/Makefile.target b/Makefile.target
> index 719699696d..7ea17d71cb 100644
> --- a/Makefile.target
> +++ b/Makefile.target
> @@ -107,7 +107,23 @@ obj-y += target/$(TARGET_BASE_ARCH)/
>  obj-y += disas.o
>  obj-$(call notempty,$(TARGET_XML_FILES)) += gdbstub-xml.o
>  
> -obj-$(CONFIG_PLUGINS) += plugin.o
> +ifdef CONFIG_PLUGINS
> +obj-y += plugin.o
> +# Abuse -libs suffix to only link with --dynamic-list/-exported_symbols_list
> +# when the final binary includes the plugin object.
> +#
> +# Note that simply setting LDFLAGS is not enough: we build binaries that
> +# never link plugin.o, and the linker might fail (at least ld64 does)
> +# if the symbols in the list are not in the output binary.
> + ifdef CONFIG_HAS_LD_DYNAMIC_LIST
> + plugin.o-libs := -Wl,--dynamic-list=$(BUILD_DIR)/qemu-plugins-ld.symbols
> + else
> +  ifdef CONFIG_HAS_LD_EXPORTED_SYMBOLS_LIST
> +  plugin.o-libs := \
> +	-Wl,-exported_symbols_list,$(BUILD_DIR)/qemu-plugins-ld64.symbols
> +  endif
> + endif
> +endif
>  
>  #########################################################
>  # Linux user emulator target
> diff --git a/configure b/configure
> index 3dc9c9697b..395acf831e 100755
> --- a/configure
> +++ b/configure
> @@ -5185,7 +5185,7 @@ fi
>  # See if -exported_symbols_list is supported by the linker
>  
>  cat > $TMPTXT <<EOF
> -  foo
> +  _foo
>  EOF
>  
>  ld_exported_symbols_list="no"
> @@ -6843,13 +6843,17 @@ fi
>  if test "$plugins" = "yes" ; then
>      echo "CONFIG_PLUGINS=y" >> $config_host_mak
>      LIBS="-ldl $LIBS"
> +    # Copy the export object list to the build dir
>      if test "$ld_dynamic_list" = "yes" ; then
> -	LDFLAGS="-Wl,--dynamic-list=\$(SRC_PATH)/qemu-plugins.symbols $LDFLAGS"
> +	echo "CONFIG_HAS_LD_DYNAMIC_LIST=yes" >> $config_host_mak
> +	ld_symbols=qemu-plugins-ld.symbols
> +	cp "$source_path/qemu-plugins.symbols" $ld_symbols
>      elif test "$ld_exported_symbols_list" = "yes" ; then
> +	echo "CONFIG_HAS_LD_EXPORTED_SYMBOLS_LIST=yes" >> $config_host_mak
>  	ld64_symbols=qemu-plugins-ld64.symbols
>  	echo "# Automatically generated by configure - do not modify" > $ld64_symbols
> -	cat "$source_path/qemu-plugins.symbols" | grep qemu_ | sed 's/;//g' >> $ld64_symbols
> -	LDFLAGS="-Wl,-exported_symbols_list,\$(BUILD_DIR)/$ld64_symbols $LDFLAGS"
> +	grep 'qemu_' "$source_path/qemu-plugins.symbols" | sed 's/;//g' | \
> +	    sed -E 's/^\s*(.*)/_\1/' >> $ld64_symbols
>      else
>  	error_exit \
>  	    "If \$plugins=yes, either \$ld_dynamic_list or " \
> 
Hi Emilio,

I think there's an issue with "\s" character class, it's not recognized
by macOS sed  and I'm getting incorrect lines in
qemu-plugins-ld64.symbols:
_  qemu_xxx
_  qemu_xyz

After I replaced "\s" with "[[:space:]]", linking proceeds further, but
doesn't succeed because of an unresolved reference for qemu-system cris,
lm32, m68k, microblaze, microblazeel, moxie, nios2, or1k, riscv32,
riscv64, sparc, unicore32, tricore, xtensa, xtensaeb:

Undefined symbols for architecture x86_64:
  "_pci_register_bar", referenced from:
      _plugin_chan_realize in plugin-chan.o

It probably has nothing to do with macOS per-se and shouldn't link on
Linux as well. If I disable the aforementioned targets the build
succeeds and I can see the symbols from qemu-plugins-ld64.symbols in
compiled qemu-system binaries.

Best regards,
Roman

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

* Re: [Qemu-devel] [RFC 41/48] configure: add --enable-plugins
  2018-11-29  9:57           ` Roman Bolshakov
@ 2018-11-29 17:00             ` Emilio G. Cota
  2018-11-29 17:49               ` Emilio G. Cota
  0 siblings, 1 reply; 132+ messages in thread
From: Emilio G. Cota @ 2018-11-29 17:00 UTC (permalink / raw)
  To: Roman Bolshakov
  Cc: Peter Maydell, Stefan Hajnoczi, qemu-devel, Pavel Dovgalyuk,
	Alex Bennée, Lluís Vilanova

On Thu, Nov 29, 2018 at 12:57:16 +0300, Roman Bolshakov wrote:
> Hi Emilio,
> 
> I think there's an issue with "\s" character class, it's not recognized
> by macOS sed  and I'm getting incorrect lines in
> qemu-plugins-ld64.symbols:
> _  qemu_xxx
> _  qemu_xyz
> 
> After I replaced "\s" with "[[:space:]]", linking proceeds further

Nice, thanks. Will update.

> , but doesn't succeed because of an unresolved reference for qemu-system cris,
> lm32, m68k, microblaze, microblazeel, moxie, nios2, or1k, riscv32,
> riscv64, sparc, unicore32, tricore, xtensa, xtensaeb:
> 
> Undefined symbols for architecture x86_64:
>   "_pci_register_bar", referenced from:
>       _plugin_chan_realize in plugin-chan.o
> 
> It probably has nothing to do with macOS per-se and shouldn't link on
> Linux as well. If I disable the aforementioned targets the build
> succeeds and I can see the symbols from qemu-plugins-ld64.symbols in
> compiled qemu-system binaries.

Yes, that's because plugin-chan should only be built if the guest has PCI
support. Will fix.

Thanks,

		Emilio

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

* Re: [Qemu-devel] [RFC 41/48] configure: add --enable-plugins
  2018-11-29 17:00             ` Emilio G. Cota
@ 2018-11-29 17:49               ` Emilio G. Cota
  2018-11-29 19:34                 ` Roman Bolshakov
  0 siblings, 1 reply; 132+ messages in thread
From: Emilio G. Cota @ 2018-11-29 17:49 UTC (permalink / raw)
  To: Roman Bolshakov
  Cc: Peter Maydell, Stefan Hajnoczi, qemu-devel, Pavel Dovgalyuk,
	Alex Bennée, Lluís Vilanova

On Thu, Nov 29, 2018 at 12:00:55 -0500, Emilio G. Cota wrote:
> On Thu, Nov 29, 2018 at 12:57:16 +0300, Roman Bolshakov wrote:
> > Hi Emilio,
> > 
> > I think there's an issue with "\s" character class, it's not recognized
> > by macOS sed  and I'm getting incorrect lines in
> > qemu-plugins-ld64.symbols:
> > _  qemu_xxx
> > _  qemu_xyz
> > 
> > After I replaced "\s" with "[[:space:]]", linking proceeds further
> 
> Nice, thanks. Will update.
> 
> > , but doesn't succeed because of an unresolved reference for qemu-system cris,
> > lm32, m68k, microblaze, microblazeel, moxie, nios2, or1k, riscv32,
> > riscv64, sparc, unicore32, tricore, xtensa, xtensaeb:
> > 
> > Undefined symbols for architecture x86_64:
> >   "_pci_register_bar", referenced from:
> >       _plugin_chan_realize in plugin-chan.o
> > 
> > It probably has nothing to do with macOS per-se and shouldn't link on
> > Linux as well. If I disable the aforementioned targets the build
> > succeeds and I can see the symbols from qemu-plugins-ld64.symbols in
> > compiled qemu-system binaries.
> 
> Yes, that's because plugin-chan should only be built if the guest has PCI
> support. Will fix.

Pushed the fixes to the github branch. Hope it works for you now!

Thanks,

		Emilio

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

* Re: [Qemu-devel] [RFC 41/48] configure: add --enable-plugins
  2018-11-29 17:49               ` Emilio G. Cota
@ 2018-11-29 19:34                 ` Roman Bolshakov
  0 siblings, 0 replies; 132+ messages in thread
From: Roman Bolshakov @ 2018-11-29 19:34 UTC (permalink / raw)
  To: Emilio G. Cota
  Cc: Peter Maydell, Stefan Hajnoczi, qemu-devel, Pavel Dovgalyuk,
	Alex Bennée, Lluís Vilanova

On Thu, Nov 29, 2018 at 12:49:27PM -0500, Emilio G. Cota wrote:
> On Thu, Nov 29, 2018 at 12:00:55 -0500, Emilio G. Cota wrote:
> > On Thu, Nov 29, 2018 at 12:57:16 +0300, Roman Bolshakov wrote:
> > > Hi Emilio,
> > > 
> > > I think there's an issue with "\s" character class, it's not recognized
> > > by macOS sed  and I'm getting incorrect lines in
> > > qemu-plugins-ld64.symbols:
> > > _  qemu_xxx
> > > _  qemu_xyz
> > > 
> > > After I replaced "\s" with "[[:space:]]", linking proceeds further
> > 
> > Nice, thanks. Will update.
> > 
> > > , but doesn't succeed because of an unresolved reference for qemu-system cris,
> > > lm32, m68k, microblaze, microblazeel, moxie, nios2, or1k, riscv32,
> > > riscv64, sparc, unicore32, tricore, xtensa, xtensaeb:
> > > 
> > > Undefined symbols for architecture x86_64:
> > >   "_pci_register_bar", referenced from:
> > >       _plugin_chan_realize in plugin-chan.o
> > > 
> > > It probably has nothing to do with macOS per-se and shouldn't link on
> > > Linux as well. If I disable the aforementioned targets the build
> > > succeeds and I can see the symbols from qemu-plugins-ld64.symbols in
> > > compiled qemu-system binaries.
> > 
> > Yes, that's because plugin-chan should only be built if the guest has PCI
> > support. Will fix.
> 
> Pushed the fixes to the github branch. Hope it works for you now!
> 

Thank you Emilio,
the build succeded with the set of declared symbols exposed in
qemu-system.

I've just noticed qemu-plugins-ld64.symbols should be added to
.gitignore.

Best regards,
Roman

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

* Re: [Qemu-devel] [RFC 48/48] plugin: add a couple of very simple examples
  2018-10-25 17:20 ` [Qemu-devel] [RFC 48/48] plugin: add a couple of very simple examples Emilio G. Cota
  2018-10-29 10:59   ` Pavel Dovgalyuk
  2018-11-28 11:28   ` Alex Bennée
@ 2018-11-29 20:45   ` Roman Bolshakov
  2018-12-08 23:32     ` Emilio G. Cota
  2 siblings, 1 reply; 132+ messages in thread
From: Roman Bolshakov @ 2018-11-29 20:45 UTC (permalink / raw)
  To: Emilio G. Cota
  Cc: qemu-devel, Peter Maydell, Alex Bennée, Lluís Vilanova,
	Pavel Dovgalyuk, Stefan Hajnoczi

On Thu, Oct 25, 2018 at 01:20:57PM -0400, Emilio G. Cota wrote:
> +
> +lib%.so: %.o
> +	$(CC) -shared -Wl,-soname,$@ -o $@ $^ $(LDLIBS)

The rule should be a bit different for macOS:
%.bundle: %.o
       $(CC) -bundle -Wl,-bundle_loader,PATH_TO_QEMU_EXE -o $@ $^ $(LDLIBS)

"-bundle" flag is needed because macOS has two kinds of Mach-O
dynamically loaded executables:
 - dylib (MH_DYLIB) - it's like an ELF shared object used for dynamic
   linking. Can be loaded, preloaded for sake of function interposing
   but cannot be explicitly unloaded.
 - bundle (MH_BUNDLE) - similar to an ELF shared object used in plugin
   dlopen/dlclose scenario.

We can pick any (enabled in configure) qemu-system executable as
bundle_loader. We specify it to check if all symbols in a bundle,
including the ones coming from the executable are defined. FWIW, here's
the reference for bundle_loader flag:
  -bundle_loader executable
      This specifies the executable that will be loading the bundle
      output file being linked. Undefined symbols from the bundle are
      checked against the specified executable like it was one of the
      dynamic libraries the bundle was linked with


The ".bundle" extension is not required but IMO it feels more native to
the system than ".so".

I've checked both plugins can be loaded and print a line when QEMU exits.

Best regards,
Roman

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

* Re: [Qemu-devel] [RFC 48/48] plugin: add a couple of very simple examples
  2018-11-29 20:45   ` Roman Bolshakov
@ 2018-12-08 23:32     ` Emilio G. Cota
  0 siblings, 0 replies; 132+ messages in thread
From: Emilio G. Cota @ 2018-12-08 23:32 UTC (permalink / raw)
  To: Roman Bolshakov
  Cc: qemu-devel, Peter Maydell, Alex Bennée, Lluís Vilanova,
	Pavel Dovgalyuk, Stefan Hajnoczi

On Thu, Nov 29, 2018 at 23:45:18 +0300, Roman Bolshakov wrote:
> On Thu, Oct 25, 2018 at 01:20:57PM -0400, Emilio G. Cota wrote:
> > +
> > +lib%.so: %.o
> > +	$(CC) -shared -Wl,-soname,$@ -o $@ $^ $(LDLIBS)
> 
> The rule should be a bit different for macOS:
> %.bundle: %.o
>        $(CC) -bundle -Wl,-bundle_loader,PATH_TO_QEMU_EXE -o $@ $^ $(LDLIBS)
> 
> "-bundle" flag is needed because macOS has two kinds of Mach-O
> dynamically loaded executables:
>  - dylib (MH_DYLIB) - it's like an ELF shared object used for dynamic
>    linking. Can be loaded, preloaded for sake of function interposing
>    but cannot be explicitly unloaded.
>  - bundle (MH_BUNDLE) - similar to an ELF shared object used in plugin
>    dlopen/dlclose scenario.
> 
> We can pick any (enabled in configure) qemu-system executable as
> bundle_loader. We specify it to check if all symbols in a bundle,
> including the ones coming from the executable are defined. FWIW, here's
> the reference for bundle_loader flag:
>   -bundle_loader executable
>       This specifies the executable that will be loading the bundle
>       output file being linked. Undefined symbols from the bundle are
>       checked against the specified executable like it was one of the
>       dynamic libraries the bundle was linked with
> 
> 
> The ".bundle" extension is not required but IMO it feels more native to
> the system than ".so".

The goal of the examples is to be target-independent, so I'm not
convinced that we want to bury $PATH_TO_QEMU_EXE in the build recipe
(or get configure involved in this).

Since you say the "bundle" business isn't a requirement, I'll leave just
the .so rule in v2.

Thanks,

		Emilio

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

end of thread, other threads:[~2018-12-08 23:32 UTC | newest]

Thread overview: 132+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-10-25 17:20 [Qemu-devel] [RFC 00/48] Plugin support Emilio G. Cota
2018-10-25 17:20 ` [Qemu-devel] [RFC 01/48] cpu: introduce run_on_cpu_no_bql Emilio G. Cota
2018-11-14 11:30   ` Alex Bennée
2018-11-14 17:08     ` Emilio G. Cota
2018-11-14 18:23       ` Alex Bennée
2018-10-25 17:20 ` [Qemu-devel] [RFC 02/48] trace: expand mem_info:size_shift to 3 bits Emilio G. Cota
2018-11-14 13:03   ` Alex Bennée
2018-11-14 17:17     ` Emilio G. Cota
2018-10-25 17:20 ` [Qemu-devel] [RFC 03/48] tcg/README: fix typo s/afterwise/afterwards/ Emilio G. Cota
2018-11-14 13:03   ` Alex Bennée
2018-10-25 17:20 ` [Qemu-devel] [RFC 04/48] exec: introduce qemu_xxhash{2,4,5,6,7} Emilio G. Cota
2018-11-14 13:04   ` [Qemu-devel] [RFC 04/48] exec: introduce qemu_xxhash{2, 4, 5, 6, 7} Alex Bennée
2018-10-25 17:20 ` [Qemu-devel] [RFC 05/48] include: move exec/tb-hash-xx.h to qemu/xxhash.h Emilio G. Cota
2018-11-14 13:05   ` Alex Bennée
2018-10-25 17:20 ` [Qemu-devel] [RFC 06/48] tcg: use QHT for helper_table Emilio G. Cota
2018-11-14 14:41   ` Alex Bennée
2018-11-14 17:50     ` Emilio G. Cota
2018-11-14 16:11   ` Alex Bennée
2018-11-14 17:52     ` Emilio G. Cota
2018-10-25 17:20 ` [Qemu-devel] [RFC 07/48] tcg: export TCGHelperInfo Emilio G. Cota
2018-11-14 16:12   ` Alex Bennée
2018-10-25 17:20 ` [Qemu-devel] [RFC 08/48] tcg: export tcg_gen_runtime_helper Emilio G. Cota
2018-11-14 16:44   ` Alex Bennée
2018-10-25 17:20 ` [Qemu-devel] [RFC 09/48] tcg: reset runtime helpers when flushing the code cache Emilio G. Cota
2018-11-14 17:01   ` Alex Bennée
2018-11-14 17:59     ` Emilio G. Cota
2018-10-25 17:20 ` [Qemu-devel] [RFC 10/48] exec: export do_tb_flush Emilio G. Cota
2018-11-22 17:09   ` Alex Bennée
2018-11-23 23:19     ` Emilio G. Cota
2018-11-26 11:11       ` Alex Bennée
2018-11-26 23:56         ` Emilio G. Cota
2018-10-25 17:20 ` [Qemu-devel] [RFC 11/48] atomic_template: fix indentation in GEN_ATOMIC_HELPER Emilio G. Cota
2018-11-22 17:09   ` Alex Bennée
2018-10-25 17:20 ` [Qemu-devel] [RFC 12/48] atomic_template: define pre/post macros Emilio G. Cota
2018-11-22 17:12   ` Alex Bennée
2018-11-24  0:09     ` Emilio G. Cota
2018-11-26 11:21       ` Alex Bennée
2018-11-27  1:16         ` Emilio G. Cota
2018-10-25 17:20 ` [Qemu-devel] [RFC 13/48] xxhash: add qemu_xxhash8 Emilio G. Cota
2018-11-22 17:15   ` Alex Bennée
2018-11-23 22:34     ` Emilio G. Cota
2018-10-25 17:20 ` [Qemu-devel] [RFC 14/48] plugin: preliminary user-facing API Emilio G. Cota
2018-10-25 17:20 ` [Qemu-devel] [RFC 15/48] plugin: add core code Emilio G. Cota
2018-10-25 17:20 ` [Qemu-devel] [RFC 16/48] tcg: add plugin_mask to TB hash Emilio G. Cota
2018-11-23 16:52   ` Alex Bennée
2018-11-24  0:02     ` Emilio G. Cota
2018-10-25 17:20 ` [Qemu-devel] [RFC 17/48] plugin-gen: add TCG code generation helpers Emilio G. Cota
2018-10-25 17:20 ` [Qemu-devel] [RFC 18/48] tcg: add memory callbacks for plugins (WIP) Emilio G. Cota
2018-11-23 16:55   ` Alex Bennée
2018-10-25 17:20 ` [Qemu-devel] [RFC 19/48] translate-all: notify plugin code of tb_flush Emilio G. Cota
2018-11-23 17:00   ` Alex Bennée
2018-11-23 23:15     ` Emilio G. Cota
2018-11-26 11:02       ` Alex Bennée
2018-11-26 21:53         ` Emilio G. Cota
2018-10-25 17:20 ` [Qemu-devel] [RFC 20/48] *-user: notify plugin of exit Emilio G. Cota
2018-11-23 17:01   ` Alex Bennée
2018-10-25 17:20 ` [Qemu-devel] [RFC 21/48] *-user: plugin syscalls Emilio G. Cota
2018-11-23 17:04   ` Alex Bennée
2018-11-24  0:03     ` Emilio G. Cota
2018-10-25 17:20 ` [Qemu-devel] [RFC 22/48] cpu: hook plugin vcpu events Emilio G. Cota
2018-11-23 17:10   ` Alex Bennée
2018-11-23 23:48     ` Emilio G. Cota
2018-11-26 11:17       ` Alex Bennée
2018-11-27  1:25         ` Emilio G. Cota
2018-10-25 17:20 ` [Qemu-devel] [RFC 23/48] translator: add plugin_insn argument to translate_insn Emilio G. Cota
2018-11-26 14:52   ` Alex Bennée
2018-11-26 18:27     ` Richard Henderson
2018-11-26 18:56       ` Alex Bennée
2018-11-26 19:19         ` Richard Henderson
2018-11-26 19:07       ` Emilio G. Cota
2018-11-26 19:30         ` Richard Henderson
2018-11-27  1:38           ` Emilio G. Cota
2018-11-28  0:54             ` Emilio G. Cota
2018-11-28  1:12               ` Emilio G. Cota
2018-11-28 12:40               ` Alex Bennée
2018-11-28 14:43                 ` Emilio G. Cota
2018-11-27 13:08         ` Pavel Dovgalyuk
2018-10-25 17:20 ` [Qemu-devel] [RFC 24/48] translator: add .ctx_base_offset and .ctx_size to TranslatorOps Emilio G. Cota
2018-10-25 17:20 ` [Qemu-devel] [RFC 25/48] target/arm: prepare for 2-pass translation Emilio G. Cota
2018-10-25 17:20 ` [Qemu-devel] [RFC 26/48] target/ppc: " Emilio G. Cota
2018-10-25 17:20 ` [Qemu-devel] [RFC 27/48] target/sh4: prepare for 2-pass translation (WIP) Emilio G. Cota
2018-10-25 17:20 ` [Qemu-devel] [RFC 28/48] target/i386: prepare for 2-pass translation Emilio G. Cota
2018-10-25 17:20 ` [Qemu-devel] [RFC 29/48] target/hppa: " Emilio G. Cota
2018-10-25 17:20 ` [Qemu-devel] [RFC 30/48] target/m68k: " Emilio G. Cota
2018-10-25 17:20 ` [Qemu-devel] [RFC 31/48] target/mips: prepare for 2-pass translation (WIP) Emilio G. Cota
2018-10-25 17:20 ` [Qemu-devel] [RFC 32/48] target/alpha: prepare for 2-pass translation Emilio G. Cota
2018-10-25 17:20 ` [Qemu-devel] [RFC 33/48] target/riscv: " Emilio G. Cota
2018-10-25 17:20 ` [Qemu-devel] [RFC 34/48] target/s390x: " Emilio G. Cota
2018-10-25 17:20 ` [Qemu-devel] [RFC 35/48] target/sparc: " Emilio G. Cota
2018-10-25 17:20 ` [Qemu-devel] [RFC 36/48] target/xtensa: " Emilio G. Cota
2018-10-25 17:20 ` [Qemu-devel] [RFC 37/48] target/openrisc: " Emilio G. Cota
2018-10-25 17:20 ` [Qemu-devel] [RFC 38/48] translator: implement " Emilio G. Cota
2018-11-26 15:16   ` Alex Bennée
2018-11-27  2:16     ` Emilio G. Cota
2018-11-27 14:48       ` Alex Bennée
2018-11-27 19:06         ` Emilio G. Cota
2018-11-28  2:30           ` Emilio G. Cota
2018-11-28 12:50             ` Alex Bennée
2018-11-28 15:03               ` Emilio G. Cota
2018-10-25 17:20 ` [Qemu-devel] [RFC 39/48] plugin: add API symbols to qemu-plugins.symbols Emilio G. Cota
2018-10-25 17:20 ` [Qemu-devel] [RFC 40/48] plugin: let plugins control the virtual clock Emilio G. Cota
2018-10-25 17:20 ` [Qemu-devel] [RFC 41/48] configure: add --enable-plugins Emilio G. Cota
2018-11-27 12:11   ` Alex Bennée
2018-11-27 17:08     ` Emilio G. Cota
2018-11-27 12:43   ` Roman Bolshakov
2018-11-27 23:13     ` Emilio G. Cota
2018-11-28 10:43       ` Roman Bolshakov
2018-11-28 17:23         ` Emilio G. Cota
2018-11-29  9:57           ` Roman Bolshakov
2018-11-29 17:00             ` Emilio G. Cota
2018-11-29 17:49               ` Emilio G. Cota
2018-11-29 19:34                 ` Roman Bolshakov
2018-10-25 17:20 ` [Qemu-devel] [RFC 42/48] vl: support -plugin option Emilio G. Cota
2018-10-25 17:20 ` [Qemu-devel] [RFC 43/48] linux-user: " Emilio G. Cota
2018-10-25 17:20 ` [Qemu-devel] [RFC 44/48] cpus: lockstep execution support Emilio G. Cota
2018-11-14 16:43   ` Alex Bennée
2018-11-14 18:33     ` Emilio G. Cota
2018-10-25 17:20 ` [Qemu-devel] [RFC 45/48] plugin: " Emilio G. Cota
2018-11-27 18:20   ` Alex Bennée
2018-11-27 19:19     ` Emilio G. Cota
2018-10-25 17:20 ` [Qemu-devel] [RFC 46/48] plugin: add plugin-chan PCI device Emilio G. Cota
2018-10-25 17:20 ` [Qemu-devel] [RFC 47/48] plugin: support guest hooks Emilio G. Cota
2018-11-27 18:28   ` Alex Bennée
2018-10-25 17:20 ` [Qemu-devel] [RFC 48/48] plugin: add a couple of very simple examples Emilio G. Cota
2018-10-29 10:59   ` Pavel Dovgalyuk
2018-10-29 16:38     ` Emilio G. Cota
2018-11-28 11:28   ` Alex Bennée
2018-11-28 14:48     ` Emilio G. Cota
2018-11-29 20:45   ` Roman Bolshakov
2018-12-08 23:32     ` Emilio G. Cota
2018-10-29  9:48 ` [Qemu-devel] [RFC 00/48] Plugin support Pavel Dovgalyuk
2018-10-29 16:45   ` Emilio G. Cota

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