All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [RFC 0/6] initial plugin support
@ 2017-09-06 20:28 Emilio G. Cota
  2017-09-06 20:28 ` [Qemu-devel] [RFC 1/6] tcg: keep a pointer to the current TB in TCGContext Emilio G. Cota
                   ` (8 more replies)
  0 siblings, 9 replies; 17+ messages in thread
From: Emilio G. Cota @ 2017-09-06 20:28 UTC (permalink / raw)
  To: qemu-devel; +Cc: Lluís Vilanova, Stefan Hajnoczi

Related threads:
  [PATCH 00/13] instrument: Add basic event instrumentation
  Date: Mon, 24 Jul 2017 20:02:24 +0300
  https://lists.gnu.org/archive/html/qemu-devel/2017-07/msg07419.html
and
  [PATCH v4 00/20] instrument: Add basic event instrumentation
  Date:	Wed, 6 Sep 2017 20:22:41 +0300
  https://lists.gnu.org/archive/html/qemu-devel/2017-07/msg07419.html

This set does something similar to the instrumentation patches by Lluis,
but with a different implementation (and for now less events).

My focus has been on working on the skeleton of a (pseudo) stable API,
as Stefan requested. Of course more events would have to be added, but
before spending more time on this I'd like to get some feedback on the
core of the design. Patch 2 has all the details.

Note: yes, patch 1 is not used in the series, but this is an RFC. It's there
because it will be needed to get the tb->plugin_mask when deciding whether
to generate a mem_cb helper when generating loads/stores from TCG.

This set applies on top of:
  https://github.com/cota/qemu/tree/tcg-generic-15%2Bmulti-tcg-v4-parallel

The tree can be fetched from:
  https://github.com/cota/qemu/tree/plugins

Thanks,

		Emilio
---
 Makefile                  |   7 +-
 Makefile.objs             |   1 +
 accel/tcg/cpu-exec.c      |   6 +-
 accel/tcg/translate-all.c |   9 +-
 configure                 |  52 ++++
 include/exec/exec-all.h   |   2 +
 include/exec/tb-hash-xx.h |  26 +-
 include/exec/tb-hash.h    |   6 +-
 include/exec/tb-lookup.h  |   1 +
 include/qemu/plugin-api.h | 105 +++++++
 include/qemu/plugin.h     |  74 +++++
 include/qom/cpu.h         |   4 +
 linux-user/main.c         |  18 ++
 plugin.c                  | 519 ++++++++++++++++++++++++++++++++
 qemu-options.hx           |  17 ++
 qemu-plugins.symbols      |   6 +
 qom/cpu.c                 |   3 +
 tcg/tcg-op.c              |  10 +-
 tcg/tcg.h                 |   2 +-
 tests/qht-bench.c         |   2 +-
 vl.c                      |  10 +
 21 files changed, 857 insertions(+), 23 deletions(-)

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

* [Qemu-devel] [RFC 1/6] tcg: keep a pointer to the current TB in TCGContext
  2017-09-06 20:28 [Qemu-devel] [RFC 0/6] initial plugin support Emilio G. Cota
@ 2017-09-06 20:28 ` Emilio G. Cota
  2017-09-06 20:28 ` [Qemu-devel] [RFC 2/6] plugin: add initial plugin support Emilio G. Cota
                   ` (7 subsequent siblings)
  8 siblings, 0 replies; 17+ messages in thread
From: Emilio G. Cota @ 2017-09-06 20:28 UTC (permalink / raw)
  To: qemu-devel; +Cc: Lluís Vilanova, Stefan Hajnoczi

Instead of just a copy of tb->cflags. This gives access in TCG
to other fields in TB, which will be needed when we implement
callbacks from memory accesses performed by TCG-translated code.

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

diff --git a/tcg/tcg.h b/tcg/tcg.h
index cf4eeaf..86ca604 100644
--- a/tcg/tcg.h
+++ b/tcg/tcg.h
@@ -676,7 +676,7 @@ struct TCGContext {
     uintptr_t *tb_jmp_target_addr; /* tb->jmp_target_addr if !USE_DIRECT_JUMP */
 
     TCGRegSet reserved_regs;
-    uint32_t tb_cflags; /* cflags of the current TB */
+    const TranslationBlock *tb;
     intptr_t current_frame_offset;
     intptr_t frame_start;
     intptr_t frame_end;
diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c
index 69cc7dc..7332afc 100644
--- a/accel/tcg/translate-all.c
+++ b/accel/tcg/translate-all.c
@@ -1600,7 +1600,6 @@ TranslationBlock *tb_gen_code(CPUState *cpu,
     tb->flags = flags;
     tb->cflags = cflags;
     tb->trace_vcpu_dstate = *cpu->trace_dstate;
-    tcg_ctx->tb_cflags = cflags;
 
 #ifdef CONFIG_PROFILER
     /* includes aborted translations because of exceptions */
@@ -1610,9 +1609,11 @@ TranslationBlock *tb_gen_code(CPUState *cpu,
 
     tcg_func_start(tcg_ctx);
 
+    tcg_ctx->tb = tb;
     tcg_ctx->cpu = ENV_GET_CPU(env);
     gen_intermediate_code(cpu, tb);
     tcg_ctx->cpu = NULL;
+    tcg_ctx->tb = NULL;
 
     trace_translate_block(tb, tb->pc, tb->tc.ptr);
 
diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c
index 2a8bf90..32449d2 100644
--- a/tcg/tcg-op.c
+++ b/tcg/tcg-op.c
@@ -150,7 +150,7 @@ void tcg_gen_op6(TCGContext *ctx, TCGOpcode opc, TCGArg a1, TCGArg a2,
 
 void tcg_gen_mb(TCGBar mb_type)
 {
-    if (tcg_ctx->tb_cflags & CF_PARALLEL) {
+    if (tb_cflags(tcg_ctx->tb) & CF_PARALLEL) {
         tcg_gen_op1(tcg_ctx, INDEX_op_mb, mb_type);
     }
 }
@@ -2794,7 +2794,7 @@ void tcg_gen_atomic_cmpxchg_i32(TCGv_i32 retv, TCGv addr, TCGv_i32 cmpv,
 {
     memop = tcg_canonicalize_memop(memop, 0, 0);
 
-    if (!(tcg_ctx->tb_cflags & CF_PARALLEL)) {
+    if (!(tb_cflags(tcg_ctx->tb) & CF_PARALLEL)) {
         TCGv_i32 t1 = tcg_temp_new_i32();
         TCGv_i32 t2 = tcg_temp_new_i32();
 
@@ -2838,7 +2838,7 @@ void tcg_gen_atomic_cmpxchg_i64(TCGv_i64 retv, TCGv addr, TCGv_i64 cmpv,
 {
     memop = tcg_canonicalize_memop(memop, 1, 0);
 
-    if (!(tcg_ctx->tb_cflags & CF_PARALLEL)) {
+    if (!(tb_cflags(tcg_ctx->tb) & CF_PARALLEL)) {
         TCGv_i64 t1 = tcg_temp_new_i64();
         TCGv_i64 t2 = tcg_temp_new_i64();
 
@@ -3015,7 +3015,7 @@ static void * const table_##NAME[16] = {                                \
 void tcg_gen_atomic_##NAME##_i32                                        \
     (TCGv_i32 ret, TCGv addr, TCGv_i32 val, TCGArg idx, TCGMemOp memop) \
 {                                                                       \
-    if (tcg_ctx->tb_cflags & CF_PARALLEL) {                             \
+    if (tb_cflags(tcg_ctx->tb) & CF_PARALLEL) {                         \
         do_atomic_op_i32(ret, addr, val, idx, memop, table_##NAME);     \
     } else {                                                            \
         do_nonatomic_op_i32(ret, addr, val, idx, memop, NEW,            \
@@ -3025,7 +3025,7 @@ void tcg_gen_atomic_##NAME##_i32                                        \
 void tcg_gen_atomic_##NAME##_i64                                        \
     (TCGv_i64 ret, TCGv addr, TCGv_i64 val, TCGArg idx, TCGMemOp memop) \
 {                                                                       \
-    if (tcg_ctx->tb_cflags & CF_PARALLEL) {                             \
+    if (tb_cflags(tcg_ctx->tb) & CF_PARALLEL) {                         \
         do_atomic_op_i64(ret, addr, val, idx, memop, table_##NAME);     \
     } else {                                                            \
         do_nonatomic_op_i64(ret, addr, val, idx, memop, NEW,            \
-- 
2.7.4

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

* [Qemu-devel] [RFC 2/6] plugin: add initial plugin support
  2017-09-06 20:28 [Qemu-devel] [RFC 0/6] initial plugin support Emilio G. Cota
  2017-09-06 20:28 ` [Qemu-devel] [RFC 1/6] tcg: keep a pointer to the current TB in TCGContext Emilio G. Cota
@ 2017-09-06 20:28 ` Emilio G. Cota
  2017-09-06 20:41   ` Emilio G. Cota
  2017-09-26  4:27   ` Thomas Huth
  2017-09-06 20:28 ` [Qemu-devel] [RFC 3/6] configure: add --enable-plugins Emilio G. Cota
                   ` (6 subsequent siblings)
  8 siblings, 2 replies; 17+ messages in thread
From: Emilio G. Cota @ 2017-09-06 20:28 UTC (permalink / raw)
  To: qemu-devel; +Cc: Lluís Vilanova, Stefan Hajnoczi

This is still a work in progress, hence the limited public-facing API.
I wanted to add more events (e.g. memory and basic block callbacks), but
before spending more time on time I'd like to see whether this direction
is appreciated by others.

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.objs             |   1 +
 include/exec/exec-all.h   |   2 +
 include/exec/tb-hash-xx.h |  26 ++-
 include/exec/tb-hash.h    |   6 +-
 include/exec/tb-lookup.h  |   1 +
 include/qemu/plugin-api.h | 105 ++++++++++
 include/qemu/plugin.h     |  74 +++++++
 include/qom/cpu.h         |   4 +
 accel/tcg/cpu-exec.c      |   6 +-
 accel/tcg/translate-all.c |   6 +-
 plugin.c                  | 519 ++++++++++++++++++++++++++++++++++++++++++++++
 qom/cpu.c                 |   3 +
 tests/qht-bench.c         |   2 +-
 qemu-plugins.symbols      |   6 +
 14 files changed, 746 insertions(+), 15 deletions(-)
 create mode 100644 include/qemu/plugin-api.h
 create mode 100644 include/qemu/plugin.h
 create mode 100644 plugin.c
 create mode 100644 qemu-plugins.symbols

diff --git a/Makefile.objs b/Makefile.objs
index 24a4ea0..52cc89c 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -84,6 +84,7 @@ endif
 #######################################################################
 # Target-independent parts used in system and user emulation
 common-obj-y += cpus-common.o
+common-obj-$(CONFIG_PLUGINS) += plugin.o
 common-obj-y += hw/
 common-obj-y += qom/
 common-obj-y += disas/
diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h
index 1d4e977..add9423 100644
--- a/include/exec/exec-all.h
+++ b/include/exec/exec-all.h
@@ -346,6 +346,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-xx.h b/include/exec/tb-hash-xx.h
index 747a9a6..4015d6a 100644
--- a/include/exec/tb-hash-xx.h
+++ b/include/exec/tb-hash-xx.h
@@ -49,7 +49,8 @@
  * 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)
+tb_hash_func8(uint64_t a0, uint64_t b0, uint32_t e, uint32_t f, uint32_t g,
+              uint32_t h)
 {
     uint32_t v1 = TB_HASH_XX_SEED + PRIME32_1 + PRIME32_2;
     uint32_t v2 = TB_HASH_XX_SEED + PRIME32_2;
@@ -77,17 +78,24 @@ tb_hash_func7(uint64_t a0, uint64_t b0, 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;
 
-    h32 += f * PRIME32_3;
-    h32  = rol32(h32, 17) * PRIME32_4;
+    v3 += g * PRIME32_2;
+    v3 = rol32(v3, 13);
+    v3 *= PRIME32_1;
 
-    h32 += g * PRIME32_3;
-    h32  = rol32(h32, 17) * PRIME32_4;
+    v4 += h * PRIME32_2;
+    v4 = rol32(v4, 13);
+    v4 *= PRIME32_1;
+
+    h32 = rol32(v1, 1) + rol32(v2, 7) + rol32(v3, 12) + rol32(v4, 18);
+    h32 += 32;
 
     h32 ^= h32 >> 15;
     h32 *= PRIME32_2;
diff --git a/include/exec/tb-hash.h b/include/exec/tb-hash.h
index 0526c4f..f501dd1 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 tb_hash_func7(phys_pc, pc, flags, cf_mask, trace_vcpu_dstate);
+    return tb_hash_func8(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 2961385..c879903 100644
--- a/include/exec/tb-lookup.h
+++ b/include/exec/tb-lookup.h
@@ -35,6 +35,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/include/qemu/plugin-api.h b/include/qemu/plugin-api.h
new file mode 100644
index 0000000..85f827a
--- /dev/null
+++ b/include/qemu/plugin-api.h
@@ -0,0 +1,105 @@
+/*
+ * 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>
+
+/*
+ * 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);
+
+/**
+ * qemu_plugin_uninstall - Uninstall a plugin
+ * @id: this plugin's opaque ID
+ *
+ * Removes all callbacks and unloads the plugin.
+ *
+ * Once this function returns, no further API calls from it are allowed.
+ *
+ * Note: if the plugin is multi-threaded (e.g. it is subscribed to callbacks
+ * from vCPUs running in parallel), some time will elapse before changes
+ * propagate to all threads, and therefore some callbacks might still be called
+ * for a short period of time after this function returns.
+ */
+void qemu_plugin_uninstall(qemu_plugin_id_t id);
+
+typedef void (*qemu_plugin_vcpu_simple_cb_t)(qemu_plugin_id_t id,
+                                             unsigned int vcpu_index);
+
+/**
+ * qemu_plugin_register_vcpu_init_cb - register a vCPU initialization callback
+ * @id: plugin ID
+ * @cb: callback function
+ *
+ * The @cb function is called every time a vCPU is initialized.
+ *
+ * See also: qemu_plugin_register_vcpu_exit_cb()
+ */
+void qemu_plugin_register_vcpu_init_cb(qemu_plugin_id_t id,
+                                       qemu_plugin_vcpu_simple_cb_t cb);
+
+/**
+ * qemu_plugin_register_vcpu_exit_cb - register a vCPU exit callback
+ * @id: plugin ID
+ * @cb: callback function
+ *
+ * The @cb function is called every time a vCPU exits.
+ *
+ * See also: qemu_plugin_register_vcpu_init_cb()
+ */
+void qemu_plugin_register_vcpu_exit_cb(qemu_plugin_id_t id,
+                                       qemu_plugin_vcpu_simple_cb_t cb);
+
+/**
+ * qemu_plugin_vcpu_for_each - iterate over the existing vCPU
+ * @id: plugin ID
+ * @cb: callback function
+ *
+ * The @cb function is called once for each existing vCPU.
+ * Note: to avoid deadlock, @cb cannot make any other qemu_plugin_*() call.
+ *
+ * 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);
+
+#endif /* QEMU_PLUGIN_API_H */
diff --git a/include/qemu/plugin.h b/include/qemu/plugin.h
new file mode 100644
index 0000000..a73540b
--- /dev/null
+++ b/include/qemu/plugin.h
@@ -0,0 +1,74 @@
+/*
+ * 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_MAX,
+};
+
+#ifdef CONFIG_PLUGINS
+
+void qemu_plugin_vcpu_init_hook(CPUState *cpu);
+void qemu_plugin_vcpu_exit_hook(CPUState *cpu);
+
+#else /* !CONFIG_PLUGINS */
+
+static inline void qemu_plugin_vcpu_init_hook(CPUState *cpu)
+{ }
+
+static inline void qemu_plugin_vcpu_exit_hook(CPUState *cpu)
+{ }
+
+#endif /* !CONFIG_PLUGINS */
+
+#endif /* QEMU_PLUGIN_H */
diff --git a/include/qom/cpu.h b/include/qom/cpu.h
index 25eefea..899b2d6 100644
--- a/include/qom/cpu.h
+++ b/include/qom/cpu.h
@@ -27,6 +27,7 @@
 #include "qemu/bitmap.h"
 #include "qemu/queue.h"
 #include "qemu/thread.h"
+#include "qemu/plugin.h"
 
 typedef int (*WriteCoreDumpFunction)(const void *buf, size_t size,
                                      void *opaque);
@@ -305,6 +306,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.
  *
  * State of one CPU core or thread.
  */
@@ -377,6 +379,8 @@ 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);
+
     /* TODO Move common fields from CPUArchState here. */
     int cpu_index; /* used by alpha TCG */
     uint32_t halted; /* used by alpha, cris, ppc TCG */
diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c
index e38479b..5523c44 100644
--- a/accel/tcg/cpu-exec.c
+++ b/accel/tcg/cpu-exec.c
@@ -271,6 +271,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)
@@ -283,6 +284,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) {
@@ -314,10 +316,12 @@ 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);
     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, tb_lookup_cmp, &desc, h);
 }
 
diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c
index 7332afc..17abfcc 100644
--- a/accel/tcg/translate-all.c
+++ b/accel/tcg/translate-all.c
@@ -1027,6 +1027,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];
 }
@@ -1344,7 +1345,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 & CF_HASH_MASK,
-                     tb->trace_vcpu_dstate);
+                     tb->trace_vcpu_dstate, tb->plugin_mask);
     if (!qht_remove(&tb_ctx.htable, tb, h)) {
         return;
     }
@@ -1533,7 +1534,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);
     existing_tb = qht_insert(&tb_ctx.htable, tb, h);
 
     /* remove TB from the page(s) if we couldn't insert it */
@@ -1600,6 +1601,7 @@ TranslationBlock *tb_gen_code(CPUState *cpu,
     tb->flags = flags;
     tb->cflags = cflags;
     tb->trace_vcpu_dstate = *cpu->trace_dstate;
+    tb->plugin_mask = *cpu->plugin_mask;
 
 #ifdef CONFIG_PROFILER
     /* includes aborted translations because of exceptions */
diff --git a/plugin.c b/plugin.c
new file mode 100644
index 0000000..3cd19df
--- /dev/null
+++ b/plugin.c
@@ -0,0 +1,519 @@
+/* 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/plugin.h"
+#include "qemu/config-file.h"
+#include "qapi/error.h"
+#include "qemu/option.h"
+#include "qemu/rcu_queue.h"
+#include "qemu/rcu.h"
+#include "qom/cpu.h"
+#include "exec/cpu-common.h"
+#include <dlfcn.h>
+
+struct qemu_plugin_cb {
+    struct qemu_plugin_ctx *ctx;
+    union {
+        qemu_plugin_vcpu_simple_cb_t vcpu_simple_cb;
+        void *func;
+    };
+    QLIST_ENTRY(qemu_plugin_cb) entry;
+};
+
+QLIST_HEAD(qemu_plugin_cb_head, qemu_plugin_cb);
+
+struct qemu_plugin_ctx {
+    /* @rcu: keep at the top to help valgrind find the whole struct */
+    struct rcu_head rcu;
+    void *handle; /* dlopen */
+    qemu_plugin_id_t id;
+    struct qemu_plugin_cb *callbacks[QEMU_PLUGIN_EV_MAX];
+    QTAILQ_ENTRY(qemu_plugin_ctx) entry;
+    bool uninstalling; /* protected by plugin.lock */
+};
+
+/* global state */
+struct qemu_plugin_state {
+    QTAILQ_HEAD(, qemu_plugin_ctx) ctxs;
+    struct qemu_plugin_cb_head 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. Since some API ops
+     * call plugin code repeatedly (e.g. vcpu_for_each), we keep
+     * a counter to allow for recursive acquisitions.
+     */
+    QemuMutex lock;
+};
+
+/*
+ * 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 __thread bool plugin_lock_held;
+
+static inline void plugin_lock(void)
+{
+    g_assert(!plugin_lock_held);
+    qemu_mutex_lock(&plugin.lock);
+    plugin_lock_held = true;
+}
+
+static inline void plugin_unlock(void)
+{
+    plugin_lock_held = false;
+    qemu_mutex_unlock(&plugin.lock);
+}
+
+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 {
+        g_assert_not_reached();
+    }
+    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;
+    }
+
+    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);
+    plugin_unlock();
+
+    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.
+         */
+        plugin_lock();
+        if (!ctx->uninstalling) {
+            qemu_plugin_uninstall(ctx->id);
+        }
+        plugin_unlock();
+        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(qemu_plugin_id_t id)
+{
+    struct qemu_plugin_ctx *ctx;
+    qemu_plugin_id_t *id_p;
+
+    g_assert(plugin_lock_held);
+    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(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);
+
+    g_assert(plugin_lock_held);
+
+    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(struct qemu_plugin_ctx *ctx,
+                                 enum qemu_plugin_event ev)
+{
+    struct qemu_plugin_cb *cb = ctx->callbacks[ev];
+
+    g_assert(plugin_lock_held);
+
+    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, NULL);
+    }
+}
+
+static void plugin_destroy__rcuthread(struct qemu_plugin_ctx *ctx)
+{
+    plugin_lock();
+    QTAILQ_REMOVE(&plugin.ctxs, ctx, entry);
+    g_assert(ctx->uninstalling);
+    plugin_unlock();
+
+    if (dlclose(ctx->handle)) {
+        warn_report("%s: %s", __func__, dlerror());
+    }
+    qemu_vfree(ctx);
+}
+
+void qemu_plugin_uninstall(qemu_plugin_id_t id)
+{
+    struct qemu_plugin_ctx *ctx;
+    enum qemu_plugin_event ev;
+    bool success;
+
+    plugin_lock();
+    ctx = id_to_ctx(id);
+    if (unlikely(ctx->uninstalling)) {
+        error_report("plugin: called %s more than once", __func__);
+        abort();
+    }
+    ctx->uninstalling = true;
+    /*
+     * 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 free the context nor invalidate its id.
+     */
+    for (ev = 0; ev < QEMU_PLUGIN_EV_MAX; ev++) {
+        plugin_unregister_cb(ctx, ev);
+    }
+    success = g_hash_table_remove(plugin.id_ht, &ctx->id);
+    g_assert(success);
+    plugin_unlock();
+
+    call_rcu(ctx, plugin_destroy__rcuthread, rcu);
+}
+
+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:
+        /* 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->vcpu_simple_cb;
+
+            func(cb->ctx->id, cpu->cpu_index);
+        }
+        break;
+    default:
+        g_assert_not_reached();
+    }
+}
+
+static void plugin_register_cb(qemu_plugin_id_t id, enum qemu_plugin_event ev,
+                               void *func)
+{
+    struct qemu_plugin_ctx *ctx;
+
+    plugin_lock();
+    ctx = id_to_ctx(id);
+    if (func) {
+        struct qemu_plugin_cb *cb = ctx->callbacks[ev];
+
+        if (cb) {
+            cb->func = func;
+        } else {
+            cb = g_new(struct qemu_plugin_cb, 1);
+            cb->ctx = ctx;
+            cb->func = func;
+            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, NULL);
+            }
+        }
+    } else {
+        plugin_unregister_cb(ctx, ev);
+    }
+    plugin_unlock();
+}
+
+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;
+
+    plugin_lock();
+    success = g_hash_table_insert(plugin.cpu_ht, &cpu->cpu_index,
+                                  &cpu->cpu_index);
+    g_assert(success);
+    plugin_unlock();
+
+    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);
+
+    plugin_lock();
+    success = g_hash_table_remove(plugin.cpu_ht, &cpu->cpu_index);
+    g_assert(success);
+    plugin_unlock();
+}
+
+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;
+    }
+    plugin_lock();
+    args.ctx = id_to_ctx(id);
+    args.cb = cb;
+    g_hash_table_foreach(plugin.cpu_ht, plugin_vcpu_for_each, &args);
+    plugin_unlock();
+}
+
+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_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);
+}
diff --git a/qom/cpu.c b/qom/cpu.c
index 4f38db0..60b4a56 100644
--- a/qom/cpu.c
+++ b/qom/cpu.c
@@ -31,6 +31,7 @@
 #include "sysemu/sysemu.h"
 #include "hw/qdev-properties.h"
 #include "trace-root.h"
+#include "qemu/plugin.h"
 
 CPUInterruptHandler cpu_interrupt_handler;
 
@@ -368,6 +369,7 @@ static void cpu_common_realizefn(DeviceState *dev, Error **errp)
 
     /* NOTE: latest generic point where the cpu is fully realized */
     trace_init_vcpu(cpu);
+    qemu_plugin_vcpu_init_hook(cpu);
 }
 
 static void cpu_common_unrealizefn(DeviceState *dev, Error **errp)
@@ -375,6 +377,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);
 }
 
diff --git a/tests/qht-bench.c b/tests/qht-bench.c
index 6c2eb8e..86cd85a 100644
--- a/tests/qht-bench.c
+++ b/tests/qht-bench.c
@@ -103,7 +103,7 @@ static bool is_equal(const void *ap, const void *bp)
 
 static inline uint32_t h(unsigned long v)
 {
-    return tb_hash_func7(v, 0, 0, 0, 0);
+    return tb_hash_func8(v, 0, 0, 0, 0, 0);
 }
 
 /*
diff --git a/qemu-plugins.symbols b/qemu-plugins.symbols
new file mode 100644
index 0000000..2b8463e
--- /dev/null
+++ b/qemu-plugins.symbols
@@ -0,0 +1,6 @@
+{
+  qemu_plugin_uninstall;
+  qemu_plugin_register_vcpu_init_cb;
+  qemu_plugin_register_vcpu_exit_cb;
+  qemu_plugin_vcpu_for_each;
+};
-- 
2.7.4

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

* [Qemu-devel] [RFC 3/6] configure: add --enable-plugins
  2017-09-06 20:28 [Qemu-devel] [RFC 0/6] initial plugin support Emilio G. Cota
  2017-09-06 20:28 ` [Qemu-devel] [RFC 1/6] tcg: keep a pointer to the current TB in TCGContext Emilio G. Cota
  2017-09-06 20:28 ` [Qemu-devel] [RFC 2/6] plugin: add initial plugin support Emilio G. Cota
@ 2017-09-06 20:28 ` Emilio G. Cota
  2017-09-06 20:28 ` [Qemu-devel] [RFC 4/6] Makefile: install qemu-api.h as include/qemu-plugin.h Emilio G. Cota
                   ` (5 subsequent siblings)
  8 siblings, 0 replies; 17+ messages in thread
From: Emilio G. Cota @ 2017-09-06 20:28 UTC (permalink / raw)
  To: qemu-devel; +Cc: Lluís Vilanova, 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 | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 52 insertions(+)

diff --git a/configure b/configure
index 987f59b..45f7af5 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
 
@@ -404,6 +405,8 @@ tcmalloc="no"
 jemalloc="no"
 replication="yes"
 vxhs=""
+plugins="no"
+ld_dynamic_list="no"
 
 supported_cpu="no"
 supported_os="no"
@@ -1282,6 +1285,10 @@ for opt do
   ;;
   --enable-vxhs) vxhs="yes"
   ;;
+  --enable-plugins) plugins="yes"
+  ;;
+  --disable-plugins) plugins="no"
+  ;;
   *)
       echo "ERROR: unknown option $opt"
       echo "Try '$0 --help' for more information"
@@ -1442,6 +1449,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:
@@ -4745,6 +4754,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
+
 ########################################
 # check if getauxval is available.
 
@@ -5388,6 +5433,7 @@ echo "jemalloc support  $jemalloc"
 echo "avx2 optimization $avx2_opt"
 echo "replication support $replication"
 echo "VxHS block device $vxhs"
+echo "plugin support    $plugins"
 
 if test "$sdl_too_old" = "yes"; then
 echo "-> Your SDL version is too old - please upgrade to have SDL support"
@@ -6053,6 +6099,12 @@ if test "$vxhs" = "yes" ; then
   echo "VXHS_LIBS=$vxhs_libs" >> $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="-I\$(SRC_PATH)/tcg/tci $QEMU_INCLUDES"
 elif test "$ARCH" = "sparc64" ; then
-- 
2.7.4

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

* [Qemu-devel] [RFC 4/6] Makefile: install qemu-api.h as include/qemu-plugin.h
  2017-09-06 20:28 [Qemu-devel] [RFC 0/6] initial plugin support Emilio G. Cota
                   ` (2 preceding siblings ...)
  2017-09-06 20:28 ` [Qemu-devel] [RFC 3/6] configure: add --enable-plugins Emilio G. Cota
@ 2017-09-06 20:28 ` Emilio G. Cota
  2017-09-06 20:28 ` [Qemu-devel] [RFC 5/6] vl: support -plugin option Emilio G. Cota
                   ` (4 subsequent siblings)
  8 siblings, 0 replies; 17+ messages in thread
From: Emilio G. Cota @ 2017-09-06 20:28 UTC (permalink / raw)
  To: qemu-devel; +Cc: Lluís Vilanova, Stefan Hajnoczi

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

diff --git a/Makefile b/Makefile
index ef72148..326867e 100644
--- a/Makefile
+++ b/Makefile
@@ -602,8 +602,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
@@ -626,6 +628,9 @@ endif
 ifeq ($(CONFIG_GTK),y)
 	$(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 \
 		$(INSTALL_DATA) $(SRC_PATH)/pc-bios/keymaps/$$x "$(DESTDIR)$(qemu_datadir)/keymaps"; \
-- 
2.7.4

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

* [Qemu-devel] [RFC 5/6] vl: support -plugin option
  2017-09-06 20:28 [Qemu-devel] [RFC 0/6] initial plugin support Emilio G. Cota
                   ` (3 preceding siblings ...)
  2017-09-06 20:28 ` [Qemu-devel] [RFC 4/6] Makefile: install qemu-api.h as include/qemu-plugin.h Emilio G. Cota
@ 2017-09-06 20:28 ` Emilio G. Cota
  2017-09-06 20:28 ` [Qemu-devel] [RFC 6/6] linux-user: " Emilio G. Cota
                   ` (3 subsequent siblings)
  8 siblings, 0 replies; 17+ messages in thread
From: Emilio G. Cota @ 2017-09-06 20:28 UTC (permalink / raw)
  To: qemu-devel; +Cc: Lluís Vilanova, 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            | 10 ++++++++++
 qemu-options.hx | 17 +++++++++++++++++
 2 files changed, 27 insertions(+)

diff --git a/vl.c b/vl.c
index fb6b2ef..d3d8adc 100644
--- a/vl.c
+++ b/vl.c
@@ -118,6 +118,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"
 
@@ -3031,6 +3032,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);
 
@@ -3058,6 +3060,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);
@@ -3999,6 +4002,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);
@@ -4185,6 +4191,10 @@ int main(int argc, char **argv, char **envp)
     }
     trace_init_file(trace_file);
 
+    if (qemu_plugin_load_list(&plugin_list)) {
+        exit(1);
+    }
+
     /* Open the logfile at this point and set the log mask if necessary.
      */
     if (log_file) {
diff --git a/qemu-options.hx b/qemu-options.hx
index 746b5fa..34557e6 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -4064,6 +4064,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.7.4

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

* [Qemu-devel] [RFC 6/6] linux-user: support -plugin option
  2017-09-06 20:28 [Qemu-devel] [RFC 0/6] initial plugin support Emilio G. Cota
                   ` (4 preceding siblings ...)
  2017-09-06 20:28 ` [Qemu-devel] [RFC 5/6] vl: support -plugin option Emilio G. Cota
@ 2017-09-06 20:28 ` Emilio G. Cota
  2017-09-14 14:33 ` [Qemu-devel] [RFC 0/6] initial plugin support Peter Maydell
                   ` (2 subsequent siblings)
  8 siblings, 0 replies; 17+ messages in thread
From: Emilio G. Cota @ 2017-09-06 20:28 UTC (permalink / raw)
  To: qemu-devel; +Cc: Lluís Vilanova, 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 31c8f1a..211ee02 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -27,6 +27,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"
@@ -4014,6 +4015,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;
@@ -4063,6 +4073,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}
@@ -4252,6 +4266,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);
 
@@ -4259,6 +4274,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.7.4

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

* Re: [Qemu-devel] [RFC 2/6] plugin: add initial plugin support
  2017-09-06 20:28 ` [Qemu-devel] [RFC 2/6] plugin: add initial plugin support Emilio G. Cota
@ 2017-09-06 20:41   ` Emilio G. Cota
  2017-09-26  4:27   ` Thomas Huth
  1 sibling, 0 replies; 17+ messages in thread
From: Emilio G. Cota @ 2017-09-06 20:41 UTC (permalink / raw)
  To: qemu-devel; +Cc: Lluís Vilanova, Stefan Hajnoczi

On Wed, Sep 06, 2017 at 16:28:44 -0400, Emilio G. Cota wrote:
> --- /dev/null
> +++ b/plugin.c
> @@ -0,0 +1,519 @@
(snip)
> +    /*
> +     * @lock protects the struct as well as ctx->uninstalling.
> +     * The lock must be acquired by all API ops. Since some API ops
> +     * call plugin code repeatedly (e.g. vcpu_for_each), we keep
> +     * a counter to allow for recursive acquisitions.
> +     */
> +    QemuMutex lock;

This comment is outdated. The lock is not recursive anymore (it used to be).

		E.

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

* Re: [Qemu-devel] [RFC 0/6] initial plugin support
  2017-09-06 20:28 [Qemu-devel] [RFC 0/6] initial plugin support Emilio G. Cota
                   ` (5 preceding siblings ...)
  2017-09-06 20:28 ` [Qemu-devel] [RFC 6/6] linux-user: " Emilio G. Cota
@ 2017-09-14 14:33 ` Peter Maydell
  2017-09-18 14:59 ` Stefan Hajnoczi
  2017-09-26  4:22 ` Thomas Huth
  8 siblings, 0 replies; 17+ messages in thread
From: Peter Maydell @ 2017-09-14 14:33 UTC (permalink / raw)
  To: Emilio G. Cota; +Cc: QEMU Developers, Lluís Vilanova, Stefan Hajnoczi

On 6 September 2017 at 21:28, Emilio G. Cota <cota@braap.org> wrote:
> Related threads:
>   [PATCH 00/13] instrument: Add basic event instrumentation
>   Date: Mon, 24 Jul 2017 20:02:24 +0300
>   https://lists.gnu.org/archive/html/qemu-devel/2017-07/msg07419.html
> and
>   [PATCH v4 00/20] instrument: Add basic event instrumentation
>   Date: Wed, 6 Sep 2017 20:22:41 +0300
>   https://lists.gnu.org/archive/html/qemu-devel/2017-07/msg07419.html
>
> This set does something similar to the instrumentation patches by Lluis,
> but with a different implementation (and for now less events).
>
> My focus has been on working on the skeleton of a (pseudo) stable API,
> as Stefan requested. Of course more events would have to be added, but
> before spending more time on this I'd like to get some feedback on the
> core of the design. Patch 2 has all the details.
>
> Note: yes, patch 1 is not used in the series, but this is an RFC. It's there
> because it will be needed to get the tb->plugin_mask when deciding whether
> to generate a mem_cb helper when generating loads/stores from TCG.
>
> This set applies on top of:
>   https://github.com/cota/qemu/tree/tcg-generic-15%2Bmulti-tcg-v4-parallel
>
> The tree can be fetched from:
>   https://github.com/cota/qemu/tree/plugins

Hi -- do you have documentation of what the plugin-facing
API here is? What I'd like to do for the initial evaluation of
this series and Luis's is just to look at the plugin API
and the command line/etc interface for users to provide the
API, because I think that's the interesting part.

thanks
-- PMM

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

* Re: [Qemu-devel] [RFC 0/6] initial plugin support
  2017-09-06 20:28 [Qemu-devel] [RFC 0/6] initial plugin support Emilio G. Cota
                   ` (6 preceding siblings ...)
  2017-09-14 14:33 ` [Qemu-devel] [RFC 0/6] initial plugin support Peter Maydell
@ 2017-09-18 14:59 ` Stefan Hajnoczi
  2017-09-18 15:05   ` Peter Maydell
  2017-09-26  4:22 ` Thomas Huth
  8 siblings, 1 reply; 17+ messages in thread
From: Stefan Hajnoczi @ 2017-09-18 14:59 UTC (permalink / raw)
  To: Emilio G. Cota
  Cc: qemu-devel, Lluís Vilanova, Stefan Hajnoczi, Peter Maydell

On Wed, Sep 06, 2017 at 04:28:42PM -0400, Emilio G. Cota wrote:
> Related threads:
>   [PATCH 00/13] instrument: Add basic event instrumentation
>   Date: Mon, 24 Jul 2017 20:02:24 +0300
>   https://lists.gnu.org/archive/html/qemu-devel/2017-07/msg07419.html
> and
>   [PATCH v4 00/20] instrument: Add basic event instrumentation
>   Date:	Wed, 6 Sep 2017 20:22:41 +0300
>   https://lists.gnu.org/archive/html/qemu-devel/2017-07/msg07419.html
> 
> This set does something similar to the instrumentation patches by Lluis,
> but with a different implementation (and for now less events).
> 
> My focus has been on working on the skeleton of a (pseudo) stable API,
> as Stefan requested. Of course more events would have to be added, but
> before spending more time on this I'd like to get some feedback on the
> core of the design. Patch 2 has all the details.
> 
> Note: yes, patch 1 is not used in the series, but this is an RFC. It's there
> because it will be needed to get the tb->plugin_mask when deciding whether
> to generate a mem_cb helper when generating loads/stores from TCG.
> 
> This set applies on top of:
>   https://github.com/cota/qemu/tree/tcg-generic-15%2Bmulti-tcg-v4-parallel
> 
> The tree can be fetched from:
>   https://github.com/cota/qemu/tree/plugins

Hi Emilio,
Thanks for sending this!  Could you and Lluís collaborate on a single
instrumentation plugin API?

Let's have a discussion with Lluís, Peter, myself, and potentially
others then we can use the QEMU Developer Conference Call that is
scheduled every 2 weeks.

The next call time slot is Tue 26 Sept at 13:00 UTC:
https://www.timeanddate.com/worldclock/fixedtime.html?iso=20170926T140000&p1=136

Stefan

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

* Re: [Qemu-devel] [RFC 0/6] initial plugin support
  2017-09-18 14:59 ` Stefan Hajnoczi
@ 2017-09-18 15:05   ` Peter Maydell
  2017-09-19  9:51     ` Stefan Hajnoczi
  0 siblings, 1 reply; 17+ messages in thread
From: Peter Maydell @ 2017-09-18 15:05 UTC (permalink / raw)
  To: Stefan Hajnoczi
  Cc: Emilio G. Cota, QEMU Developers, Lluís Vilanova, Stefan Hajnoczi

On 18 September 2017 at 15:59, Stefan Hajnoczi <stefanha@gmail.com> wrote:
> Thanks for sending this!  Could you and Lluís collaborate on a single
> instrumentation plugin API?
>
> Let's have a discussion with Lluís, Peter, myself, and potentially
> others then we can use the QEMU Developer Conference Call that is
> scheduled every 2 weeks.
>
> The next call time slot is Tue 26 Sept at 13:00 UTC:
> https://www.timeanddate.com/worldclock/fixedtime.html?iso=20170926T140000&p1=136

I can't make that, I'm afraid, it's Linaro Connect that week.

>From my point of view, it would be helpful to start with
just designing the API that we're trying to implement.
I care a lot that we get that API correct, I don't care much
at all about the implementation...

thanks
-- PMM

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

* Re: [Qemu-devel] [RFC 0/6] initial plugin support
  2017-09-18 15:05   ` Peter Maydell
@ 2017-09-19  9:51     ` Stefan Hajnoczi
  2017-09-25 18:12       ` Lluís Vilanova
  0 siblings, 1 reply; 17+ messages in thread
From: Stefan Hajnoczi @ 2017-09-19  9:51 UTC (permalink / raw)
  To: Peter Maydell
  Cc: Emilio G. Cota, QEMU Developers, Lluís Vilanova, Stefan Hajnoczi

On Mon, Sep 18, 2017 at 04:05:31PM +0100, Peter Maydell wrote:
> On 18 September 2017 at 15:59, Stefan Hajnoczi <stefanha@gmail.com> wrote:
> > Thanks for sending this!  Could you and Lluís collaborate on a single
> > instrumentation plugin API?
> >
> > Let's have a discussion with Lluís, Peter, myself, and potentially
> > others then we can use the QEMU Developer Conference Call that is
> > scheduled every 2 weeks.
> >
> > The next call time slot is Tue 26 Sept at 13:00 UTC:
> > https://www.timeanddate.com/worldclock/fixedtime.html?iso=20170926T140000&p1=136
> 
> I can't make that, I'm afraid, it's Linaro Connect that week.
> 
> From my point of view, it would be helpful to start with
> just designing the API that we're trying to implement.
> I care a lot that we get that API correct, I don't care much
> at all about the implementation...

That sounds good to me.

An RFC patch with just the header files would be enough for mailing list
discussion.  No implementation is needed yet.

Stefan

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

* Re: [Qemu-devel] [RFC 0/6] initial plugin support
  2017-09-19  9:51     ` Stefan Hajnoczi
@ 2017-09-25 18:12       ` Lluís Vilanova
  0 siblings, 0 replies; 17+ messages in thread
From: Lluís Vilanova @ 2017-09-25 18:12 UTC (permalink / raw)
  To: Stefan Hajnoczi
  Cc: Peter Maydell, Emilio G. Cota, QEMU Developers, Stefan Hajnoczi

Stefan Hajnoczi writes:

> On Mon, Sep 18, 2017 at 04:05:31PM +0100, Peter Maydell wrote:
>> On 18 September 2017 at 15:59, Stefan Hajnoczi <stefanha@gmail.com> wrote:
>> > Thanks for sending this!  Could you and Lluís collaborate on a single
>> > instrumentation plugin API?
>> >
>> > Let's have a discussion with Lluís, Peter, myself, and potentially
>> > others then we can use the QEMU Developer Conference Call that is
>> > scheduled every 2 weeks.
>> >
>> > The next call time slot is Tue 26 Sept at 13:00 UTC:
>> > https://www.timeanddate.com/worldclock/fixedtime.html?iso=20170926T140000&p1=136
>> 
>> I can't make that, I'm afraid, it's Linaro Connect that week.
>> 
>> From my point of view, it would be helpful to start with
>> just designing the API that we're trying to implement.
>> I care a lot that we get that API correct, I don't care much
>> at all about the implementation...

> That sounds good to me.

> An RFC patch with just the header files would be enough for mailing list
> discussion.  No implementation is needed yet.

I can send a stripped down version of this series only with the docs and public
headers.

But first I would like to know how to handle the translate/execute
differentiation first (if it is indeed needed).


Cheers,
  Lluis

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

* Re: [Qemu-devel] [RFC 0/6] initial plugin support
  2017-09-06 20:28 [Qemu-devel] [RFC 0/6] initial plugin support Emilio G. Cota
                   ` (7 preceding siblings ...)
  2017-09-18 14:59 ` Stefan Hajnoczi
@ 2017-09-26  4:22 ` Thomas Huth
  2017-09-26  9:32   ` Stefan Hajnoczi
  2017-09-26 16:15   ` Emilio G. Cota
  8 siblings, 2 replies; 17+ messages in thread
From: Thomas Huth @ 2017-09-26  4:22 UTC (permalink / raw)
  To: Emilio G. Cota, qemu-devel; +Cc: Lluís Vilanova, Stefan Hajnoczi

On 06.09.2017 22:28, Emilio G. Cota wrote:
> Related threads:
>   [PATCH 00/13] instrument: Add basic event instrumentation
>   Date: Mon, 24 Jul 2017 20:02:24 +0300
>   https://lists.gnu.org/archive/html/qemu-devel/2017-07/msg07419.html
> and
>   [PATCH v4 00/20] instrument: Add basic event instrumentation
>   Date:	Wed, 6 Sep 2017 20:22:41 +0300
>   https://lists.gnu.org/archive/html/qemu-devel/2017-07/msg07419.html
> 
> This set does something similar to the instrumentation patches by Lluis,
> but with a different implementation (and for now less events).
> 
> My focus has been on working on the skeleton of a (pseudo) stable API,
> as Stefan requested. Of course more events would have to be added, but
> before spending more time on this I'd like to get some feedback on the
> core of the design. Patch 2 has all the details.

Sorry for my ignorance, but if you send a patch series like this, could
you please elaborate a little bit more on the topic what this all is
about? In this cover letter, you basically give only some pointers about
other patch series and point the reader to patch 2, but also patch 2
does not really have a proper *description* of what this is really all
about. Sure, it's about plugins, but what kind of plugins? Audio? Video?
CPU? Everything? If you send RFC, you should properly describe your
vision first, and maybe give some examples, before you jump into the
details.

 Thanks,
  Thomas

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

* Re: [Qemu-devel] [RFC 2/6] plugin: add initial plugin support
  2017-09-06 20:28 ` [Qemu-devel] [RFC 2/6] plugin: add initial plugin support Emilio G. Cota
  2017-09-06 20:41   ` Emilio G. Cota
@ 2017-09-26  4:27   ` Thomas Huth
  1 sibling, 0 replies; 17+ messages in thread
From: Thomas Huth @ 2017-09-26  4:27 UTC (permalink / raw)
  To: Emilio G. Cota, qemu-devel; +Cc: Lluís Vilanova, Stefan Hajnoczi

On 06.09.2017 22:28, Emilio G. Cota wrote:
[...]
> diff --git a/plugin.c b/plugin.c
> new file mode 100644
> index 0000000..3cd19df
> --- /dev/null
> +++ b/plugin.c
> @@ -0,0 +1,519 @@
> +/* 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.
> + */

If you introduce new .c files, please add some sentences with a proper
description in the header with some very high level description about
what the code in the file is supposed to be doing. Just reading "plugin
interface" is not really very helpful when trying to understand new code.

 Thanks,
  Thomas

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

* Re: [Qemu-devel] [RFC 0/6] initial plugin support
  2017-09-26  4:22 ` Thomas Huth
@ 2017-09-26  9:32   ` Stefan Hajnoczi
  2017-09-26 16:15   ` Emilio G. Cota
  1 sibling, 0 replies; 17+ messages in thread
From: Stefan Hajnoczi @ 2017-09-26  9:32 UTC (permalink / raw)
  To: Thomas Huth
  Cc: Emilio G. Cota, qemu-devel, Lluís Vilanova, Stefan Hajnoczi

On Tue, Sep 26, 2017 at 06:22:31AM +0200, Thomas Huth wrote:
> On 06.09.2017 22:28, Emilio G. Cota wrote:
> > Related threads:
> >   [PATCH 00/13] instrument: Add basic event instrumentation
> >   Date: Mon, 24 Jul 2017 20:02:24 +0300
> >   https://lists.gnu.org/archive/html/qemu-devel/2017-07/msg07419.html
> > and
> >   [PATCH v4 00/20] instrument: Add basic event instrumentation
> >   Date:	Wed, 6 Sep 2017 20:22:41 +0300
> >   https://lists.gnu.org/archive/html/qemu-devel/2017-07/msg07419.html
> > 
> > This set does something similar to the instrumentation patches by Lluis,
> > but with a different implementation (and for now less events).
> > 
> > My focus has been on working on the skeleton of a (pseudo) stable API,
> > as Stefan requested. Of course more events would have to be added, but
> > before spending more time on this I'd like to get some feedback on the
> > core of the design. Patch 2 has all the details.
> 
> Sorry for my ignorance, but if you send a patch series like this, could
> you please elaborate a little bit more on the topic what this all is
> about? In this cover letter, you basically give only some pointers about
> other patch series and point the reader to patch 2, but also patch 2
> does not really have a proper *description* of what this is really all
> about. Sure, it's about plugins, but what kind of plugins? Audio? Video?
> CPU? Everything? If you send RFC, you should properly describe your
> vision first, and maybe give some examples, before you jump into the
> details.

Good point.

This series is about introducing a stable API/ABI and loadable
instrumentation plugin interface.  The goal is to offer dynamic binary
instrumentation frameworks like Intel's Pin and DynamoRIO offer.

In other words, performance analysis tasks (especially for TCG) can be
implemented in a shared library that is loaded by QEMU when running a
benchmark.

Stefan

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

* Re: [Qemu-devel] [RFC 0/6] initial plugin support
  2017-09-26  4:22 ` Thomas Huth
  2017-09-26  9:32   ` Stefan Hajnoczi
@ 2017-09-26 16:15   ` Emilio G. Cota
  1 sibling, 0 replies; 17+ messages in thread
From: Emilio G. Cota @ 2017-09-26 16:15 UTC (permalink / raw)
  To: Thomas Huth; +Cc: qemu-devel, Lluís Vilanova, Stefan Hajnoczi

On Tue, Sep 26, 2017 at 06:22:31 +0200, Thomas Huth wrote:
> Sorry for my ignorance, but if you send a patch series like this, could
> you please elaborate a little bit more on the topic what this all is
> about? In this cover letter, you basically give only some pointers about
> other patch series and point the reader to patch 2, but also patch 2
> does not really have a proper *description* of what this is really all
> about. Sure, it's about plugins, but what kind of plugins? Audio? Video?
> CPU? Everything? If you send RFC, you should properly describe your
> vision first, and maybe give some examples, before you jump into the
> details.

On Tue, Sep 26, 2017 at 06:27:01 +0200, Thomas Huth wrote:
> If you introduce new .c files, please add some sentences with a proper
> description in the header with some very high level description about
> what the code in the file is supposed to be doing. Just reading "plugin
> interface" is not really very helpful when trying to understand new code.

You make very good points.

It was an RFC aimed mostly at people who had been following the previous
threads on this topic. However, I understand people who had not followed
those threads lacked the necessary context. Sorry about that.

As Stefan pointed out, the goal is to provide some sort of instrumentation
a la Pin/DynamoRIO. The extent of said instrumentation is still to be decided,
although we agree on that at least we should be able to "instrument" (i.e.
observe via callbacks) instructions executed and memory accesses in the guest.

Currently I'm working bottom-up on this; my goal is to simulate an ARMv8
system, so for that I need to feed the instruction stream and memory accesses
to the timing model. This could be done in different ways, so I'll evaluate
the alternatives to see how they affect performance.

Note that plugin (or instrumentation) support has been implemented in the
past. However, this was for academic projects and the code was never submitted
to the list. Some examples:
- QTrace
  code: https://github.com/x-y-z/QTRACE
  paper: http://ieeexplore.ieee.org/document/7095810/ (paywall)
- Decaf
  code: https://github.com/sycurelab/DECAF
  paper: http://www.cs.ucr.edu/~heng/pubs/issta14.pdf
- QSim
  code: https://github.com/gtcasl/qsim
  paper: http://ieeexplore.ieee.org/abstract/document/6429075/ (paywall)
- PEMU
  code: https://github.com/utds3lab/pemu
  paper: http://www.utdallas.edu/~zxl111930/file/VEE15.pdf
- ESESC
  code: https://github.com/masc-ucsc/esesc
  paper: https://users.soe.ucsc.edu/~renau/docs/hpca13.pdf

Thanks,

		Emilio

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

end of thread, other threads:[~2017-09-26 16:16 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-09-06 20:28 [Qemu-devel] [RFC 0/6] initial plugin support Emilio G. Cota
2017-09-06 20:28 ` [Qemu-devel] [RFC 1/6] tcg: keep a pointer to the current TB in TCGContext Emilio G. Cota
2017-09-06 20:28 ` [Qemu-devel] [RFC 2/6] plugin: add initial plugin support Emilio G. Cota
2017-09-06 20:41   ` Emilio G. Cota
2017-09-26  4:27   ` Thomas Huth
2017-09-06 20:28 ` [Qemu-devel] [RFC 3/6] configure: add --enable-plugins Emilio G. Cota
2017-09-06 20:28 ` [Qemu-devel] [RFC 4/6] Makefile: install qemu-api.h as include/qemu-plugin.h Emilio G. Cota
2017-09-06 20:28 ` [Qemu-devel] [RFC 5/6] vl: support -plugin option Emilio G. Cota
2017-09-06 20:28 ` [Qemu-devel] [RFC 6/6] linux-user: " Emilio G. Cota
2017-09-14 14:33 ` [Qemu-devel] [RFC 0/6] initial plugin support Peter Maydell
2017-09-18 14:59 ` Stefan Hajnoczi
2017-09-18 15:05   ` Peter Maydell
2017-09-19  9:51     ` Stefan Hajnoczi
2017-09-25 18:12       ` Lluís Vilanova
2017-09-26  4:22 ` Thomas Huth
2017-09-26  9:32   ` Stefan Hajnoczi
2017-09-26 16:15   ` 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.