Re: [PATCH 5/5] core/cpu-common: initialise plugin state before thread creation

2024-05-30 Thread Pierrick Bouvier

On 5/30/24 12:42, Alex Bennée wrote:

Originally I tried to move where vCPU thread initialisation to later
in realize. However pulling that thread (sic) got gnarly really
quickly. It turns out some steps of CPU realization need values that
can only be determined from the running vCPU thread.

However having moved enough out of the thread creation we can now
queue work before the thread starts (at least for TCG guests) and
avoid the race between vcpu_init and other vcpu states a plugin might
subscribe to.

Signed-off-by: Alex Bennée 
---
  hw/core/cpu-common.c | 20 
  1 file changed, 12 insertions(+), 8 deletions(-)

diff --git a/hw/core/cpu-common.c b/hw/core/cpu-common.c
index 6cfc01593a..bf1a7b8892 100644
--- a/hw/core/cpu-common.c
+++ b/hw/core/cpu-common.c
@@ -222,14 +222,6 @@ static void cpu_common_realizefn(DeviceState *dev, Error 
**errp)
  cpu_resume(cpu);
  }
  
-/* Plugin initialization must wait until the cpu start executing code */

-#ifdef CONFIG_PLUGIN
-if (tcg_enabled()) {
-cpu->plugin_state = qemu_plugin_create_vcpu_state();
-async_run_on_cpu(cpu, qemu_plugin_vcpu_init__async, RUN_ON_CPU_NULL);
-}
-#endif
-
  /* NOTE: latest generic point where the cpu is fully realized */
  }
  
@@ -273,6 +265,18 @@ static void cpu_common_initfn(Object *obj)

  QTAILQ_INIT(>watchpoints);
  
  cpu_exec_initfn(cpu);

+
+/*
+ * Plugin initialization must wait until the cpu start executing
+ * code, but we must queue this work before the threads are
+ * created to ensure we don't race.
+ */
+#ifdef CONFIG_PLUGIN
+if (tcg_enabled()) {
+cpu->plugin_state = qemu_plugin_create_vcpu_state();
+async_run_on_cpu(cpu, qemu_plugin_vcpu_init__async, RUN_ON_CPU_NULL);
+}
+#endif
  }
  
  static void cpu_common_finalize(Object *obj)


Could you check it works for all combination?
- user-mode
- system-mode tcg
- system-mode mttcg

When I tried to move this code around, one of them didn't work correctly.

Else,
Reviewed-by: Pierrick Bouvier 


Re: [PATCH 4/5] plugins: remove special casing for cpu->realized

2024-05-30 Thread Pierrick Bouvier

On 5/30/24 12:42, Alex Bennée wrote:

Now the condition variable is initialised early on we don't need to go
through hoops to avoid calling async_run_on_cpu.

Signed-off-by: Alex Bennée 
---
  plugins/core.c | 6 +-
  1 file changed, 1 insertion(+), 5 deletions(-)

diff --git a/plugins/core.c b/plugins/core.c
index 0726bc7f25..badede28cf 100644
--- a/plugins/core.c
+++ b/plugins/core.c
@@ -65,11 +65,7 @@ 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 (DEVICE(cpu)->realized) {

-async_run_on_cpu(cpu, plugin_cpu_update__async, mask);
-} else {
-plugin_cpu_update__async(cpu, mask);
-}
+async_run_on_cpu(cpu, plugin_cpu_update__async, mask);
  }
  
  void plugin_unregister_cb__locked(struct qemu_plugin_ctx *ctx,


Reviewed-by: Pierrick Bouvier 


Re: [PATCH 3/5] cpu-target: don't set cpu->thread_id to bogus value

2024-05-30 Thread Pierrick Bouvier

On 5/30/24 12:42, Alex Bennée wrote:

The thread_id isn't valid until the threads are created. There is no
point setting it here. The only thing that cares about the thread_id
is qmp_query_cpus_fast.

Signed-off-by: Alex Bennée 
---
  cpu-target.c | 1 -
  1 file changed, 1 deletion(-)

diff --git a/cpu-target.c b/cpu-target.c
index 5af120e8aa..499facf774 100644
--- a/cpu-target.c
+++ b/cpu-target.c
@@ -241,7 +241,6 @@ void cpu_exec_initfn(CPUState *cpu)
  cpu->num_ases = 0;
  
  #ifndef CONFIG_USER_ONLY

-cpu->thread_id = qemu_get_thread_id();
  cpu->memory = get_system_memory();
  object_ref(OBJECT(cpu->memory));
  #endif


Reviewed-by: Pierrick Bouvier 


Re: [PATCH 1/5] hw/core: expand on the alignment of CPUState

2024-05-30 Thread Pierrick Bouvier

On 5/30/24 12:42, Alex Bennée wrote:

Make the relationship between CPUState, ArchCPU and cpu_env a bit
clearer in the kdoc comments.

Signed-off-by: Alex Bennée 
---
  include/hw/core/cpu.h | 14 ++
  1 file changed, 10 insertions(+), 4 deletions(-)

diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h
index bb398e8237..35d345371b 100644
--- a/include/hw/core/cpu.h
+++ b/include/hw/core/cpu.h
@@ -391,7 +391,8 @@ struct qemu_work_item;
  #define CPU_UNSET_NUMA_NODE_ID -1
  
  /**

- * CPUState:
+ * struct CPUState - common state of one CPU core or thread.
+ *
   * @cpu_index: CPU index (informative).
   * @cluster_index: Identifies which cluster this CPU is in.
   *   For boards which don't define clusters or for "loose" CPUs not assigned
@@ -439,10 +440,15 @@ struct qemu_work_item;
   * @kvm_fetch_index: Keeps the index that we last fetched from the per-vCPU
   *dirty ring structure.
   *
- * State of one CPU core or thread.
+ * @neg_align: The CPUState is the common part of a concrete ArchCPU
+ * which is allocated when an individual CPU instance is created. As
+ * such care is taken is ensure there is no gap between between
+ * CPUState and CPUArchState within ArchCPU.
   *
- * Align, in order to match possible alignment required by CPUArchState,
- * and eliminate a hole between CPUState and CPUArchState within ArchCPU.
+ * @neg: The architectural register state ("cpu_env") immediately follows 
CPUState
+ * in ArchCPU and is passed to TCG code. The @neg structure holds some
+ * common TCG CPU variables which are accessed with a negative offset
+ * from cpu_env.
   */
  struct CPUState {
  /*< private >*/


Reviewed-by: Pierrick Bouvier 


Re: [PATCH 2/5] cpu: move Qemu[Thread|Cond] setup into common code

2024-05-30 Thread Pierrick Bouvier
  
  /* share a single thread for all cpus with TCG */

  snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "ALL CPUs/TCG");
  qemu_thread_create(cpu->thread, thread_name,
 rr_cpu_thread_fn,
 cpu, QEMU_THREAD_JOINABLE);
-
-single_tcg_halt_cond = cpu->halt_cond;
-single_tcg_cpu_thread = cpu->thread;
  } else {
-/* we share the thread */
+/* we share the thread, dump spare data */
+g_free(cpu->thread);
+qemu_cond_destroy(cpu->halt_cond);
  cpu->thread = single_tcg_cpu_thread;
  cpu->halt_cond = single_tcg_halt_cond;
+
+/* copy the stuff done at start of rr_cpu_thread_fn */
  cpu->thread_id = first_cpu->thread_id;
  cpu->neg.can_do_io = 1;
  cpu->created = true;
diff --git a/hw/core/cpu-common.c b/hw/core/cpu-common.c
index 0f0a247f56..6cfc01593a 100644
--- a/hw/core/cpu-common.c
+++ b/hw/core/cpu-common.c
@@ -261,6 +261,11 @@ static void cpu_common_initfn(Object *obj)
  cpu->nr_threads = 1;
  cpu->cflags_next_tb = -1;
  
+/* allocate storage for thread info, initialise condition variables */

+cpu->thread = g_new0(QemuThread, 1);
+cpu->halt_cond = g_new0(QemuCond, 1);
+qemu_cond_init(cpu->halt_cond);
+
  qemu_mutex_init(>work_mutex);
  qemu_lockcnt_init(>in_ioctl_lock);
  QSIMPLEQ_INIT(>work_list);
diff --git a/target/i386/nvmm/nvmm-accel-ops.c 
b/target/i386/nvmm/nvmm-accel-ops.c
index 6b2bfd9b9c..0ba31201e2 100644
--- a/target/i386/nvmm/nvmm-accel-ops.c
+++ b/target/i386/nvmm/nvmm-accel-ops.c
@@ -64,9 +64,6 @@ static void nvmm_start_vcpu_thread(CPUState *cpu)
  {
  char thread_name[VCPU_THREAD_NAME_SIZE];
  
-cpu->thread = g_new0(QemuThread, 1);

-cpu->halt_cond = g_new0(QemuCond, 1);
-qemu_cond_init(cpu->halt_cond);
  snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "CPU %d/NVMM",
   cpu->cpu_index);
  qemu_thread_create(cpu->thread, thread_name, qemu_nvmm_cpu_thread_fn,
diff --git a/target/i386/whpx/whpx-accel-ops.c 
b/target/i386/whpx/whpx-accel-ops.c
index 189ae0f140..1a2b4e1c43 100644
--- a/target/i386/whpx/whpx-accel-ops.c
+++ b/target/i386/whpx/whpx-accel-ops.c
@@ -64,9 +64,6 @@ static void whpx_start_vcpu_thread(CPUState *cpu)
  {
  char thread_name[VCPU_THREAD_NAME_SIZE];
  
-cpu->thread = g_new0(QemuThread, 1);

-cpu->halt_cond = g_new0(QemuCond, 1);
-qemu_cond_init(cpu->halt_cond);
  snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "CPU %d/WHPX",
   cpu->cpu_index);
  qemu_thread_create(cpu->thread, thread_name, whpx_cpu_thread_fn,


Reviewed-by: Pierrick Bouvier 


[PATCH v3 3/6] sysemu: generalise qtest_warp_clock as qemu_clock_advance_virtual_time

2024-05-30 Thread Pierrick Bouvier
From: Alex Bennée 

Move the key functionality of moving time forward into the clock
sub-system itself. This will allow us to plumb in time control into
plugins.

From: Alex Bennée 
Signed-off-by: Alex Bennée 
Signed-off-by: Pierrick Bouvier 
---
 include/qemu/timer.h | 15 +++
 system/qtest.c   | 25 +++--
 util/qemu-timer.c| 26 ++
 3 files changed, 44 insertions(+), 22 deletions(-)

diff --git a/include/qemu/timer.h b/include/qemu/timer.h
index 9a366e551fb..910587d8293 100644
--- a/include/qemu/timer.h
+++ b/include/qemu/timer.h
@@ -245,6 +245,21 @@ bool qemu_clock_run_timers(QEMUClockType type);
  */
 bool qemu_clock_run_all_timers(void);
 
+/**
+ * qemu_clock_advance_virtual_time(): advance the virtual time tick
+ * @target: target time in nanoseconds
+ *
+ * This function is used where the control of the flow of time has
+ * been delegated to outside the clock subsystem (be it qtest, icount
+ * or some other external source). You can ask the clock system to
+ * return @early at the first expired timer.
+ *
+ * Time can only move forward, attempts to reverse time would lead to
+ * an error.
+ *
+ * Returns: new virtual time.
+ */
+int64_t qemu_clock_advance_virtual_time(int64_t dest);
 
 /*
  * QEMUTimerList
diff --git a/system/qtest.c b/system/qtest.c
index ee8b139e982..e6f6b4e62d5 100644
--- a/system/qtest.c
+++ b/system/qtest.c
@@ -337,26 +337,6 @@ void qtest_set_virtual_clock(int64_t count)
 qatomic_set_i64(_clock_counter, count);
 }
 
-static void qtest_clock_warp(int64_t dest)
-{
-int64_t clock = cpus_get_virtual_clock();
-AioContext *aio_context;
-assert(qtest_enabled());
-aio_context = qemu_get_aio_context();
-while (clock < dest) {
-int64_t deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL,
-  QEMU_TIMER_ATTR_ALL);
-int64_t warp = qemu_soonest_timeout(dest - clock, deadline);
-
-cpus_set_virtual_clock(cpus_get_virtual_clock() + warp);
-
-qemu_clock_run_timers(QEMU_CLOCK_VIRTUAL);
-timerlist_run_timers(aio_context->tlg.tl[QEMU_CLOCK_VIRTUAL]);
-clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
-}
-qemu_clock_notify(QEMU_CLOCK_VIRTUAL);
-}
-
 static bool (*process_command_cb)(CharBackend *chr, gchar **words);
 
 void qtest_set_command_cb(bool (*pc_cb)(CharBackend *chr, gchar **words))
@@ -755,7 +735,8 @@ static void qtest_process_command(CharBackend *chr, gchar 
**words)
 ns = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL,
 QEMU_TIMER_ATTR_ALL);
 }
-qtest_clock_warp(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + ns);
+qemu_clock_advance_virtual_time(
+qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + ns);
 qtest_send_prefix(chr);
 qtest_sendf(chr, "OK %"PRIi64"\n",
 (int64_t)qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL));
@@ -781,7 +762,7 @@ static void qtest_process_command(CharBackend *chr, gchar 
**words)
 g_assert(words[1]);
 ret = qemu_strtoi64(words[1], NULL, 0, );
 g_assert(ret == 0);
-qtest_clock_warp(ns);
+qemu_clock_advance_virtual_time(ns);
 qtest_send_prefix(chr);
 qtest_sendf(chr, "OK %"PRIi64"\n",
 (int64_t)qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL));
diff --git a/util/qemu-timer.c b/util/qemu-timer.c
index 6a0de33dd2b..213114be68c 100644
--- a/util/qemu-timer.c
+++ b/util/qemu-timer.c
@@ -645,6 +645,11 @@ int64_t qemu_clock_get_ns(QEMUClockType type)
 }
 }
 
+static void qemu_virtual_clock_set_ns(int64_t time)
+{
+return cpus_set_virtual_clock(time);
+}
+
 void init_clocks(QEMUTimerListNotifyCB *notify_cb)
 {
 QEMUClockType type;
@@ -675,3 +680,24 @@ bool qemu_clock_run_all_timers(void)
 
 return progress;
 }
+
+int64_t qemu_clock_advance_virtual_time(int64_t dest)
+{
+int64_t clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+AioContext *aio_context;
+aio_context = qemu_get_aio_context();
+while (clock < dest) {
+int64_t deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL,
+  QEMU_TIMER_ATTR_ALL);
+int64_t warp = qemu_soonest_timeout(dest - clock, deadline);
+
+qemu_virtual_clock_set_ns(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 
warp);
+
+qemu_clock_run_timers(QEMU_CLOCK_VIRTUAL);
+timerlist_run_timers(aio_context->tlg.tl[QEMU_CLOCK_VIRTUAL]);
+clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+}
+qemu_clock_notify(QEMU_CLOCK_VIRTUAL);
+
+return clock;
+}
-- 
2.39.2




[PATCH v3 4/6] qtest: move qtest_{get, set}_virtual_clock to accel/qtest/qtest.c

2024-05-30 Thread Pierrick Bouvier
Signed-off-by: Pierrick Bouvier 
---
 include/sysemu/qtest.h |  3 ---
 accel/qtest/qtest.c| 12 
 system/qtest.c | 12 
 3 files changed, 12 insertions(+), 15 deletions(-)

diff --git a/include/sysemu/qtest.h b/include/sysemu/qtest.h
index 45f3b7e1df5..c161d751650 100644
--- a/include/sysemu/qtest.h
+++ b/include/sysemu/qtest.h
@@ -34,9 +34,6 @@ void qtest_server_init(const char *qtest_chrdev, const char 
*qtest_log, Error **
 void qtest_server_set_send_handler(void (*send)(void *, const char *),
  void *opaque);
 void qtest_server_inproc_recv(void *opaque, const char *buf);
-
-int64_t qtest_get_virtual_clock(void);
-void qtest_set_virtual_clock(int64_t count);
 #endif
 
 #endif
diff --git a/accel/qtest/qtest.c b/accel/qtest/qtest.c
index 53182e6c2ae..bf14032d294 100644
--- a/accel/qtest/qtest.c
+++ b/accel/qtest/qtest.c
@@ -24,6 +24,18 @@
 #include "qemu/main-loop.h"
 #include "hw/core/cpu.h"
 
+static int64_t qtest_clock_counter;
+
+static int64_t qtest_get_virtual_clock(void)
+{
+return qatomic_read_i64(_clock_counter);
+}
+
+static void qtest_set_virtual_clock(int64_t count)
+{
+qatomic_set_i64(_clock_counter, count);
+}
+
 static int qtest_init_accel(MachineState *ms)
 {
 return 0;
diff --git a/system/qtest.c b/system/qtest.c
index e6f6b4e62d5..ba210780ec0 100644
--- a/system/qtest.c
+++ b/system/qtest.c
@@ -325,18 +325,6 @@ static void qtest_irq_handler(void *opaque, int n, int 
level)
 }
 }
 
-static int64_t qtest_clock_counter;
-
-int64_t qtest_get_virtual_clock(void)
-{
-return qatomic_read_i64(_clock_counter);
-}
-
-void qtest_set_virtual_clock(int64_t count)
-{
-qatomic_set_i64(_clock_counter, count);
-}
-
 static bool (*process_command_cb)(CharBackend *chr, gchar **words);
 
 void qtest_set_command_cb(bool (*pc_cb)(CharBackend *chr, gchar **words))
-- 
2.39.2




[PATCH v3 1/6] sysemu: add set_virtual_time to accel ops

2024-05-30 Thread Pierrick Bouvier
From: Alex Bennée 

We are about to remove direct calls to individual accelerators for
this information and will need a central point for plugins to hook
into time changes.

From: Alex Bennée 
Signed-off-by: Alex Bennée 
Reviewed-by: Philippe Mathieu-Daudé 
Signed-off-by: Pierrick Bouvier 
---
 include/sysemu/accel-ops.h | 18 +-
 include/sysemu/cpu-timers.h|  3 ++-
 ...et-virtual-clock.c => cpus-virtual-clock.c} |  5 +
 system/cpus.c  | 11 +++
 stubs/meson.build  |  2 +-
 5 files changed, 36 insertions(+), 3 deletions(-)
 rename stubs/{cpus-get-virtual-clock.c => cpus-virtual-clock.c} (68%)

diff --git a/include/sysemu/accel-ops.h b/include/sysemu/accel-ops.h
index ef91fc28bbd..a0886722305 100644
--- a/include/sysemu/accel-ops.h
+++ b/include/sysemu/accel-ops.h
@@ -20,7 +20,12 @@
 typedef struct AccelOpsClass AccelOpsClass;
 DECLARE_CLASS_CHECKERS(AccelOpsClass, ACCEL_OPS, TYPE_ACCEL_OPS)
 
-/* cpus.c operations interface */
+/**
+ * struct AccelOpsClass - accelerator interfaces
+ *
+ * This structure is used to abstract accelerator differences from the
+ * core CPU code. Not all have to be implemented.
+ */
 struct AccelOpsClass {
 /*< private >*/
 ObjectClass parent_class;
@@ -44,7 +49,18 @@ struct AccelOpsClass {
 
 void (*handle_interrupt)(CPUState *cpu, int mask);
 
+/**
+ * @get_virtual_clock: fetch virtual clock
+ * @set_virtual_clock: set virtual clock
+ *
+ * These allow the timer subsystem to defer to the accelerator to
+ * fetch time. The set function is needed if the accelerator wants
+ * to track the changes to time as the timer is warped through
+ * various timer events.
+ */
 int64_t (*get_virtual_clock)(void);
+void (*set_virtual_clock)(int64_t time);
+
 int64_t (*get_elapsed_ticks)(void);
 
 /* gdbstub hooks */
diff --git a/include/sysemu/cpu-timers.h b/include/sysemu/cpu-timers.h
index d86738a378d..7bfa960fbd6 100644
--- a/include/sysemu/cpu-timers.h
+++ b/include/sysemu/cpu-timers.h
@@ -96,8 +96,9 @@ int64_t cpu_get_clock(void);
 
 void qemu_timer_notify_cb(void *opaque, QEMUClockType type);
 
-/* get the VIRTUAL clock and VM elapsed ticks via the cpus accel interface */
+/* get/set VIRTUAL clock and VM elapsed ticks via the cpus accel interface */
 int64_t cpus_get_virtual_clock(void);
+void cpus_set_virtual_clock(int64_t new_time);
 int64_t cpus_get_elapsed_ticks(void);
 
 #endif /* SYSEMU_CPU_TIMERS_H */
diff --git a/stubs/cpus-get-virtual-clock.c b/stubs/cpus-virtual-clock.c
similarity index 68%
rename from stubs/cpus-get-virtual-clock.c
rename to stubs/cpus-virtual-clock.c
index fd447d53f3c..af7c1a1d403 100644
--- a/stubs/cpus-get-virtual-clock.c
+++ b/stubs/cpus-virtual-clock.c
@@ -6,3 +6,8 @@ int64_t cpus_get_virtual_clock(void)
 {
 return cpu_get_clock();
 }
+
+void cpus_set_virtual_clock(int64_t new_time)
+{
+/* do nothing */
+}
diff --git a/system/cpus.c b/system/cpus.c
index f8fa78f33d4..d3640c95030 100644
--- a/system/cpus.c
+++ b/system/cpus.c
@@ -229,6 +229,17 @@ int64_t cpus_get_virtual_clock(void)
 return cpu_get_clock();
 }
 
+/*
+ * Signal the new virtual time to the accelerator. This is only needed
+ * by accelerators that need to track the changes as we warp time.
+ */
+void cpus_set_virtual_clock(int64_t new_time)
+{
+if (cpus_accel && cpus_accel->set_virtual_clock) {
+cpus_accel->set_virtual_clock(new_time);
+}
+}
+
 /*
  * return the time elapsed in VM between vm_start and vm_stop.  Unless
  * icount is active, cpus_get_elapsed_ticks() uses units of the host CPU cycle
diff --git a/stubs/meson.build b/stubs/meson.build
index 3b9d42023cb..a1deafde08c 100644
--- a/stubs/meson.build
+++ b/stubs/meson.build
@@ -28,7 +28,7 @@ endif
 if have_block or have_ga
   stub_ss.add(files('replay-tools.c'))
   # stubs for hooks in util/main-loop.c, util/async.c etc.
-  stub_ss.add(files('cpus-get-virtual-clock.c'))
+  stub_ss.add(files('cpus-virtual-clock.c'))
   stub_ss.add(files('icount.c'))
   stub_ss.add(files('graph-lock.c'))
   if linux_io_uring.found()
-- 
2.39.2




Re: [PATCH v2 0/6] Implement icount=auto using TCG Plugins

2024-05-30 Thread Pierrick Bouvier

A v3 was just sent, with a fix to the algorithm used.

On 5/30/24 10:49, Pierrick Bouvier wrote:

The goal here is to be able to scale temporally execution of qemu-user/system,
using a given number of instructions per second.

We define a virtual clock, that can be late or in advance compared to real time.
When we are in advance, we slow execution (by sleeping) until catching real
time.

Finally, we should be able to cleanup icount=auto mode completely, and keep
icount usage for determistic purposes only.

It is built upon new TCG Plugins inline ops (store + conditional callbacks), now
merged on master.

Example in user-mode:

- Retrieve number of instructions to execute /bin/true
$ ./build/qemu-x86_64 -plugin ./build/tests/plugin/libinsn.so -d plugin 
/bin/true
cpu 0 insns: 120546
total insns: 120546
- Slow execution to match 5 seconds
$ time ./build/qemu-x86_64 -plugin 
./build/contrib/plugins/libips,ips=$((120546/5)) /bin/true
real0m4.985s

v2
--

- Added missing personal Signed-off-by for commits from Alex
- Fix bad rebase in stubs/meson.build
- move qtest_{get,set}_virtual_clock to accel/qtest/qtest.c
- A race condition was identified for plugins init/idle/resume, but is not
   related to this series, and will be fixed in another one:
   
https://lore.kernel.org/qemu-devel/20240529152219.825680-1-alex.ben...@linaro.org/

Alex Bennée (4):
   sysemu: add set_virtual_time to accel ops
   qtest: use cpu interface in qtest_clock_warp
   sysemu: generalise qtest_warp_clock as qemu_clock_advance_virtual_time
   plugins: add time control API

Pierrick Bouvier (2):
   qtest: move qtest_{get,set}_virtual_clock to accel/qtest/qtest.c
   contrib/plugins: add ips plugin example for cost modeling

  include/qemu/qemu-plugin.h|  23 ++
  include/qemu/timer.h  |  15 ++
  include/sysemu/accel-ops.h|  18 +-
  include/sysemu/cpu-timers.h   |   3 +-
  include/sysemu/qtest.h|   2 -
  accel/qtest/qtest.c   |  13 +
  contrib/plugins/ips.c | 239 ++
  plugins/api.c |  31 +++
  ...t-virtual-clock.c => cpus-virtual-clock.c} |   5 +
  system/cpus.c |  11 +
  system/qtest.c|  37 +--
  util/qemu-timer.c |  26 ++
  contrib/plugins/Makefile  |   1 +
  plugins/qemu-plugins.symbols  |   2 +
  stubs/meson.build |   2 +-
  15 files changed, 389 insertions(+), 39 deletions(-)
  create mode 100644 contrib/plugins/ips.c
  rename stubs/{cpus-get-virtual-clock.c => cpus-virtual-clock.c} (68%)



[PATCH v3 0/6] Implement icount=auto using TCG Plugins

2024-05-30 Thread Pierrick Bouvier
The goal here is to be able to scale temporally execution of qemu-user/system,
using a given number of instructions per second.

We define a virtual clock, that can be late or in advance compared to real time.
When we are in advance, we slow execution (by sleeping) until catching real
time.

Finally, we should be able to cleanup icount=auto mode completely, and keep
icount usage for determistic purposes only.

It is built upon new TCG Plugins inline ops (store + conditional callbacks), now
merged on master.

Example in user-mode:

- Retrieve number of instructions to execute /bin/true
$ ./build/qemu-x86_64 -plugin ./build/tests/plugin/libinsn.so -d plugin 
/bin/true
cpu 0 insns: 120546
total insns: 120546
- Slow execution to match 5 seconds
$ time ./build/qemu-x86_64 -plugin 
./build/contrib/plugins/libips,ips=$((120546/5)) /bin/true
real0m4.985s

Tested in system mode by booting a full debian system, and using:
$ sysbench cpu run
Performance decrease linearly with the given number of ips.

v2
--

- Added missing personal Signed-off-by for commits from Alex
- Fix bad rebase in stubs/meson.build
- move qtest_{get,set}_virtual_clock to accel/qtest/qtest.c
- A race condition was identified for plugins init/idle/resume, but is not
  related to this series, and will be fixed in another one:
  
https://lore.kernel.org/qemu-devel/20240529152219.825680-1-alex.ben...@linaro.org/

v3
--

- remove precise execution (per insn, per tb is enough)
- fixed algorithm used. Instead of comparing from start time of the system, we
  just check on a given quantum of time that we didn't run too fast. It is more
  simple, there is no need to track idle/resume events, and a vcpu can sleep
  correctly.
- use "sysbench cpu run" in system mode to check execution is slowed as 
expected.
- do not use idle/resume callback

Alex Bennée (4):
  sysemu: add set_virtual_time to accel ops
  qtest: use cpu interface in qtest_clock_warp
  sysemu: generalise qtest_warp_clock as qemu_clock_advance_virtual_time
  plugins: add time control API

Pierrick Bouvier (2):
  qtest: move qtest_{get,set}_virtual_clock to accel/qtest/qtest.c
  contrib/plugins: add ips plugin example for cost modeling

 include/qemu/qemu-plugin.h|  23 +++
 include/qemu/timer.h  |  15 ++
 include/sysemu/accel-ops.h|  18 +-
 include/sysemu/cpu-timers.h   |   3 +-
 include/sysemu/qtest.h|   2 -
 accel/qtest/qtest.c   |  13 ++
 contrib/plugins/ips.c | 164 ++
 plugins/api.c |  31 
 ...t-virtual-clock.c => cpus-virtual-clock.c} |   5 +
 system/cpus.c |  11 ++
 system/qtest.c|  37 +---
 util/qemu-timer.c |  26 +++
 contrib/plugins/Makefile  |   1 +
 plugins/qemu-plugins.symbols  |   2 +
 stubs/meson.build |   2 +-
 15 files changed, 314 insertions(+), 39 deletions(-)
 create mode 100644 contrib/plugins/ips.c
 rename stubs/{cpus-get-virtual-clock.c => cpus-virtual-clock.c} (68%)

-- 
2.39.2




[PATCH v3 6/6] contrib/plugins: add ips plugin example for cost modeling

2024-05-30 Thread Pierrick Bouvier
This plugin uses the new time control interface to make decisions
about the state of time during the emulation. The algorithm is
currently very simple. The user specifies an ips rate which applies
per core. If the core runs ahead of its allocated execution time the
plugin sleeps for a bit to let real time catch up. Either way time is
updated for the emulation as a function of total executed instructions
with some adjustments for cores that idle.

Examples


Slow down execution of /bin/true:
$ num_insn=$(./build/qemu-x86_64 -plugin ./build/tests/plugin/libinsn.so -d 
plugin /bin/true |& grep total | sed -e 's/.*: //')
$ time ./build/qemu-x86_64 -plugin 
./build/contrib/plugins/libips.so,ips=$(($num_insn/4)) /bin/true
real 4.000s

Boot a Linux kernel simulating a 250MHz cpu:
$ /build/qemu-system-x86_64 -kernel /boot/vmlinuz-6.1.0-21-amd64 -append 
"console=ttyS0" -plugin 
./build/contrib/plugins/libips.so,ips=$((250*1000*1000)) -smp 1 -m 512
check time until kernel panic on serial0

Tested in system mode by booting a full debian system, and using:
$ sysbench cpu run
Performance decrease linearly with the given number of ips.

Signed-off-by: Pierrick Bouvier 
---
 contrib/plugins/ips.c| 164 +++
 contrib/plugins/Makefile |   1 +
 2 files changed, 165 insertions(+)
 create mode 100644 contrib/plugins/ips.c

diff --git a/contrib/plugins/ips.c b/contrib/plugins/ips.c
new file mode 100644
index 000..db77729264b
--- /dev/null
+++ b/contrib/plugins/ips.c
@@ -0,0 +1,164 @@
+/*
+ * ips rate limiting plugin.
+ *
+ * This plugin can be used to restrict the execution of a system to a
+ * particular number of Instructions Per Second (ips). This controls
+ * time as seen by the guest so while wall-clock time may be longer
+ * from the guests point of view time will pass at the normal rate.
+ *
+ * This uses the new plugin API which allows the plugin to control
+ * system time.
+ *
+ * Copyright (c) 2023 Linaro Ltd
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include 
+#include 
+#include 
+
+QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION;
+
+/* how many times do we update time per sec */
+#define NUM_TIME_UPDATE_PER_SEC 10
+#define NSEC_IN_ONE_SEC (1000 * 1000 * 1000)
+
+static GMutex global_state_lock;
+
+static uint64_t max_insn_per_second = 1000 * 1000 * 1000; /* ips per core, per 
second */
+static uint64_t max_insn_per_quantum; /* trap every N instructions */
+static int64_t virtual_time_ns; /* last set virtual time */
+
+static const void *time_handle;
+
+typedef struct {
+uint64_t total_insn;
+uint64_t quantum_insn; /* insn in last quantum */
+int64_t last_quantum_time; /* time when last quantum started */
+} vCPUTime;
+
+struct qemu_plugin_scoreboard *vcpus;
+
+/* return epoch time in ns */
+static int64_t now_ns(void)
+{
+return g_get_real_time() * 1000;
+}
+
+static uint64_t num_insn_during(int64_t elapsed_ns)
+{
+double num_secs = elapsed_ns / (double) NSEC_IN_ONE_SEC;
+return num_secs * (double) max_insn_per_second;
+}
+
+static int64_t time_for_insn(uint64_t num_insn)
+{
+double num_secs = (double) num_insn / (double) max_insn_per_second;
+return num_secs * (double) NSEC_IN_ONE_SEC;
+}
+
+static void update_system_time(vCPUTime *vcpu)
+{
+int64_t elapsed_ns = now_ns() - vcpu->last_quantum_time;
+uint64_t max_insn = num_insn_during(elapsed_ns);
+
+if (vcpu->quantum_insn >= max_insn) {
+/* this vcpu ran faster than expected, so it has to sleep */
+uint64_t insn_advance = vcpu->quantum_insn - max_insn;
+uint64_t time_advance_ns = time_for_insn(insn_advance);
+int64_t sleep_us = time_advance_ns / 1000;
+g_usleep(sleep_us);
+}
+
+vcpu->total_insn += vcpu->quantum_insn;
+vcpu->quantum_insn = 0;
+vcpu->last_quantum_time = now_ns();
+
+/* based on total number of instructions, what should be the new time? */
+int64_t new_virtual_time = time_for_insn(vcpu->total_insn);
+
+g_mutex_lock(_state_lock);
+
+/* Time only moves forward. Another vcpu might have updated it already. */
+if (new_virtual_time > virtual_time_ns) {
+qemu_plugin_update_ns(time_handle, new_virtual_time);
+virtual_time_ns = new_virtual_time;
+}
+
+g_mutex_unlock(_state_lock);
+}
+
+static void vcpu_init(qemu_plugin_id_t id, unsigned int cpu_index)
+{
+vCPUTime *vcpu = qemu_plugin_scoreboard_find(vcpus, cpu_index);
+vcpu->total_insn = 0;
+vcpu->quantum_insn = 0;
+vcpu->last_quantum_time = now_ns();
+}
+
+static void vcpu_exit(qemu_plugin_id_t id, unsigned int cpu_index)
+{
+vCPUTime *vcpu = qemu_plugin_scoreboard_find(vcpus, cpu_index);
+update_system_time(vcpu);
+}
+
+static void every_quantum_insn(unsigned int cpu_index, void *udata)
+{
+vCPUTime *vcpu = qemu_plugin_scoreboard_find(vcpus, cpu_index);
+g_assert(vcpu->quantum_ins

[PATCH v3 5/6] plugins: add time control API

2024-05-30 Thread Pierrick Bouvier
From: Alex Bennée 

Expose the ability to control time through the plugin API. Only one
plugin can control time so it has to request control when loaded.
There are probably more corner cases to catch here.

From: Alex Bennée 
Signed-off-by: Alex Bennée 
Signed-off-by: Pierrick Bouvier 
---
 include/qemu/qemu-plugin.h   | 23 +++
 plugins/api.c| 31 +++
 plugins/qemu-plugins.symbols |  2 ++
 3 files changed, 56 insertions(+)

diff --git a/include/qemu/qemu-plugin.h b/include/qemu/qemu-plugin.h
index 95703d8fec1..80b1637cede 100644
--- a/include/qemu/qemu-plugin.h
+++ b/include/qemu/qemu-plugin.h
@@ -661,6 +661,29 @@ void qemu_plugin_register_vcpu_mem_inline_per_vcpu(
 qemu_plugin_u64 entry,
 uint64_t imm);
 
+/**
+ * qemu_plugin_request_time_control() - request the ability to control time
+ *
+ * This grants the plugin the ability to control system time. Only one
+ * plugin can control time so if multiple plugins request the ability
+ * all but the first will fail.
+ *
+ * Returns an opaque handle or NULL if fails
+ */
+const void *qemu_plugin_request_time_control(void);
+
+/**
+ * qemu_plugin_update_ns() - update system emulation time
+ * @handle: opaque handle returned by qemu_plugin_request_time_control()
+ * @time: time in nanoseconds
+ *
+ * This allows an appropriately authorised plugin (i.e. holding the
+ * time control handle) to move system time forward to @time.
+ *
+ * Start time is 0.
+ */
+void qemu_plugin_update_ns(const void *handle, int64_t time);
+
 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,
diff --git a/plugins/api.c b/plugins/api.c
index 5a0a7f8c712..26822b69ea2 100644
--- a/plugins/api.c
+++ b/plugins/api.c
@@ -39,6 +39,7 @@
 #include "qemu/main-loop.h"
 #include "qemu/plugin.h"
 #include "qemu/log.h"
+#include "qemu/timer.h"
 #include "tcg/tcg.h"
 #include "exec/exec-all.h"
 #include "exec/gdbstub.h"
@@ -583,3 +584,33 @@ uint64_t qemu_plugin_u64_sum(qemu_plugin_u64 entry)
 }
 return total;
 }
+
+/*
+ * Time control
+ */
+static bool has_control;
+
+const void *qemu_plugin_request_time_control(void)
+{
+if (!has_control) {
+has_control = true;
+return _control;
+}
+return NULL;
+}
+
+static void advance_virtual_time__async(CPUState *cpu, run_on_cpu_data data)
+{
+int64_t new_time = data.host_ulong;
+qemu_clock_advance_virtual_time(new_time);
+}
+
+void qemu_plugin_update_ns(const void *handle, int64_t new_time)
+{
+if (handle == _control) {
+/* Need to execute out of cpu_exec, so bql can be locked. */
+async_run_on_cpu(current_cpu,
+ advance_virtual_time__async,
+ RUN_ON_CPU_HOST_ULONG(new_time));
+}
+}
diff --git a/plugins/qemu-plugins.symbols b/plugins/qemu-plugins.symbols
index aa0a77a319f..ca773d8d9fe 100644
--- a/plugins/qemu-plugins.symbols
+++ b/plugins/qemu-plugins.symbols
@@ -38,6 +38,7 @@
   qemu_plugin_register_vcpu_tb_exec_cond_cb;
   qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu;
   qemu_plugin_register_vcpu_tb_trans_cb;
+  qemu_plugin_request_time_control;
   qemu_plugin_reset;
   qemu_plugin_scoreboard_free;
   qemu_plugin_scoreboard_find;
@@ -51,5 +52,6 @@
   qemu_plugin_u64_set;
   qemu_plugin_u64_sum;
   qemu_plugin_uninstall;
+  qemu_plugin_update_ns;
   qemu_plugin_vcpu_for_each;
 };
-- 
2.39.2




[PATCH v3 2/6] qtest: use cpu interface in qtest_clock_warp

2024-05-30 Thread Pierrick Bouvier
From: Alex Bennée 

This generalises the qtest_clock_warp code to use the AccelOps
handlers for updating its own sense of time. This will make the next
patch which moves the warp code closer to pure code motion.

From: Alex Bennée 
Signed-off-by: Alex Bennée 
Acked-by: Thomas Huth 
Signed-off-by: Pierrick Bouvier 
---
 include/sysemu/qtest.h | 1 +
 accel/qtest/qtest.c| 1 +
 system/qtest.c | 6 +++---
 3 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/include/sysemu/qtest.h b/include/sysemu/qtest.h
index b5d5fd34637..45f3b7e1df5 100644
--- a/include/sysemu/qtest.h
+++ b/include/sysemu/qtest.h
@@ -36,6 +36,7 @@ void qtest_server_set_send_handler(void (*send)(void *, const 
char *),
 void qtest_server_inproc_recv(void *opaque, const char *buf);
 
 int64_t qtest_get_virtual_clock(void);
+void qtest_set_virtual_clock(int64_t count);
 #endif
 
 #endif
diff --git a/accel/qtest/qtest.c b/accel/qtest/qtest.c
index f6056ac8361..53182e6c2ae 100644
--- a/accel/qtest/qtest.c
+++ b/accel/qtest/qtest.c
@@ -52,6 +52,7 @@ static void qtest_accel_ops_class_init(ObjectClass *oc, void 
*data)
 
 ops->create_vcpu_thread = dummy_start_vcpu_thread;
 ops->get_virtual_clock = qtest_get_virtual_clock;
+ops->set_virtual_clock = qtest_set_virtual_clock;
 };
 
 static const TypeInfo qtest_accel_ops_type = {
diff --git a/system/qtest.c b/system/qtest.c
index 6da58b3874e..ee8b139e982 100644
--- a/system/qtest.c
+++ b/system/qtest.c
@@ -332,14 +332,14 @@ int64_t qtest_get_virtual_clock(void)
 return qatomic_read_i64(_clock_counter);
 }
 
-static void qtest_set_virtual_clock(int64_t count)
+void qtest_set_virtual_clock(int64_t count)
 {
 qatomic_set_i64(_clock_counter, count);
 }
 
 static void qtest_clock_warp(int64_t dest)
 {
-int64_t clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+int64_t clock = cpus_get_virtual_clock();
 AioContext *aio_context;
 assert(qtest_enabled());
 aio_context = qemu_get_aio_context();
@@ -348,7 +348,7 @@ static void qtest_clock_warp(int64_t dest)
   QEMU_TIMER_ATTR_ALL);
 int64_t warp = qemu_soonest_timeout(dest - clock, deadline);
 
-qtest_set_virtual_clock(qtest_get_virtual_clock() + warp);
+cpus_set_virtual_clock(cpus_get_virtual_clock() + warp);
 
 qemu_clock_run_timers(QEMU_CLOCK_VIRTUAL);
 timerlist_run_timers(aio_context->tlg.tl[QEMU_CLOCK_VIRTUAL]);
-- 
2.39.2




[PATCH v2 6/6] contrib/plugins: add ips plugin example for cost modeling

2024-05-30 Thread Pierrick Bouvier
This plugin uses the new time control interface to make decisions
about the state of time during the emulation. The algorithm is
currently very simple. The user specifies an ips rate which applies
per core. If the core runs ahead of its allocated execution time the
plugin sleeps for a bit to let real time catch up. Either way time is
updated for the emulation as a function of total executed instructions
with some adjustments for cores that idle.

Examples


Slow down execution of /bin/true:
$ num_insn=$(./build/qemu-x86_64 -plugin ./build/tests/plugin/libinsn.so -d 
plugin /bin/true |& grep total | sed -e 's/.*: //')
$ time ./build/qemu-x86_64 -plugin 
./build/contrib/plugins/libips.so,ips=$(($num_insn/4)) /bin/true
real 4.000s

Boot a Linux kernel simulating a 250MHz cpu:
$ /build/qemu-system-x86_64 -kernel /boot/vmlinuz-6.1.0-21-amd64 -append 
"console=ttyS0" -plugin 
./build/contrib/plugins/libips.so,ips=$((250*1000*1000)) -smp 1 -m 512
check time until kernel panic on serial0

Signed-off-by: Pierrick Bouvier 
---
 contrib/plugins/ips.c| 239 +++
 contrib/plugins/Makefile |   1 +
 2 files changed, 240 insertions(+)
 create mode 100644 contrib/plugins/ips.c

diff --git a/contrib/plugins/ips.c b/contrib/plugins/ips.c
new file mode 100644
index 000..cf3159df391
--- /dev/null
+++ b/contrib/plugins/ips.c
@@ -0,0 +1,239 @@
+/*
+ * ips rate limiting plugin.
+ *
+ * This plugin can be used to restrict the execution of a system to a
+ * particular number of Instructions Per Second (ips). This controls
+ * time as seen by the guest so while wall-clock time may be longer
+ * from the guests point of view time will pass at the normal rate.
+ *
+ * This uses the new plugin API which allows the plugin to control
+ * system time.
+ *
+ * Copyright (c) 2023 Linaro Ltd
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include 
+#include 
+#include 
+
+QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION;
+
+/* how many times do we update time per sec */
+#define NUM_TIME_UPDATE_PER_SEC 10
+#define NSEC_IN_ONE_SEC (1000 * 1000 * 1000)
+
+static GMutex global_state_lock;
+
+static uint64_t insn_per_second = 1000 * 1000; /* ips per core, per second */
+static uint64_t insn_quantum; /* trap every N instructions */
+static bool precise_execution; /* count every instruction */
+static int64_t start_time_ns; /* time (ns since epoch) first vCPU started */
+static int64_t virtual_time_ns; /* last set virtual time */
+
+static const void *time_handle;
+
+typedef enum {
+UNKNOWN = 0,
+EXECUTING,
+IDLE,
+FINISHED
+} vCPUState;
+
+typedef struct {
+uint64_t counter;
+uint64_t track_insn;
+vCPUState state;
+/* timestamp when vCPU entered state */
+int64_t last_state_time;
+} vCPUTime;
+
+struct qemu_plugin_scoreboard *vcpus;
+
+/* return epoch time in ns */
+static int64_t now_ns(void)
+{
+return g_get_real_time() * 1000;
+}
+
+static uint64_t num_insn_during(int64_t elapsed_ns)
+{
+double num_secs = elapsed_ns / (double) NSEC_IN_ONE_SEC;
+return num_secs * (double) insn_per_second;
+}
+
+static int64_t time_for_insn(uint64_t num_insn)
+{
+double num_secs = (double) num_insn / (double) insn_per_second;
+return num_secs * (double) NSEC_IN_ONE_SEC;
+}
+
+static int64_t uptime_ns(void)
+{
+int64_t now = now_ns();
+g_assert(now >= start_time_ns);
+return now - start_time_ns;
+}
+
+static void vcpu_set_state(vCPUTime *vcpu, vCPUState new_state)
+{
+vcpu->last_state_time = now_ns();
+vcpu->state = new_state;
+}
+
+static void update_system_time(vCPUTime *vcpu)
+{
+/* flush remaining instructions */
+vcpu->counter += vcpu->track_insn;
+vcpu->track_insn = 0;
+
+int64_t uptime = uptime_ns();
+uint64_t expected_insn = num_insn_during(uptime);
+
+if (vcpu->counter >= expected_insn) {
+/* this vcpu ran faster than expected, so it has to sleep */
+uint64_t insn_advance = vcpu->counter - expected_insn;
+uint64_t time_advance_ns = time_for_insn(insn_advance);
+int64_t sleep_us = time_advance_ns / 1000;
+g_usleep(sleep_us);
+}
+
+/* based on number of instructions, what should be the new time? */
+int64_t new_virtual_time = time_for_insn(vcpu->counter);
+
+g_mutex_lock(_state_lock);
+
+/* Time only moves forward. Another vcpu might have updated it already. */
+if (new_virtual_time > virtual_time_ns) {
+qemu_plugin_update_ns(time_handle, new_virtual_time);
+virtual_time_ns = new_virtual_time;
+}
+
+g_mutex_unlock(_state_lock);
+}
+
+static void set_start_time()
+{
+g_mutex_lock(_state_lock);
+if (!start_time_ns) {
+start_time_ns = now_ns();
+}
+g_mutex_unlock(_state_lock);
+}
+
+static void vcpu_init(qemu_plugin_id_t id, unsigned int cpu_index)
+{
+vCPUTime *vcpu = qemu_plugin_scoreboard_find(vcpus, cpu

[PATCH v2 5/6] plugins: add time control API

2024-05-30 Thread Pierrick Bouvier
From: Alex Bennée 

Expose the ability to control time through the plugin API. Only one
plugin can control time so it has to request control when loaded.
There are probably more corner cases to catch here.

From: Alex Bennée 
Signed-off-by: Alex Bennée 
Signed-off-by: Pierrick Bouvier 
---
 include/qemu/qemu-plugin.h   | 23 +++
 plugins/api.c| 31 +++
 plugins/qemu-plugins.symbols |  2 ++
 3 files changed, 56 insertions(+)

diff --git a/include/qemu/qemu-plugin.h b/include/qemu/qemu-plugin.h
index 95703d8fec1..80b1637cede 100644
--- a/include/qemu/qemu-plugin.h
+++ b/include/qemu/qemu-plugin.h
@@ -661,6 +661,29 @@ void qemu_plugin_register_vcpu_mem_inline_per_vcpu(
 qemu_plugin_u64 entry,
 uint64_t imm);
 
+/**
+ * qemu_plugin_request_time_control() - request the ability to control time
+ *
+ * This grants the plugin the ability to control system time. Only one
+ * plugin can control time so if multiple plugins request the ability
+ * all but the first will fail.
+ *
+ * Returns an opaque handle or NULL if fails
+ */
+const void *qemu_plugin_request_time_control(void);
+
+/**
+ * qemu_plugin_update_ns() - update system emulation time
+ * @handle: opaque handle returned by qemu_plugin_request_time_control()
+ * @time: time in nanoseconds
+ *
+ * This allows an appropriately authorised plugin (i.e. holding the
+ * time control handle) to move system time forward to @time.
+ *
+ * Start time is 0.
+ */
+void qemu_plugin_update_ns(const void *handle, int64_t time);
+
 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,
diff --git a/plugins/api.c b/plugins/api.c
index 5a0a7f8c712..26822b69ea2 100644
--- a/plugins/api.c
+++ b/plugins/api.c
@@ -39,6 +39,7 @@
 #include "qemu/main-loop.h"
 #include "qemu/plugin.h"
 #include "qemu/log.h"
+#include "qemu/timer.h"
 #include "tcg/tcg.h"
 #include "exec/exec-all.h"
 #include "exec/gdbstub.h"
@@ -583,3 +584,33 @@ uint64_t qemu_plugin_u64_sum(qemu_plugin_u64 entry)
 }
 return total;
 }
+
+/*
+ * Time control
+ */
+static bool has_control;
+
+const void *qemu_plugin_request_time_control(void)
+{
+if (!has_control) {
+has_control = true;
+return _control;
+}
+return NULL;
+}
+
+static void advance_virtual_time__async(CPUState *cpu, run_on_cpu_data data)
+{
+int64_t new_time = data.host_ulong;
+qemu_clock_advance_virtual_time(new_time);
+}
+
+void qemu_plugin_update_ns(const void *handle, int64_t new_time)
+{
+if (handle == _control) {
+/* Need to execute out of cpu_exec, so bql can be locked. */
+async_run_on_cpu(current_cpu,
+ advance_virtual_time__async,
+ RUN_ON_CPU_HOST_ULONG(new_time));
+}
+}
diff --git a/plugins/qemu-plugins.symbols b/plugins/qemu-plugins.symbols
index aa0a77a319f..ca773d8d9fe 100644
--- a/plugins/qemu-plugins.symbols
+++ b/plugins/qemu-plugins.symbols
@@ -38,6 +38,7 @@
   qemu_plugin_register_vcpu_tb_exec_cond_cb;
   qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu;
   qemu_plugin_register_vcpu_tb_trans_cb;
+  qemu_plugin_request_time_control;
   qemu_plugin_reset;
   qemu_plugin_scoreboard_free;
   qemu_plugin_scoreboard_find;
@@ -51,5 +52,6 @@
   qemu_plugin_u64_set;
   qemu_plugin_u64_sum;
   qemu_plugin_uninstall;
+  qemu_plugin_update_ns;
   qemu_plugin_vcpu_for_each;
 };
-- 
2.39.2




[PATCH v2 2/6] qtest: use cpu interface in qtest_clock_warp

2024-05-30 Thread Pierrick Bouvier
From: Alex Bennée 

This generalises the qtest_clock_warp code to use the AccelOps
handlers for updating its own sense of time. This will make the next
patch which moves the warp code closer to pure code motion.

From: Alex Bennée 
Signed-off-by: Alex Bennée 
Acked-by: Thomas Huth 
Signed-off-by: Pierrick Bouvier 
---
 include/sysemu/qtest.h | 1 +
 accel/qtest/qtest.c| 1 +
 system/qtest.c | 6 +++---
 3 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/include/sysemu/qtest.h b/include/sysemu/qtest.h
index b5d5fd34637..45f3b7e1df5 100644
--- a/include/sysemu/qtest.h
+++ b/include/sysemu/qtest.h
@@ -36,6 +36,7 @@ void qtest_server_set_send_handler(void (*send)(void *, const 
char *),
 void qtest_server_inproc_recv(void *opaque, const char *buf);
 
 int64_t qtest_get_virtual_clock(void);
+void qtest_set_virtual_clock(int64_t count);
 #endif
 
 #endif
diff --git a/accel/qtest/qtest.c b/accel/qtest/qtest.c
index f6056ac8361..53182e6c2ae 100644
--- a/accel/qtest/qtest.c
+++ b/accel/qtest/qtest.c
@@ -52,6 +52,7 @@ static void qtest_accel_ops_class_init(ObjectClass *oc, void 
*data)
 
 ops->create_vcpu_thread = dummy_start_vcpu_thread;
 ops->get_virtual_clock = qtest_get_virtual_clock;
+ops->set_virtual_clock = qtest_set_virtual_clock;
 };
 
 static const TypeInfo qtest_accel_ops_type = {
diff --git a/system/qtest.c b/system/qtest.c
index 6da58b3874e..ee8b139e982 100644
--- a/system/qtest.c
+++ b/system/qtest.c
@@ -332,14 +332,14 @@ int64_t qtest_get_virtual_clock(void)
 return qatomic_read_i64(_clock_counter);
 }
 
-static void qtest_set_virtual_clock(int64_t count)
+void qtest_set_virtual_clock(int64_t count)
 {
 qatomic_set_i64(_clock_counter, count);
 }
 
 static void qtest_clock_warp(int64_t dest)
 {
-int64_t clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+int64_t clock = cpus_get_virtual_clock();
 AioContext *aio_context;
 assert(qtest_enabled());
 aio_context = qemu_get_aio_context();
@@ -348,7 +348,7 @@ static void qtest_clock_warp(int64_t dest)
   QEMU_TIMER_ATTR_ALL);
 int64_t warp = qemu_soonest_timeout(dest - clock, deadline);
 
-qtest_set_virtual_clock(qtest_get_virtual_clock() + warp);
+cpus_set_virtual_clock(cpus_get_virtual_clock() + warp);
 
 qemu_clock_run_timers(QEMU_CLOCK_VIRTUAL);
 timerlist_run_timers(aio_context->tlg.tl[QEMU_CLOCK_VIRTUAL]);
-- 
2.39.2




[PATCH v2 3/6] sysemu: generalise qtest_warp_clock as qemu_clock_advance_virtual_time

2024-05-30 Thread Pierrick Bouvier
From: Alex Bennée 

Move the key functionality of moving time forward into the clock
sub-system itself. This will allow us to plumb in time control into
plugins.

From: Alex Bennée 
Signed-off-by: Alex Bennée 
Signed-off-by: Pierrick Bouvier 
---
 include/qemu/timer.h | 15 +++
 system/qtest.c   | 25 +++--
 util/qemu-timer.c| 26 ++
 3 files changed, 44 insertions(+), 22 deletions(-)

diff --git a/include/qemu/timer.h b/include/qemu/timer.h
index 9a366e551fb..910587d8293 100644
--- a/include/qemu/timer.h
+++ b/include/qemu/timer.h
@@ -245,6 +245,21 @@ bool qemu_clock_run_timers(QEMUClockType type);
  */
 bool qemu_clock_run_all_timers(void);
 
+/**
+ * qemu_clock_advance_virtual_time(): advance the virtual time tick
+ * @target: target time in nanoseconds
+ *
+ * This function is used where the control of the flow of time has
+ * been delegated to outside the clock subsystem (be it qtest, icount
+ * or some other external source). You can ask the clock system to
+ * return @early at the first expired timer.
+ *
+ * Time can only move forward, attempts to reverse time would lead to
+ * an error.
+ *
+ * Returns: new virtual time.
+ */
+int64_t qemu_clock_advance_virtual_time(int64_t dest);
 
 /*
  * QEMUTimerList
diff --git a/system/qtest.c b/system/qtest.c
index ee8b139e982..e6f6b4e62d5 100644
--- a/system/qtest.c
+++ b/system/qtest.c
@@ -337,26 +337,6 @@ void qtest_set_virtual_clock(int64_t count)
 qatomic_set_i64(_clock_counter, count);
 }
 
-static void qtest_clock_warp(int64_t dest)
-{
-int64_t clock = cpus_get_virtual_clock();
-AioContext *aio_context;
-assert(qtest_enabled());
-aio_context = qemu_get_aio_context();
-while (clock < dest) {
-int64_t deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL,
-  QEMU_TIMER_ATTR_ALL);
-int64_t warp = qemu_soonest_timeout(dest - clock, deadline);
-
-cpus_set_virtual_clock(cpus_get_virtual_clock() + warp);
-
-qemu_clock_run_timers(QEMU_CLOCK_VIRTUAL);
-timerlist_run_timers(aio_context->tlg.tl[QEMU_CLOCK_VIRTUAL]);
-clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
-}
-qemu_clock_notify(QEMU_CLOCK_VIRTUAL);
-}
-
 static bool (*process_command_cb)(CharBackend *chr, gchar **words);
 
 void qtest_set_command_cb(bool (*pc_cb)(CharBackend *chr, gchar **words))
@@ -755,7 +735,8 @@ static void qtest_process_command(CharBackend *chr, gchar 
**words)
 ns = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL,
 QEMU_TIMER_ATTR_ALL);
 }
-qtest_clock_warp(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + ns);
+qemu_clock_advance_virtual_time(
+qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + ns);
 qtest_send_prefix(chr);
 qtest_sendf(chr, "OK %"PRIi64"\n",
 (int64_t)qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL));
@@ -781,7 +762,7 @@ static void qtest_process_command(CharBackend *chr, gchar 
**words)
 g_assert(words[1]);
 ret = qemu_strtoi64(words[1], NULL, 0, );
 g_assert(ret == 0);
-qtest_clock_warp(ns);
+qemu_clock_advance_virtual_time(ns);
 qtest_send_prefix(chr);
 qtest_sendf(chr, "OK %"PRIi64"\n",
 (int64_t)qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL));
diff --git a/util/qemu-timer.c b/util/qemu-timer.c
index 6a0de33dd2b..213114be68c 100644
--- a/util/qemu-timer.c
+++ b/util/qemu-timer.c
@@ -645,6 +645,11 @@ int64_t qemu_clock_get_ns(QEMUClockType type)
 }
 }
 
+static void qemu_virtual_clock_set_ns(int64_t time)
+{
+return cpus_set_virtual_clock(time);
+}
+
 void init_clocks(QEMUTimerListNotifyCB *notify_cb)
 {
 QEMUClockType type;
@@ -675,3 +680,24 @@ bool qemu_clock_run_all_timers(void)
 
 return progress;
 }
+
+int64_t qemu_clock_advance_virtual_time(int64_t dest)
+{
+int64_t clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+AioContext *aio_context;
+aio_context = qemu_get_aio_context();
+while (clock < dest) {
+int64_t deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL,
+  QEMU_TIMER_ATTR_ALL);
+int64_t warp = qemu_soonest_timeout(dest - clock, deadline);
+
+qemu_virtual_clock_set_ns(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 
warp);
+
+qemu_clock_run_timers(QEMU_CLOCK_VIRTUAL);
+timerlist_run_timers(aio_context->tlg.tl[QEMU_CLOCK_VIRTUAL]);
+clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+}
+qemu_clock_notify(QEMU_CLOCK_VIRTUAL);
+
+return clock;
+}
-- 
2.39.2




[PATCH v2 4/6] qtest: move qtest_{get, set}_virtual_clock to accel/qtest/qtest.c

2024-05-30 Thread Pierrick Bouvier
Signed-off-by: Pierrick Bouvier 
---
 include/sysemu/qtest.h |  3 ---
 accel/qtest/qtest.c| 12 
 system/qtest.c | 12 
 3 files changed, 12 insertions(+), 15 deletions(-)

diff --git a/include/sysemu/qtest.h b/include/sysemu/qtest.h
index 45f3b7e1df5..c161d751650 100644
--- a/include/sysemu/qtest.h
+++ b/include/sysemu/qtest.h
@@ -34,9 +34,6 @@ void qtest_server_init(const char *qtest_chrdev, const char 
*qtest_log, Error **
 void qtest_server_set_send_handler(void (*send)(void *, const char *),
  void *opaque);
 void qtest_server_inproc_recv(void *opaque, const char *buf);
-
-int64_t qtest_get_virtual_clock(void);
-void qtest_set_virtual_clock(int64_t count);
 #endif
 
 #endif
diff --git a/accel/qtest/qtest.c b/accel/qtest/qtest.c
index 53182e6c2ae..bf14032d294 100644
--- a/accel/qtest/qtest.c
+++ b/accel/qtest/qtest.c
@@ -24,6 +24,18 @@
 #include "qemu/main-loop.h"
 #include "hw/core/cpu.h"
 
+static int64_t qtest_clock_counter;
+
+static int64_t qtest_get_virtual_clock(void)
+{
+return qatomic_read_i64(_clock_counter);
+}
+
+static void qtest_set_virtual_clock(int64_t count)
+{
+qatomic_set_i64(_clock_counter, count);
+}
+
 static int qtest_init_accel(MachineState *ms)
 {
 return 0;
diff --git a/system/qtest.c b/system/qtest.c
index e6f6b4e62d5..ba210780ec0 100644
--- a/system/qtest.c
+++ b/system/qtest.c
@@ -325,18 +325,6 @@ static void qtest_irq_handler(void *opaque, int n, int 
level)
 }
 }
 
-static int64_t qtest_clock_counter;
-
-int64_t qtest_get_virtual_clock(void)
-{
-return qatomic_read_i64(_clock_counter);
-}
-
-void qtest_set_virtual_clock(int64_t count)
-{
-qatomic_set_i64(_clock_counter, count);
-}
-
 static bool (*process_command_cb)(CharBackend *chr, gchar **words);
 
 void qtest_set_command_cb(bool (*pc_cb)(CharBackend *chr, gchar **words))
-- 
2.39.2




[PATCH v2 1/6] sysemu: add set_virtual_time to accel ops

2024-05-30 Thread Pierrick Bouvier
From: Alex Bennée 

We are about to remove direct calls to individual accelerators for
this information and will need a central point for plugins to hook
into time changes.

From: Alex Bennée 
Signed-off-by: Alex Bennée 
Reviewed-by: Philippe Mathieu-Daudé 
Signed-off-by: Pierrick Bouvier 
---
 include/sysemu/accel-ops.h | 18 +-
 include/sysemu/cpu-timers.h|  3 ++-
 ...et-virtual-clock.c => cpus-virtual-clock.c} |  5 +
 system/cpus.c  | 11 +++
 stubs/meson.build  |  2 +-
 5 files changed, 36 insertions(+), 3 deletions(-)
 rename stubs/{cpus-get-virtual-clock.c => cpus-virtual-clock.c} (68%)

diff --git a/include/sysemu/accel-ops.h b/include/sysemu/accel-ops.h
index ef91fc28bbd..a0886722305 100644
--- a/include/sysemu/accel-ops.h
+++ b/include/sysemu/accel-ops.h
@@ -20,7 +20,12 @@
 typedef struct AccelOpsClass AccelOpsClass;
 DECLARE_CLASS_CHECKERS(AccelOpsClass, ACCEL_OPS, TYPE_ACCEL_OPS)
 
-/* cpus.c operations interface */
+/**
+ * struct AccelOpsClass - accelerator interfaces
+ *
+ * This structure is used to abstract accelerator differences from the
+ * core CPU code. Not all have to be implemented.
+ */
 struct AccelOpsClass {
 /*< private >*/
 ObjectClass parent_class;
@@ -44,7 +49,18 @@ struct AccelOpsClass {
 
 void (*handle_interrupt)(CPUState *cpu, int mask);
 
+/**
+ * @get_virtual_clock: fetch virtual clock
+ * @set_virtual_clock: set virtual clock
+ *
+ * These allow the timer subsystem to defer to the accelerator to
+ * fetch time. The set function is needed if the accelerator wants
+ * to track the changes to time as the timer is warped through
+ * various timer events.
+ */
 int64_t (*get_virtual_clock)(void);
+void (*set_virtual_clock)(int64_t time);
+
 int64_t (*get_elapsed_ticks)(void);
 
 /* gdbstub hooks */
diff --git a/include/sysemu/cpu-timers.h b/include/sysemu/cpu-timers.h
index d86738a378d..7bfa960fbd6 100644
--- a/include/sysemu/cpu-timers.h
+++ b/include/sysemu/cpu-timers.h
@@ -96,8 +96,9 @@ int64_t cpu_get_clock(void);
 
 void qemu_timer_notify_cb(void *opaque, QEMUClockType type);
 
-/* get the VIRTUAL clock and VM elapsed ticks via the cpus accel interface */
+/* get/set VIRTUAL clock and VM elapsed ticks via the cpus accel interface */
 int64_t cpus_get_virtual_clock(void);
+void cpus_set_virtual_clock(int64_t new_time);
 int64_t cpus_get_elapsed_ticks(void);
 
 #endif /* SYSEMU_CPU_TIMERS_H */
diff --git a/stubs/cpus-get-virtual-clock.c b/stubs/cpus-virtual-clock.c
similarity index 68%
rename from stubs/cpus-get-virtual-clock.c
rename to stubs/cpus-virtual-clock.c
index fd447d53f3c..af7c1a1d403 100644
--- a/stubs/cpus-get-virtual-clock.c
+++ b/stubs/cpus-virtual-clock.c
@@ -6,3 +6,8 @@ int64_t cpus_get_virtual_clock(void)
 {
 return cpu_get_clock();
 }
+
+void cpus_set_virtual_clock(int64_t new_time)
+{
+/* do nothing */
+}
diff --git a/system/cpus.c b/system/cpus.c
index f8fa78f33d4..d3640c95030 100644
--- a/system/cpus.c
+++ b/system/cpus.c
@@ -229,6 +229,17 @@ int64_t cpus_get_virtual_clock(void)
 return cpu_get_clock();
 }
 
+/*
+ * Signal the new virtual time to the accelerator. This is only needed
+ * by accelerators that need to track the changes as we warp time.
+ */
+void cpus_set_virtual_clock(int64_t new_time)
+{
+if (cpus_accel && cpus_accel->set_virtual_clock) {
+cpus_accel->set_virtual_clock(new_time);
+}
+}
+
 /*
  * return the time elapsed in VM between vm_start and vm_stop.  Unless
  * icount is active, cpus_get_elapsed_ticks() uses units of the host CPU cycle
diff --git a/stubs/meson.build b/stubs/meson.build
index 3b9d42023cb..a1deafde08c 100644
--- a/stubs/meson.build
+++ b/stubs/meson.build
@@ -28,7 +28,7 @@ endif
 if have_block or have_ga
   stub_ss.add(files('replay-tools.c'))
   # stubs for hooks in util/main-loop.c, util/async.c etc.
-  stub_ss.add(files('cpus-get-virtual-clock.c'))
+  stub_ss.add(files('cpus-virtual-clock.c'))
   stub_ss.add(files('icount.c'))
   stub_ss.add(files('graph-lock.c'))
   if linux_io_uring.found()
-- 
2.39.2




[PATCH v2 0/6] Implement icount=auto using TCG Plugins

2024-05-30 Thread Pierrick Bouvier
The goal here is to be able to scale temporally execution of qemu-user/system,
using a given number of instructions per second.

We define a virtual clock, that can be late or in advance compared to real time.
When we are in advance, we slow execution (by sleeping) until catching real
time.

Finally, we should be able to cleanup icount=auto mode completely, and keep
icount usage for determistic purposes only.

It is built upon new TCG Plugins inline ops (store + conditional callbacks), now
merged on master.

Example in user-mode:

- Retrieve number of instructions to execute /bin/true
$ ./build/qemu-x86_64 -plugin ./build/tests/plugin/libinsn.so -d plugin 
/bin/true
cpu 0 insns: 120546
total insns: 120546
- Slow execution to match 5 seconds
$ time ./build/qemu-x86_64 -plugin 
./build/contrib/plugins/libips,ips=$((120546/5)) /bin/true
real0m4.985s

v2
--

- Added missing personal Signed-off-by for commits from Alex
- Fix bad rebase in stubs/meson.build
- move qtest_{get,set}_virtual_clock to accel/qtest/qtest.c
- A race condition was identified for plugins init/idle/resume, but is not
  related to this series, and will be fixed in another one:
  
https://lore.kernel.org/qemu-devel/20240529152219.825680-1-alex.ben...@linaro.org/

Alex Bennée (4):
  sysemu: add set_virtual_time to accel ops
  qtest: use cpu interface in qtest_clock_warp
  sysemu: generalise qtest_warp_clock as qemu_clock_advance_virtual_time
  plugins: add time control API

Pierrick Bouvier (2):
  qtest: move qtest_{get,set}_virtual_clock to accel/qtest/qtest.c
  contrib/plugins: add ips plugin example for cost modeling

 include/qemu/qemu-plugin.h|  23 ++
 include/qemu/timer.h  |  15 ++
 include/sysemu/accel-ops.h|  18 +-
 include/sysemu/cpu-timers.h   |   3 +-
 include/sysemu/qtest.h|   2 -
 accel/qtest/qtest.c   |  13 +
 contrib/plugins/ips.c | 239 ++
 plugins/api.c |  31 +++
 ...t-virtual-clock.c => cpus-virtual-clock.c} |   5 +
 system/cpus.c |  11 +
 system/qtest.c|  37 +--
 util/qemu-timer.c |  26 ++
 contrib/plugins/Makefile  |   1 +
 plugins/qemu-plugins.symbols  |   2 +
 stubs/meson.build |   2 +-
 15 files changed, 389 insertions(+), 39 deletions(-)
 create mode 100644 contrib/plugins/ips.c
 rename stubs/{cpus-get-virtual-clock.c => cpus-virtual-clock.c} (68%)

-- 
2.39.2




Re: [PATCH 2/5] qtest: use cpu interface in qtest_clock_warp

2024-05-30 Thread Pierrick Bouvier

On 5/29/24 23:32, Paolo Bonzini wrote:

On Fri, May 17, 2024 at 12:21 AM Pierrick Bouvier
 wrote:


From: Alex Bennée 

This generalises the qtest_clock_warp code to use the AccelOps
handlers for updating its own sense of time. This will make the next
patch which moves the warp code closer to pure code motion.

From: Alex Bennée 
Signed-off-by: Alex Bennée 
Acked-by: Thomas Huth 
---
  include/sysemu/qtest.h | 1 +
  accel/qtest/qtest.c| 1 +
  system/qtest.c | 6 +++---
  3 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/include/sysemu/qtest.h b/include/sysemu/qtest.h
index b5d5fd34637..45f3b7e1df5 100644
--- a/include/sysemu/qtest.h
+++ b/include/sysemu/qtest.h
@@ -36,6 +36,7 @@ void qtest_server_set_send_handler(void (*send)(void *, const 
char *),
  void qtest_server_inproc_recv(void *opaque, const char *buf);

  int64_t qtest_get_virtual_clock(void);
+void qtest_set_virtual_clock(int64_t count);


You can move qtest_get_virtual_clock/qtest_set_virtual_clock to
accel/qtest/qtest.c instead, and make them static.

They are not used anymore in system/qtest.c, and it actually makes a
lot more sense that they aren't.



Changed for next revision,
thanks


Paolo


  #endif

  #endif
diff --git a/accel/qtest/qtest.c b/accel/qtest/qtest.c
index f6056ac8361..53182e6c2ae 100644
--- a/accel/qtest/qtest.c
+++ b/accel/qtest/qtest.c
@@ -52,6 +52,7 @@ static void qtest_accel_ops_class_init(ObjectClass *oc, void 
*data)

  ops->create_vcpu_thread = dummy_start_vcpu_thread;
  ops->get_virtual_clock = qtest_get_virtual_clock;
+ops->set_virtual_clock = qtest_set_virtual_clock;
  };

  static const TypeInfo qtest_accel_ops_type = {
diff --git a/system/qtest.c b/system/qtest.c
index 6da58b3874e..ee8b139e982 100644
--- a/system/qtest.c
+++ b/system/qtest.c
@@ -332,14 +332,14 @@ int64_t qtest_get_virtual_clock(void)
  return qatomic_read_i64(_clock_counter);
  }

-static void qtest_set_virtual_clock(int64_t count)
+void qtest_set_virtual_clock(int64_t count)
  {
  qatomic_set_i64(_clock_counter, count);
  }

  static void qtest_clock_warp(int64_t dest)
  {
-int64_t clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+int64_t clock = cpus_get_virtual_clock();
  AioContext *aio_context;
  assert(qtest_enabled());
  aio_context = qemu_get_aio_context();
@@ -348,7 +348,7 @@ static void qtest_clock_warp(int64_t dest)
QEMU_TIMER_ATTR_ALL);
  int64_t warp = qemu_soonest_timeout(dest - clock, deadline);

-qtest_set_virtual_clock(qtest_get_virtual_clock() + warp);
+cpus_set_virtual_clock(cpus_get_virtual_clock() + warp);

  qemu_clock_run_timers(QEMU_CLOCK_VIRTUAL);
  timerlist_run_timers(aio_context->tlg.tl[QEMU_CLOCK_VIRTUAL]);
--
2.39.2





Re: [RFC PATCH] cpus: split qemu_init_vcpu and delay vCPU thread creation

2024-05-30 Thread Pierrick Bouvier

On 5/29/24 08:22, Alex Bennée wrote:

This ensures we don't start the thread until cpu_common_realizefn has
finished. This ensures that plugins will always run
qemu_plugin_vcpu_init__async first before any other states. It doesn't
totally eliminate the race that plugin_cpu_update__locked has to work
around though. I found this while reviewing the ips plugin which makes
heavy use of the vcpu phase callbacks.

An alternative might be to move the explicit creation of vCPU threads
to qdev_machine_creation_done()? It doesn't affect user-mode which
already has a thread to execute in and ensures the QOM object has
completed creation in cpu_create() before continuing.

Signed-off-by: Alex Bennée 
Cc: Pierrick Bouvier 
Cc: Philippe Mathieu-Daudé 
---
  include/hw/core/cpu.h  |  8 
  accel/tcg/user-exec-stub.c |  5 +
  hw/core/cpu-common.c   |  7 ++-
  plugins/core.c |  5 +
  system/cpus.c  | 15 ++-
  5 files changed, 34 insertions(+), 6 deletions(-)

diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h
index bb398e8237..6920699585 100644
--- a/include/hw/core/cpu.h
+++ b/include/hw/core/cpu.h
@@ -1041,6 +1041,14 @@ void end_exclusive(void);
   */
  void qemu_init_vcpu(CPUState *cpu);
  
+/**

+ * qemu_start_vcpu:
+ * @cpu: The vCPU to start.
+ *
+ * Create the vCPU thread and start it running.
+ */
+void qemu_start_vcpu(CPUState *cpu);
+
  #define SSTEP_ENABLE  0x1  /* Enable simulated HW single stepping */
  #define SSTEP_NOIRQ   0x2  /* Do not use IRQ while single stepping */
  #define SSTEP_NOTIMER 0x4  /* Do not Timers while single stepping */
diff --git a/accel/tcg/user-exec-stub.c b/accel/tcg/user-exec-stub.c
index 4fbe2dbdc8..162bb72bbe 100644
--- a/accel/tcg/user-exec-stub.c
+++ b/accel/tcg/user-exec-stub.c
@@ -18,6 +18,11 @@ void cpu_exec_reset_hold(CPUState *cpu)
  {
  }
  
+void qemu_start_vcpu(CPUState *cpu)

+{
+/* NOP for user-mode, we already have a thread */
+}
+
  /* User mode emulation does not support record/replay yet.  */
  
  bool replay_exception(void)

diff --git a/hw/core/cpu-common.c b/hw/core/cpu-common.c
index 0f0a247f56..68895ddd59 100644
--- a/hw/core/cpu-common.c
+++ b/hw/core/cpu-common.c
@@ -230,7 +230,12 @@ static void cpu_common_realizefn(DeviceState *dev, Error 
**errp)
  }
  #endif
  
-/* NOTE: latest generic point where the cpu is fully realized */

+/*
+ * With everything set up we can finally start the vCPU thread.
+ * This is a NOP for linux-user.
+ * NOTE: latest generic point where the cpu is fully realized
+ */
+qemu_start_vcpu(cpu);
  }
  
  static void cpu_common_unrealizefn(DeviceState *dev)

diff --git a/plugins/core.c b/plugins/core.c
index 0726bc7f25..1e5da7853b 100644
--- a/plugins/core.c
+++ b/plugins/core.c
@@ -65,6 +65,11 @@ 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);
  
+/*

+ * There is a race condition between the starting of the vCPU
+ * thread at the end of cpu_common_realizefn and when realized is
+ * finally set.
+ */


Could we simply have an active wait here?
while (!DEVICE(cpu)->realized) {}

We have a guarantee it will be realized shortly, and if it's too hard to 
have a proper synchronization mechanism (introduce a realize_cond?), 
then waiting for the proper state does not seem too bad.


It's a bit strange for me to document an existing race condition, 
instead of finding a solution.



  if (DEVICE(cpu)->realized) {
  async_run_on_cpu(cpu, plugin_cpu_update__async, mask);
  } else {
diff --git a/system/cpus.c b/system/cpus.c
index d3640c9503..7dd8464c5e 100644
--- a/system/cpus.c
+++ b/system/cpus.c
@@ -488,11 +488,13 @@ void cpus_kick_thread(CPUState *cpu)
  
  void qemu_cpu_kick(CPUState *cpu)

  {
-qemu_cond_broadcast(cpu->halt_cond);
-if (cpus_accel->kick_vcpu_thread) {
-cpus_accel->kick_vcpu_thread(cpu);
-} else { /* default */
-cpus_kick_thread(cpu);
+if (cpu->halt_cond) {
+qemu_cond_broadcast(cpu->halt_cond);
+if (cpus_accel->kick_vcpu_thread) {
+cpus_accel->kick_vcpu_thread(cpu);
+} else { /* default */
+cpus_kick_thread(cpu);
+}
  }
  }
  
@@ -674,7 +676,10 @@ void qemu_init_vcpu(CPUState *cpu)

  cpu->num_ases = 1;
  cpu_address_space_init(cpu, 0, "cpu-memory", cpu->memory);
  }
+}
  
+void qemu_start_vcpu(CPUState *cpu)

+{
  /* accelerators all implement the AccelOpsClass */
  g_assert(cpus_accel != NULL && cpus_accel->create_vcpu_thread != NULL);
  cpus_accel->create_vcpu_thread(cpu);


Re: [PATCH 1/5] sysemu: add set_virtual_time to accel ops

2024-05-30 Thread Pierrick Bouvier

On 5/29/24 23:30, Paolo Bonzini wrote:

On Fri, May 17, 2024 at 12:21 AM Pierrick Bouvier
 wrote:

diff --git a/stubs/meson.build b/stubs/meson.build
index 3b9d42023cb..672213b7482 100644
--- a/stubs/meson.build
+++ b/stubs/meson.build
@@ -3,6 +3,11 @@
  # below, so that it is clear who needs the stubbed functionality.

  stub_ss.add(files('cpu-get-clock.c'))
+stub_ss.add(files('cpus-virtual-clock.c'))
+stub_ss.add(files('qemu-timer-notify-cb.c'))
+stub_ss.add(files('icount.c'))
+stub_ss.add(files('dump.c'))
+stub_ss.add(files('error-printf.c'))


Was there a problem when rebasing?

Paolo



Hi Paolo,

Yes, Philippe made the same comment. I'll fix it in next revision.
Thanks!


  stub_ss.add(files('fdset.c'))
  stub_ss.add(files('iothread-lock.c'))
  stub_ss.add(files('is-daemonized.c'))
@@ -28,7 +33,6 @@ endif
  if have_block or have_ga
stub_ss.add(files('replay-tools.c'))
# stubs for hooks in util/main-loop.c, util/async.c etc.
-  stub_ss.add(files('cpus-get-virtual-clock.c'))
stub_ss.add(files('icount.c'))
stub_ss.add(files('graph-lock.c'))
if linux_io_uring.found()
--
2.39.2





Re: [PATCH 1/5] sysemu: add set_virtual_time to accel ops

2024-05-29 Thread Pierrick Bouvier

On 5/29/24 05:29, Philippe Mathieu-Daudé wrote:

On 17/5/24 00:20, Pierrick Bouvier wrote:

From: Alex Bennée 

We are about to remove direct calls to individual accelerators for
this information and will need a central point for plugins to hook
into time changes.

From: Alex Bennée 
Signed-off-by: Alex Bennée 
Reviewed-by: Philippe Mathieu-Daudé 
---
   include/sysemu/accel-ops.h | 18 +-
   include/sysemu/cpu-timers.h|  3 ++-
   ...et-virtual-clock.c => cpus-virtual-clock.c} |  5 +
   system/cpus.c  | 11 +++
   stubs/meson.build  |  6 +-
   5 files changed, 40 insertions(+), 3 deletions(-)




diff --git a/stubs/meson.build b/stubs/meson.build
index 3b9d42023cb..672213b7482 100644
--- a/stubs/meson.build
+++ b/stubs/meson.build
@@ -3,6 +3,11 @@
   # below, so that it is clear who needs the stubbed functionality.
   
   stub_ss.add(files('cpu-get-clock.c'))

+stub_ss.add(files('cpus-virtual-clock.c'))


Are these lines <...


+stub_ss.add(files('qemu-timer-notify-cb.c'))
+stub_ss.add(files('icount.c'))
+stub_ss.add(files('dump.c'))
+stub_ss.add(files('error-printf.c'))


...> due to a failed git-rebase?



You're right, fixed this!


   stub_ss.add(files('fdset.c'))
   stub_ss.add(files('iothread-lock.c'))
   stub_ss.add(files('is-daemonized.c'))
@@ -28,7 +33,6 @@ endif
   if have_block or have_ga
 stub_ss.add(files('replay-tools.c'))
 # stubs for hooks in util/main-loop.c, util/async.c etc.
-  stub_ss.add(files('cpus-get-virtual-clock.c'))
 stub_ss.add(files('icount.c'))
 stub_ss.add(files('graph-lock.c'))
 if linux_io_uring.found()




Re: [PATCH 5/5] contrib/plugins: add ips plugin example for cost modeling

2024-05-29 Thread Pierrick Bouvier

On 5/29/24 05:13, Alex Bennée wrote:

Pierrick Bouvier  writes:

(Added Philip to CC)


On 5/28/24 12:57, Alex Bennée wrote:

Pierrick Bouvier  writes:


On 5/28/24 12:14, Alex Bennée wrote:

Pierrick Bouvier  writes:


This plugin uses the new time control interface to make decisions
about the state of time during the emulation. The algorithm is
currently very simple. The user specifies an ips rate which applies
per core.





+static void vcpu_init(qemu_plugin_id_t id, unsigned int cpu_index)
+{
+vCPUTime *vcpu = qemu_plugin_scoreboard_find(vcpus, cpu_index);
+/* ensure start time is set first */
+set_start_time();
+/* start counter from absolute time reference */
+vcpu->counter = num_insn_during(uptime_ns());
+vcpu_set_state(vcpu, EXECUTING);
+}
+
+static void vcpu_idle(qemu_plugin_id_t id, unsigned int cpu_index)
+{
+vCPUTime *vcpu = qemu_plugin_scoreboard_find(vcpus, cpu_index);
+vcpu_set_state(vcpu, IDLE);
+}
+
+static void vcpu_resume(qemu_plugin_id_t id, unsigned int cpu_index)
+{
+vCPUTime *vcpu = qemu_plugin_scoreboard_find(vcpus, cpu_index);
+g_assert(vcpu->state == IDLE);

I'm triggering a weird race here:
 (gdb) b vcpu_init
 Breakpoint 1 at 0x77fa15f7: file 
/home/alex/lsrc/qemu.git/contrib/plugins/ips.c, line 127.
 (gdb) r
 The program being debugged has been started already.
 Start it from the beginning? (y or n) y
 Starting program:
/home/alex/lsrc/qemu.git/builds/arm.debug/qemu-system-aarch64
-machine type=virt,virtualization=on,pflash0=rom,pflash1=efivars
-cpu cortex-a57 -smp 32 -accel tcg -device
virtio-net-pci,netdev=unet -device virtio-scsi-pci -device
scsi-hd,drive=hd -netdev user,id=unet,hostfwd=tcp::-:22
-blockdev
driver=raw,node-name=hd,file.driver=host_device,file.filename=/dev/zen-ssd2/trixie-arm64,discard=unmap
-serial mon:stdio -blockdev
node-name=rom,driver=file,filename=/home/alex/lsrc/qemu.git/builds/arm.debug/pc-bios/edk2-aarch64-code.fd,read-only=true
-blockdev
node-name=efivars,driver=file,filename=/home/alex/images/qemu-arm64-efivars
-m 8192 -object memory-backend-memfd,id=mem,size=8G,share=on -kernel
/home/alex/lsrc/linux.git/builds/arm64/arch/arm64/boot/Image -append
root=/dev/sda2\ console=ttyAMA0 -plugin
contrib/plugins/libips.so,ips=10
 [Thread debugging using libthread_db enabled]
 Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
 [New Thread 0x7fffe72006c0 (LWP 360538)]
 [New Thread 0x7fffe68006c0 (LWP 360540)]
 [New Thread 0x7fffe5e006c0 (LWP 360541)]
 [New Thread 0x7fffe54006c0 (LWP 360542)]
 [New Thread 0x7fffe4a006c0 (LWP 360543)]
 [New Thread 0x7fffdfe006c0 (LWP 360544)]
 [New Thread 0x7fffdf4006c0 (LWP 360545)]
 [New Thread 0x7fffdea006c0 (LWP 360546)]
 [Switching to Thread 0x7fffdf4006c0 (LWP 360545)]
 Thread 8 "qemu-system-aar" hit Breakpoint 1, vcpu_init
(id=10457908569352202058, cpu_index=0) at
/home/alex/lsrc/qemu.git/contrib/plugins/ips.c:127
 127 vCPUTime *vcpu = qemu_plugin_scoreboard_find(vcpus, cpu_index);
 (gdb) c
 Continuing.
 [New Thread 0x7fffde0006c0 (LWP 360548)]
 [Switching to Thread 0x7fffdea006c0 (LWP 360546)]
 Thread 9 "qemu-system-aar" hit Breakpoint 1, vcpu_init
(id=10457908569352202058, cpu_index=1) at
/home/alex/lsrc/qemu.git/contrib/plugins/ips.c:127
 127 vCPUTime *vcpu = qemu_plugin_scoreboard_find(vcpus, cpu_index);
 (gdb)
 Continuing.
 [New Thread 0x7fffdd6006c0 (LWP 360549)]
 [Switching to Thread 0x7fffde0006c0 (LWP 360548)]
 Thread 10 "qemu-system-aar" hit Breakpoint 1, vcpu_init
(id=10457908569352202058, cpu_index=2) at
/home/alex/lsrc/qemu.git/contrib/plugins/ips.c:127
 127 vCPUTime *vcpu = qemu_plugin_scoreboard_find(vcpus, cpu_index);
 (gdb)
 Continuing.
 [New Thread 0x7fffdcc006c0 (LWP 360550)]
 [Switching to Thread 0x7fffdd6006c0 (LWP 360549)]
 Thread 11 "qemu-system-aar" hit Breakpoint 1, vcpu_init
(id=10457908569352202058, cpu_index=3) at
/home/alex/lsrc/qemu.git/contrib/plugins/ips.c:127
 127 vCPUTime *vcpu = qemu_plugin_scoreboard_find(vcpus, cpu_index);
 (gdb)
 Continuing.
 [New Thread 0x7fffd3e006c0 (LWP 360551)]
 [Switching to Thread 0x7fffdcc006c0 (LWP 360550)]
 Thread 12 "qemu-system-aar" hit Breakpoint 1, vcpu_init
(id=10457908569352202058, cpu_index=4) at
/home/alex/lsrc/qemu.git/contrib/plugins/ips.c:127
 127 vCPUTime *vcpu = qemu_plugin_scoreboard_find(vcpus, cpu_index);
 (gdb) n
 129 set_start_time();
 (gdb)
 131 vcpu->counter = num_insn_during(uptime_ns());
 (gdb)
 132 vcpu_set_state(vcpu, EXECUTING);
 (gdb)
 133 }
 (gdb) p vcpu->state
 $1 = EXECUTING
 (gdb) p >state
 $2 = (vCPUState *) 0x57c6b5d0
 (gdb) watch *(vCPUState *) 0x57c6b5d0
 Hardwa

Re: [PATCH 5/5] contrib/plugins: add ips plugin example for cost modeling

2024-05-28 Thread Pierrick Bouvier

On 5/28/24 12:57, Alex Bennée wrote:

Pierrick Bouvier  writes:


On 5/28/24 12:14, Alex Bennée wrote:

Pierrick Bouvier  writes:


This plugin uses the new time control interface to make decisions
about the state of time during the emulation. The algorithm is
currently very simple. The user specifies an ips rate which applies
per core. If the core runs ahead of its allocated execution time the
plugin sleeps for a bit to let real time catch up. Either way time is
updated for the emulation as a function of total executed instructions
with some adjustments for cores that idle.

Examples


Slow down execution of /bin/true:
$ num_insn=$(./build/qemu-x86_64 -plugin ./build/tests/plugin/libinsn.so -d plugin 
/bin/true |& grep total | sed -e 's/.*: //')
$ time ./build/qemu-x86_64 -plugin 
./build/contrib/plugins/libips.so,ips=$(($num_insn/4)) /bin/true
real 4.000s

Boot a Linux kernel simulating a 250MHz cpu:
$ /build/qemu-system-x86_64 -kernel /boot/vmlinuz-6.1.0-21-amd64 -append 
"console=ttyS0" -plugin 
./build/contrib/plugins/libips.so,ips=$((250*1000*1000)) -smp 1 -m 512
check time until kernel panic on serial0

Signed-off-by: Pierrick Bouvier 
---
   contrib/plugins/ips.c| 239 +++
   contrib/plugins/Makefile |   1 +
   2 files changed, 240 insertions(+)
   create mode 100644 contrib/plugins/ips.c

diff --git a/contrib/plugins/ips.c b/contrib/plugins/ips.c
new file mode 100644
index 000..cf3159df391
--- /dev/null
+++ b/contrib/plugins/ips.c
@@ -0,0 +1,239 @@
+/*
+ * ips rate limiting plugin.
+ *
+ * This plugin can be used to restrict the execution of a system to a
+ * particular number of Instructions Per Second (ips). This controls
+ * time as seen by the guest so while wall-clock time may be longer
+ * from the guests point of view time will pass at the normal rate.
+ *
+ * This uses the new plugin API which allows the plugin to control
+ * system time.
+ *
+ * Copyright (c) 2023 Linaro Ltd
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include 
+#include 
+#include 
+
+QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION;
+
+/* how many times do we update time per sec */
+#define NUM_TIME_UPDATE_PER_SEC 10
+#define NSEC_IN_ONE_SEC (1000 * 1000 * 1000)
+
+static GMutex global_state_lock;
+
+static uint64_t insn_per_second = 1000 * 1000; /* ips per core, per second */
+static uint64_t insn_quantum; /* trap every N instructions */
+static bool precise_execution; /* count every instruction */
+static int64_t start_time_ns; /* time (ns since epoch) first vCPU started */
+static int64_t virtual_time_ns; /* last set virtual time */
+
+static const void *time_handle;
+
+typedef enum {
+UNKNOWN = 0,
+EXECUTING,
+IDLE,
+FINISHED
+} vCPUState;
+
+typedef struct {
+uint64_t counter;
+uint64_t track_insn;
+vCPUState state;
+/* timestamp when vCPU entered state */
+int64_t last_state_time;
+} vCPUTime;
+
+struct qemu_plugin_scoreboard *vcpus;
+
+/* return epoch time in ns */
+static int64_t now_ns(void)
+{
+return g_get_real_time() * 1000;
+}
+
+static uint64_t num_insn_during(int64_t elapsed_ns)
+{
+double num_secs = elapsed_ns / (double) NSEC_IN_ONE_SEC;
+return num_secs * (double) insn_per_second;
+}
+
+static int64_t time_for_insn(uint64_t num_insn)
+{
+double num_secs = (double) num_insn / (double) insn_per_second;
+return num_secs * (double) NSEC_IN_ONE_SEC;
+}
+
+static int64_t uptime_ns(void)
+{
+int64_t now = now_ns();
+g_assert(now >= start_time_ns);
+return now - start_time_ns;
+}
+
+static void vcpu_set_state(vCPUTime *vcpu, vCPUState new_state)
+{
+vcpu->last_state_time = now_ns();
+vcpu->state = new_state;
+}
+
+static void update_system_time(vCPUTime *vcpu)
+{
+/* flush remaining instructions */
+vcpu->counter += vcpu->track_insn;
+vcpu->track_insn = 0;
+
+int64_t uptime = uptime_ns();
+uint64_t expected_insn = num_insn_during(uptime);
+
+if (vcpu->counter >= expected_insn) {
+/* this vcpu ran faster than expected, so it has to sleep */
+uint64_t insn_advance = vcpu->counter - expected_insn;
+uint64_t time_advance_ns = time_for_insn(insn_advance);
+int64_t sleep_us = time_advance_ns / 1000;
+g_usleep(sleep_us);
+}
+
+/* based on number of instructions, what should be the new time? */
+int64_t new_virtual_time = time_for_insn(vcpu->counter);
+
+g_mutex_lock(_state_lock);
+
+/* Time only moves forward. Another vcpu might have updated it already. */
+if (new_virtual_time > virtual_time_ns) {
+qemu_plugin_update_ns(time_handle, new_virtual_time);
+virtual_time_ns = new_virtual_time;
+}
+
+g_mutex_unlock(_state_lock);
+}
+
+static void set_start_time()
+{
+g_mutex_lock(_state_lock);
+if (!start_time_ns) {
+start_time_ns = now_ns();
+}
+g_mutex_unlock(_state_lock);

Re: [PATCH 5/5] contrib/plugins: add ips plugin example for cost modeling

2024-05-28 Thread Pierrick Bouvier

On 5/28/24 12:14, Alex Bennée wrote:

Pierrick Bouvier  writes:


This plugin uses the new time control interface to make decisions
about the state of time during the emulation. The algorithm is
currently very simple. The user specifies an ips rate which applies
per core. If the core runs ahead of its allocated execution time the
plugin sleeps for a bit to let real time catch up. Either way time is
updated for the emulation as a function of total executed instructions
with some adjustments for cores that idle.

Examples


Slow down execution of /bin/true:
$ num_insn=$(./build/qemu-x86_64 -plugin ./build/tests/plugin/libinsn.so -d plugin 
/bin/true |& grep total | sed -e 's/.*: //')
$ time ./build/qemu-x86_64 -plugin 
./build/contrib/plugins/libips.so,ips=$(($num_insn/4)) /bin/true
real 4.000s

Boot a Linux kernel simulating a 250MHz cpu:
$ /build/qemu-system-x86_64 -kernel /boot/vmlinuz-6.1.0-21-amd64 -append 
"console=ttyS0" -plugin 
./build/contrib/plugins/libips.so,ips=$((250*1000*1000)) -smp 1 -m 512
check time until kernel panic on serial0

Signed-off-by: Pierrick Bouvier 
---
  contrib/plugins/ips.c| 239 +++
  contrib/plugins/Makefile |   1 +
  2 files changed, 240 insertions(+)
  create mode 100644 contrib/plugins/ips.c

diff --git a/contrib/plugins/ips.c b/contrib/plugins/ips.c
new file mode 100644
index 000..cf3159df391
--- /dev/null
+++ b/contrib/plugins/ips.c
@@ -0,0 +1,239 @@
+/*
+ * ips rate limiting plugin.
+ *
+ * This plugin can be used to restrict the execution of a system to a
+ * particular number of Instructions Per Second (ips). This controls
+ * time as seen by the guest so while wall-clock time may be longer
+ * from the guests point of view time will pass at the normal rate.
+ *
+ * This uses the new plugin API which allows the plugin to control
+ * system time.
+ *
+ * Copyright (c) 2023 Linaro Ltd
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include 
+#include 
+#include 
+
+QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION;
+
+/* how many times do we update time per sec */
+#define NUM_TIME_UPDATE_PER_SEC 10
+#define NSEC_IN_ONE_SEC (1000 * 1000 * 1000)
+
+static GMutex global_state_lock;
+
+static uint64_t insn_per_second = 1000 * 1000; /* ips per core, per second */
+static uint64_t insn_quantum; /* trap every N instructions */
+static bool precise_execution; /* count every instruction */
+static int64_t start_time_ns; /* time (ns since epoch) first vCPU started */
+static int64_t virtual_time_ns; /* last set virtual time */
+
+static const void *time_handle;
+
+typedef enum {
+UNKNOWN = 0,
+EXECUTING,
+IDLE,
+FINISHED
+} vCPUState;
+
+typedef struct {
+uint64_t counter;
+uint64_t track_insn;
+vCPUState state;
+/* timestamp when vCPU entered state */
+int64_t last_state_time;
+} vCPUTime;
+
+struct qemu_plugin_scoreboard *vcpus;
+
+/* return epoch time in ns */
+static int64_t now_ns(void)
+{
+return g_get_real_time() * 1000;
+}
+
+static uint64_t num_insn_during(int64_t elapsed_ns)
+{
+double num_secs = elapsed_ns / (double) NSEC_IN_ONE_SEC;
+return num_secs * (double) insn_per_second;
+}
+
+static int64_t time_for_insn(uint64_t num_insn)
+{
+double num_secs = (double) num_insn / (double) insn_per_second;
+return num_secs * (double) NSEC_IN_ONE_SEC;
+}
+
+static int64_t uptime_ns(void)
+{
+int64_t now = now_ns();
+g_assert(now >= start_time_ns);
+return now - start_time_ns;
+}
+
+static void vcpu_set_state(vCPUTime *vcpu, vCPUState new_state)
+{
+vcpu->last_state_time = now_ns();
+vcpu->state = new_state;
+}
+
+static void update_system_time(vCPUTime *vcpu)
+{
+/* flush remaining instructions */
+vcpu->counter += vcpu->track_insn;
+vcpu->track_insn = 0;
+
+int64_t uptime = uptime_ns();
+uint64_t expected_insn = num_insn_during(uptime);
+
+if (vcpu->counter >= expected_insn) {
+/* this vcpu ran faster than expected, so it has to sleep */
+uint64_t insn_advance = vcpu->counter - expected_insn;
+uint64_t time_advance_ns = time_for_insn(insn_advance);
+int64_t sleep_us = time_advance_ns / 1000;
+g_usleep(sleep_us);
+}
+
+/* based on number of instructions, what should be the new time? */
+int64_t new_virtual_time = time_for_insn(vcpu->counter);
+
+g_mutex_lock(_state_lock);
+
+/* Time only moves forward. Another vcpu might have updated it already. */
+if (new_virtual_time > virtual_time_ns) {
+qemu_plugin_update_ns(time_handle, new_virtual_time);
+virtual_time_ns = new_virtual_time;
+}
+
+g_mutex_unlock(_state_lock);
+}
+
+static void set_start_time()
+{
+g_mutex_lock(_state_lock);
+if (!start_time_ns) {
+start_time_ns = now_ns();
+}
+g_mutex_unlock(_state_lock);
+}
+
+static void vcpu_init(qemu_plugin_id_t id, unsigned int cpu_index)
+{
+ 

Re: [PATCH 1/5] sysemu: add set_virtual_time to accel ops

2024-05-28 Thread Pierrick Bouvier

On 5/28/24 10:11, Alex Bennée wrote:

Pierrick Bouvier  writes:


From: Alex Bennée 

We are about to remove direct calls to individual accelerators for
this information and will need a central point for plugins to hook
into time changes.

From: Alex Bennée 
Signed-off-by: Alex Bennée 
Reviewed-by: Philippe Mathieu-Daudé 


Just a note, when patches written by other people come via your tree you
should add your s-o-b tag to indicate:

   "I'm legally okay to contribute this and happy for it to go into QEMU"



Thanks for clarifying, I didn't know it was needed as a committer (vs 
author), and checkpatch.pl does not seem to check for this.


I'll add this when reposting the series, if we have some comments.


Re: [PATCH 0/6] accel: Restrict TCG plugin (un)registration to TCG accel

2024-05-28 Thread Pierrick Bouvier
On 5/28/24 07:59, Philippe Mathieu-Daudé wrote: > Philippe Mathieu-Daudé 
(6):

   system/runstate: Remove unused 'qemu/plugin.h' header
   accel/tcg: Move common declarations to 'internal-common.h'
   accel: Clarify accel_cpu_common_[un]realize() use unassigned vCPU
   accel: Introduce accel_cpu_common_[un]realize_assigned() handlers
   accel: Restrict TCG plugin (un)registration to TCG accel
   accel/tcg: Move qemu_plugin_vcpu_init__async() to plugins/



Reviewed-by: Pierrick Bouvier 


Re: [PATCH] accel/tcg: Init tb size and icount before plugin_gen_tb_end

2024-05-22 Thread Pierrick Bouvier

On 5/21/24 14:06, Richard Henderson wrote:

When passing disassembly data to plugin callbacks,
translator_st_len relies on db->tb->size having been set.

Fixes: 4c833c60e047 ("disas: Use translator_st to get disassembly data")
Reported-by: Bernhard Beschow 
Signed-off-by: Richard Henderson 
---
  accel/tcg/translator.c | 8 
  1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/accel/tcg/translator.c b/accel/tcg/translator.c
index c56967eecd..113edcffe3 100644
--- a/accel/tcg/translator.c
+++ b/accel/tcg/translator.c
@@ -214,14 +214,14 @@ void translator_loop(CPUState *cpu, TranslationBlock *tb, 
int *max_insns,
  set_can_do_io(db, true);
  tcg_ctx->emit_before_op = NULL;
  
+/* May be used by disas_log or plugin callbacks. */

+tb->size = db->pc_next - db->pc_first;
+tb->icount = db->num_insns;
+
  if (plugin_enabled) {
  plugin_gen_tb_end(cpu, db->num_insns);
  }
  
-/* The disas_log hook may use these values rather than recompute.  */

-tb->size = db->pc_next - db->pc_first;
-tb->icount = db->num_insns;
-
  if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)
  && qemu_log_in_addr_range(db->pc_first)) {
      FILE *logfile = qemu_log_trylock();


Reviewed-by: Pierrick Bouvier 



[PATCH 5/5] contrib/plugins: add ips plugin example for cost modeling

2024-05-16 Thread Pierrick Bouvier
This plugin uses the new time control interface to make decisions
about the state of time during the emulation. The algorithm is
currently very simple. The user specifies an ips rate which applies
per core. If the core runs ahead of its allocated execution time the
plugin sleeps for a bit to let real time catch up. Either way time is
updated for the emulation as a function of total executed instructions
with some adjustments for cores that idle.

Examples


Slow down execution of /bin/true:
$ num_insn=$(./build/qemu-x86_64 -plugin ./build/tests/plugin/libinsn.so -d 
plugin /bin/true |& grep total | sed -e 's/.*: //')
$ time ./build/qemu-x86_64 -plugin 
./build/contrib/plugins/libips.so,ips=$(($num_insn/4)) /bin/true
real 4.000s

Boot a Linux kernel simulating a 250MHz cpu:
$ /build/qemu-system-x86_64 -kernel /boot/vmlinuz-6.1.0-21-amd64 -append 
"console=ttyS0" -plugin 
./build/contrib/plugins/libips.so,ips=$((250*1000*1000)) -smp 1 -m 512
check time until kernel panic on serial0

Signed-off-by: Pierrick Bouvier 
---
 contrib/plugins/ips.c| 239 +++
 contrib/plugins/Makefile |   1 +
 2 files changed, 240 insertions(+)
 create mode 100644 contrib/plugins/ips.c

diff --git a/contrib/plugins/ips.c b/contrib/plugins/ips.c
new file mode 100644
index 000..cf3159df391
--- /dev/null
+++ b/contrib/plugins/ips.c
@@ -0,0 +1,239 @@
+/*
+ * ips rate limiting plugin.
+ *
+ * This plugin can be used to restrict the execution of a system to a
+ * particular number of Instructions Per Second (ips). This controls
+ * time as seen by the guest so while wall-clock time may be longer
+ * from the guests point of view time will pass at the normal rate.
+ *
+ * This uses the new plugin API which allows the plugin to control
+ * system time.
+ *
+ * Copyright (c) 2023 Linaro Ltd
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include 
+#include 
+#include 
+
+QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION;
+
+/* how many times do we update time per sec */
+#define NUM_TIME_UPDATE_PER_SEC 10
+#define NSEC_IN_ONE_SEC (1000 * 1000 * 1000)
+
+static GMutex global_state_lock;
+
+static uint64_t insn_per_second = 1000 * 1000; /* ips per core, per second */
+static uint64_t insn_quantum; /* trap every N instructions */
+static bool precise_execution; /* count every instruction */
+static int64_t start_time_ns; /* time (ns since epoch) first vCPU started */
+static int64_t virtual_time_ns; /* last set virtual time */
+
+static const void *time_handle;
+
+typedef enum {
+UNKNOWN = 0,
+EXECUTING,
+IDLE,
+FINISHED
+} vCPUState;
+
+typedef struct {
+uint64_t counter;
+uint64_t track_insn;
+vCPUState state;
+/* timestamp when vCPU entered state */
+int64_t last_state_time;
+} vCPUTime;
+
+struct qemu_plugin_scoreboard *vcpus;
+
+/* return epoch time in ns */
+static int64_t now_ns(void)
+{
+return g_get_real_time() * 1000;
+}
+
+static uint64_t num_insn_during(int64_t elapsed_ns)
+{
+double num_secs = elapsed_ns / (double) NSEC_IN_ONE_SEC;
+return num_secs * (double) insn_per_second;
+}
+
+static int64_t time_for_insn(uint64_t num_insn)
+{
+double num_secs = (double) num_insn / (double) insn_per_second;
+return num_secs * (double) NSEC_IN_ONE_SEC;
+}
+
+static int64_t uptime_ns(void)
+{
+int64_t now = now_ns();
+g_assert(now >= start_time_ns);
+return now - start_time_ns;
+}
+
+static void vcpu_set_state(vCPUTime *vcpu, vCPUState new_state)
+{
+vcpu->last_state_time = now_ns();
+vcpu->state = new_state;
+}
+
+static void update_system_time(vCPUTime *vcpu)
+{
+/* flush remaining instructions */
+vcpu->counter += vcpu->track_insn;
+vcpu->track_insn = 0;
+
+int64_t uptime = uptime_ns();
+uint64_t expected_insn = num_insn_during(uptime);
+
+if (vcpu->counter >= expected_insn) {
+/* this vcpu ran faster than expected, so it has to sleep */
+uint64_t insn_advance = vcpu->counter - expected_insn;
+uint64_t time_advance_ns = time_for_insn(insn_advance);
+int64_t sleep_us = time_advance_ns / 1000;
+g_usleep(sleep_us);
+}
+
+/* based on number of instructions, what should be the new time? */
+int64_t new_virtual_time = time_for_insn(vcpu->counter);
+
+g_mutex_lock(_state_lock);
+
+/* Time only moves forward. Another vcpu might have updated it already. */
+if (new_virtual_time > virtual_time_ns) {
+qemu_plugin_update_ns(time_handle, new_virtual_time);
+virtual_time_ns = new_virtual_time;
+}
+
+g_mutex_unlock(_state_lock);
+}
+
+static void set_start_time()
+{
+g_mutex_lock(_state_lock);
+if (!start_time_ns) {
+start_time_ns = now_ns();
+}
+g_mutex_unlock(_state_lock);
+}
+
+static void vcpu_init(qemu_plugin_id_t id, unsigned int cpu_index)
+{
+vCPUTime *vcpu = qemu_plugin_scoreboard_find(vcpus, cpu

[PATCH 4/5] plugins: add time control API

2024-05-16 Thread Pierrick Bouvier
From: Alex Bennée 

Expose the ability to control time through the plugin API. Only one
plugin can control time so it has to request control when loaded.
There are probably more corner cases to catch here.

From: Alex Bennée 
Signed-off-by: Alex Bennée 
---
 include/qemu/qemu-plugin.h   | 23 +++
 plugins/api.c| 31 +++
 plugins/qemu-plugins.symbols |  2 ++
 3 files changed, 56 insertions(+)

diff --git a/include/qemu/qemu-plugin.h b/include/qemu/qemu-plugin.h
index 95703d8fec1..80b1637cede 100644
--- a/include/qemu/qemu-plugin.h
+++ b/include/qemu/qemu-plugin.h
@@ -661,6 +661,29 @@ void qemu_plugin_register_vcpu_mem_inline_per_vcpu(
 qemu_plugin_u64 entry,
 uint64_t imm);
 
+/**
+ * qemu_plugin_request_time_control() - request the ability to control time
+ *
+ * This grants the plugin the ability to control system time. Only one
+ * plugin can control time so if multiple plugins request the ability
+ * all but the first will fail.
+ *
+ * Returns an opaque handle or NULL if fails
+ */
+const void *qemu_plugin_request_time_control(void);
+
+/**
+ * qemu_plugin_update_ns() - update system emulation time
+ * @handle: opaque handle returned by qemu_plugin_request_time_control()
+ * @time: time in nanoseconds
+ *
+ * This allows an appropriately authorised plugin (i.e. holding the
+ * time control handle) to move system time forward to @time.
+ *
+ * Start time is 0.
+ */
+void qemu_plugin_update_ns(const void *handle, int64_t time);
+
 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,
diff --git a/plugins/api.c b/plugins/api.c
index 5a0a7f8c712..26822b69ea2 100644
--- a/plugins/api.c
+++ b/plugins/api.c
@@ -39,6 +39,7 @@
 #include "qemu/main-loop.h"
 #include "qemu/plugin.h"
 #include "qemu/log.h"
+#include "qemu/timer.h"
 #include "tcg/tcg.h"
 #include "exec/exec-all.h"
 #include "exec/gdbstub.h"
@@ -583,3 +584,33 @@ uint64_t qemu_plugin_u64_sum(qemu_plugin_u64 entry)
 }
 return total;
 }
+
+/*
+ * Time control
+ */
+static bool has_control;
+
+const void *qemu_plugin_request_time_control(void)
+{
+if (!has_control) {
+has_control = true;
+return _control;
+}
+return NULL;
+}
+
+static void advance_virtual_time__async(CPUState *cpu, run_on_cpu_data data)
+{
+int64_t new_time = data.host_ulong;
+qemu_clock_advance_virtual_time(new_time);
+}
+
+void qemu_plugin_update_ns(const void *handle, int64_t new_time)
+{
+if (handle == _control) {
+/* Need to execute out of cpu_exec, so bql can be locked. */
+async_run_on_cpu(current_cpu,
+ advance_virtual_time__async,
+ RUN_ON_CPU_HOST_ULONG(new_time));
+}
+}
diff --git a/plugins/qemu-plugins.symbols b/plugins/qemu-plugins.symbols
index aa0a77a319f..ca773d8d9fe 100644
--- a/plugins/qemu-plugins.symbols
+++ b/plugins/qemu-plugins.symbols
@@ -38,6 +38,7 @@
   qemu_plugin_register_vcpu_tb_exec_cond_cb;
   qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu;
   qemu_plugin_register_vcpu_tb_trans_cb;
+  qemu_plugin_request_time_control;
   qemu_plugin_reset;
   qemu_plugin_scoreboard_free;
   qemu_plugin_scoreboard_find;
@@ -51,5 +52,6 @@
   qemu_plugin_u64_set;
   qemu_plugin_u64_sum;
   qemu_plugin_uninstall;
+  qemu_plugin_update_ns;
   qemu_plugin_vcpu_for_each;
 };
-- 
2.39.2




[PATCH 1/5] sysemu: add set_virtual_time to accel ops

2024-05-16 Thread Pierrick Bouvier
From: Alex Bennée 

We are about to remove direct calls to individual accelerators for
this information and will need a central point for plugins to hook
into time changes.

From: Alex Bennée 
Signed-off-by: Alex Bennée 
Reviewed-by: Philippe Mathieu-Daudé 
---
 include/sysemu/accel-ops.h | 18 +-
 include/sysemu/cpu-timers.h|  3 ++-
 ...et-virtual-clock.c => cpus-virtual-clock.c} |  5 +
 system/cpus.c  | 11 +++
 stubs/meson.build  |  6 +-
 5 files changed, 40 insertions(+), 3 deletions(-)
 rename stubs/{cpus-get-virtual-clock.c => cpus-virtual-clock.c} (68%)

diff --git a/include/sysemu/accel-ops.h b/include/sysemu/accel-ops.h
index ef91fc28bbd..a0886722305 100644
--- a/include/sysemu/accel-ops.h
+++ b/include/sysemu/accel-ops.h
@@ -20,7 +20,12 @@
 typedef struct AccelOpsClass AccelOpsClass;
 DECLARE_CLASS_CHECKERS(AccelOpsClass, ACCEL_OPS, TYPE_ACCEL_OPS)
 
-/* cpus.c operations interface */
+/**
+ * struct AccelOpsClass - accelerator interfaces
+ *
+ * This structure is used to abstract accelerator differences from the
+ * core CPU code. Not all have to be implemented.
+ */
 struct AccelOpsClass {
 /*< private >*/
 ObjectClass parent_class;
@@ -44,7 +49,18 @@ struct AccelOpsClass {
 
 void (*handle_interrupt)(CPUState *cpu, int mask);
 
+/**
+ * @get_virtual_clock: fetch virtual clock
+ * @set_virtual_clock: set virtual clock
+ *
+ * These allow the timer subsystem to defer to the accelerator to
+ * fetch time. The set function is needed if the accelerator wants
+ * to track the changes to time as the timer is warped through
+ * various timer events.
+ */
 int64_t (*get_virtual_clock)(void);
+void (*set_virtual_clock)(int64_t time);
+
 int64_t (*get_elapsed_ticks)(void);
 
 /* gdbstub hooks */
diff --git a/include/sysemu/cpu-timers.h b/include/sysemu/cpu-timers.h
index d86738a378d..7bfa960fbd6 100644
--- a/include/sysemu/cpu-timers.h
+++ b/include/sysemu/cpu-timers.h
@@ -96,8 +96,9 @@ int64_t cpu_get_clock(void);
 
 void qemu_timer_notify_cb(void *opaque, QEMUClockType type);
 
-/* get the VIRTUAL clock and VM elapsed ticks via the cpus accel interface */
+/* get/set VIRTUAL clock and VM elapsed ticks via the cpus accel interface */
 int64_t cpus_get_virtual_clock(void);
+void cpus_set_virtual_clock(int64_t new_time);
 int64_t cpus_get_elapsed_ticks(void);
 
 #endif /* SYSEMU_CPU_TIMERS_H */
diff --git a/stubs/cpus-get-virtual-clock.c b/stubs/cpus-virtual-clock.c
similarity index 68%
rename from stubs/cpus-get-virtual-clock.c
rename to stubs/cpus-virtual-clock.c
index fd447d53f3c..af7c1a1d403 100644
--- a/stubs/cpus-get-virtual-clock.c
+++ b/stubs/cpus-virtual-clock.c
@@ -6,3 +6,8 @@ int64_t cpus_get_virtual_clock(void)
 {
 return cpu_get_clock();
 }
+
+void cpus_set_virtual_clock(int64_t new_time)
+{
+/* do nothing */
+}
diff --git a/system/cpus.c b/system/cpus.c
index 68d161d96b7..03ba026667c 100644
--- a/system/cpus.c
+++ b/system/cpus.c
@@ -229,6 +229,17 @@ int64_t cpus_get_virtual_clock(void)
 return cpu_get_clock();
 }
 
+/*
+ * Signal the new virtual time to the accelerator. This is only needed
+ * by accelerators that need to track the changes as we warp time.
+ */
+void cpus_set_virtual_clock(int64_t new_time)
+{
+if (cpus_accel && cpus_accel->set_virtual_clock) {
+cpus_accel->set_virtual_clock(new_time);
+}
+}
+
 /*
  * return the time elapsed in VM between vm_start and vm_stop.  Unless
  * icount is active, cpus_get_elapsed_ticks() uses units of the host CPU cycle
diff --git a/stubs/meson.build b/stubs/meson.build
index 3b9d42023cb..672213b7482 100644
--- a/stubs/meson.build
+++ b/stubs/meson.build
@@ -3,6 +3,11 @@
 # below, so that it is clear who needs the stubbed functionality.
 
 stub_ss.add(files('cpu-get-clock.c'))
+stub_ss.add(files('cpus-virtual-clock.c'))
+stub_ss.add(files('qemu-timer-notify-cb.c'))
+stub_ss.add(files('icount.c'))
+stub_ss.add(files('dump.c'))
+stub_ss.add(files('error-printf.c'))
 stub_ss.add(files('fdset.c'))
 stub_ss.add(files('iothread-lock.c'))
 stub_ss.add(files('is-daemonized.c'))
@@ -28,7 +33,6 @@ endif
 if have_block or have_ga
   stub_ss.add(files('replay-tools.c'))
   # stubs for hooks in util/main-loop.c, util/async.c etc.
-  stub_ss.add(files('cpus-get-virtual-clock.c'))
   stub_ss.add(files('icount.c'))
   stub_ss.add(files('graph-lock.c'))
   if linux_io_uring.found()
-- 
2.39.2




[PATCH 2/5] qtest: use cpu interface in qtest_clock_warp

2024-05-16 Thread Pierrick Bouvier
From: Alex Bennée 

This generalises the qtest_clock_warp code to use the AccelOps
handlers for updating its own sense of time. This will make the next
patch which moves the warp code closer to pure code motion.

From: Alex Bennée 
Signed-off-by: Alex Bennée 
Acked-by: Thomas Huth 
---
 include/sysemu/qtest.h | 1 +
 accel/qtest/qtest.c| 1 +
 system/qtest.c | 6 +++---
 3 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/include/sysemu/qtest.h b/include/sysemu/qtest.h
index b5d5fd34637..45f3b7e1df5 100644
--- a/include/sysemu/qtest.h
+++ b/include/sysemu/qtest.h
@@ -36,6 +36,7 @@ void qtest_server_set_send_handler(void (*send)(void *, const 
char *),
 void qtest_server_inproc_recv(void *opaque, const char *buf);
 
 int64_t qtest_get_virtual_clock(void);
+void qtest_set_virtual_clock(int64_t count);
 #endif
 
 #endif
diff --git a/accel/qtest/qtest.c b/accel/qtest/qtest.c
index f6056ac8361..53182e6c2ae 100644
--- a/accel/qtest/qtest.c
+++ b/accel/qtest/qtest.c
@@ -52,6 +52,7 @@ static void qtest_accel_ops_class_init(ObjectClass *oc, void 
*data)
 
 ops->create_vcpu_thread = dummy_start_vcpu_thread;
 ops->get_virtual_clock = qtest_get_virtual_clock;
+ops->set_virtual_clock = qtest_set_virtual_clock;
 };
 
 static const TypeInfo qtest_accel_ops_type = {
diff --git a/system/qtest.c b/system/qtest.c
index 6da58b3874e..ee8b139e982 100644
--- a/system/qtest.c
+++ b/system/qtest.c
@@ -332,14 +332,14 @@ int64_t qtest_get_virtual_clock(void)
 return qatomic_read_i64(_clock_counter);
 }
 
-static void qtest_set_virtual_clock(int64_t count)
+void qtest_set_virtual_clock(int64_t count)
 {
 qatomic_set_i64(_clock_counter, count);
 }
 
 static void qtest_clock_warp(int64_t dest)
 {
-int64_t clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+int64_t clock = cpus_get_virtual_clock();
 AioContext *aio_context;
 assert(qtest_enabled());
 aio_context = qemu_get_aio_context();
@@ -348,7 +348,7 @@ static void qtest_clock_warp(int64_t dest)
   QEMU_TIMER_ATTR_ALL);
 int64_t warp = qemu_soonest_timeout(dest - clock, deadline);
 
-qtest_set_virtual_clock(qtest_get_virtual_clock() + warp);
+cpus_set_virtual_clock(cpus_get_virtual_clock() + warp);
 
 qemu_clock_run_timers(QEMU_CLOCK_VIRTUAL);
 timerlist_run_timers(aio_context->tlg.tl[QEMU_CLOCK_VIRTUAL]);
-- 
2.39.2




[PATCH 0/5] Implement icount=auto using TCG Plugins

2024-05-16 Thread Pierrick Bouvier
The goal here is to be able to scale temporally execution of qemu-user/system,
using a given number of instructions per second.

We define a virtual clock, that can be late or in advance compared to real time.
When we are in advance, we slow execution (by sleeping) until catching real
time.

Finally, we should be able to cleanup icount=auto mode completely, and keep
icount usage for determistic purposes only.

It is built upon new TCG Plugins inline ops (store + conditional callbacks), now
merged on master.

Example in user-mode:

- Retrieve number of instructions to execute /bin/true
$ ./build/qemu-x86_64 -plugin ./build/tests/plugin/libinsn.so -d plugin 
/bin/true
cpu 0 insns: 120546
total insns: 120546
- Slow execution to match 5 seconds
$ time ./build/qemu-x86_64 -plugin 
./build/contrib/plugins/libips,ips=$((120546/5)) /bin/true
real0m4.985s

Alex Bennée (4):
  sysemu: add set_virtual_time to accel ops
  qtest: use cpu interface in qtest_clock_warp
  sysemu: generalise qtest_warp_clock as qemu_clock_advance_virtual_time
  plugins: add time control API

Pierrick Bouvier (1):
  contrib/plugins: add ips plugin example for cost modeling

 include/qemu/qemu-plugin.h|  23 ++
 include/qemu/timer.h  |  15 ++
 include/sysemu/accel-ops.h|  18 +-
 include/sysemu/cpu-timers.h   |   3 +-
 include/sysemu/qtest.h|   1 +
 accel/qtest/qtest.c   |   1 +
 contrib/plugins/ips.c | 239 ++
 plugins/api.c |  31 +++
 ...t-virtual-clock.c => cpus-virtual-clock.c} |   5 +
 system/cpus.c |  11 +
 system/qtest.c|  27 +-
 util/qemu-timer.c |  26 ++
 contrib/plugins/Makefile  |   1 +
 plugins/qemu-plugins.symbols  |   2 +
 stubs/meson.build |   6 +-
 15 files changed, 383 insertions(+), 26 deletions(-)
 create mode 100644 contrib/plugins/ips.c
 rename stubs/{cpus-get-virtual-clock.c => cpus-virtual-clock.c} (68%)

-- 
2.39.2




[PATCH 3/5] sysemu: generalise qtest_warp_clock as qemu_clock_advance_virtual_time

2024-05-16 Thread Pierrick Bouvier
From: Alex Bennée 

Move the key functionality of moving time forward into the clock
sub-system itself. This will allow us to plumb in time control into
plugins.

From: Alex Bennée 
Signed-off-by: Alex Bennée 
---
 include/qemu/timer.h | 15 +++
 system/qtest.c   | 25 +++--
 util/qemu-timer.c| 26 ++
 3 files changed, 44 insertions(+), 22 deletions(-)

diff --git a/include/qemu/timer.h b/include/qemu/timer.h
index 9a366e551fb..910587d8293 100644
--- a/include/qemu/timer.h
+++ b/include/qemu/timer.h
@@ -245,6 +245,21 @@ bool qemu_clock_run_timers(QEMUClockType type);
  */
 bool qemu_clock_run_all_timers(void);
 
+/**
+ * qemu_clock_advance_virtual_time(): advance the virtual time tick
+ * @target: target time in nanoseconds
+ *
+ * This function is used where the control of the flow of time has
+ * been delegated to outside the clock subsystem (be it qtest, icount
+ * or some other external source). You can ask the clock system to
+ * return @early at the first expired timer.
+ *
+ * Time can only move forward, attempts to reverse time would lead to
+ * an error.
+ *
+ * Returns: new virtual time.
+ */
+int64_t qemu_clock_advance_virtual_time(int64_t dest);
 
 /*
  * QEMUTimerList
diff --git a/system/qtest.c b/system/qtest.c
index ee8b139e982..e6f6b4e62d5 100644
--- a/system/qtest.c
+++ b/system/qtest.c
@@ -337,26 +337,6 @@ void qtest_set_virtual_clock(int64_t count)
 qatomic_set_i64(_clock_counter, count);
 }
 
-static void qtest_clock_warp(int64_t dest)
-{
-int64_t clock = cpus_get_virtual_clock();
-AioContext *aio_context;
-assert(qtest_enabled());
-aio_context = qemu_get_aio_context();
-while (clock < dest) {
-int64_t deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL,
-  QEMU_TIMER_ATTR_ALL);
-int64_t warp = qemu_soonest_timeout(dest - clock, deadline);
-
-cpus_set_virtual_clock(cpus_get_virtual_clock() + warp);
-
-qemu_clock_run_timers(QEMU_CLOCK_VIRTUAL);
-timerlist_run_timers(aio_context->tlg.tl[QEMU_CLOCK_VIRTUAL]);
-clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
-}
-qemu_clock_notify(QEMU_CLOCK_VIRTUAL);
-}
-
 static bool (*process_command_cb)(CharBackend *chr, gchar **words);
 
 void qtest_set_command_cb(bool (*pc_cb)(CharBackend *chr, gchar **words))
@@ -755,7 +735,8 @@ static void qtest_process_command(CharBackend *chr, gchar 
**words)
 ns = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL,
 QEMU_TIMER_ATTR_ALL);
 }
-qtest_clock_warp(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + ns);
+qemu_clock_advance_virtual_time(
+qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + ns);
 qtest_send_prefix(chr);
 qtest_sendf(chr, "OK %"PRIi64"\n",
 (int64_t)qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL));
@@ -781,7 +762,7 @@ static void qtest_process_command(CharBackend *chr, gchar 
**words)
 g_assert(words[1]);
 ret = qemu_strtoi64(words[1], NULL, 0, );
 g_assert(ret == 0);
-qtest_clock_warp(ns);
+qemu_clock_advance_virtual_time(ns);
 qtest_send_prefix(chr);
 qtest_sendf(chr, "OK %"PRIi64"\n",
 (int64_t)qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL));
diff --git a/util/qemu-timer.c b/util/qemu-timer.c
index 6a0de33dd2b..213114be68c 100644
--- a/util/qemu-timer.c
+++ b/util/qemu-timer.c
@@ -645,6 +645,11 @@ int64_t qemu_clock_get_ns(QEMUClockType type)
 }
 }
 
+static void qemu_virtual_clock_set_ns(int64_t time)
+{
+return cpus_set_virtual_clock(time);
+}
+
 void init_clocks(QEMUTimerListNotifyCB *notify_cb)
 {
 QEMUClockType type;
@@ -675,3 +680,24 @@ bool qemu_clock_run_all_timers(void)
 
 return progress;
 }
+
+int64_t qemu_clock_advance_virtual_time(int64_t dest)
+{
+int64_t clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+AioContext *aio_context;
+aio_context = qemu_get_aio_context();
+while (clock < dest) {
+int64_t deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL,
+  QEMU_TIMER_ATTR_ALL);
+int64_t warp = qemu_soonest_timeout(dest - clock, deadline);
+
+qemu_virtual_clock_set_ns(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 
warp);
+
+qemu_clock_run_timers(QEMU_CLOCK_VIRTUAL);
+timerlist_run_timers(aio_context->tlg.tl[QEMU_CLOCK_VIRTUAL]);
+clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+}
+qemu_clock_notify(QEMU_CLOCK_VIRTUAL);
+
+return clock;
+}
-- 
2.39.2




Re: [PATCH 02/11] scripts/update-linux-header.sh: be more src tree friendly

2024-05-14 Thread Pierrick Bouvier
ot;$tmpdir/include/asm/unistd_64.h" "$output/linux-headers/asm-x86/"
-cp_portable "$tmpdir/include/asm/kvm_para.h" 
"$output/include/standard-headers/asm-$arch"
+cp "$hdrdir/include/asm/unistd_32.h" "$output/linux-headers/asm-x86/"
+cp "$hdrdir/include/asm/unistd_x32.h" "$output/linux-headers/asm-x86/"
+cp "$hdrdir/include/asm/unistd_64.h" "$output/linux-headers/asm-x86/"
+cp_portable "$hdrdir/include/asm/kvm_para.h" 
"$output/include/standard-headers/asm-$arch"
  # Remove everything except the macros from bootparam.h avoiding the
  # unnecessary import of several video/ist/etc headers
  sed -e '/__ASSEMBLY__/,/__ASSEMBLY__/d' \
-   "$tmpdir/include/asm/bootparam.h" > "$tmpdir/bootparam.h"
-cp_portable "$tmpdir/bootparam.h" \
+   "$hdrdir/include/asm/bootparam.h" > "$hdrdir/bootparam.h"
+cp_portable "$hdrdir/bootparam.h" \
  "$output/include/standard-headers/asm-$arch"
-cp_portable "$tmpdir/include/asm/setup_data.h" \
+cp_portable "$hdrdir/include/asm/setup_data.h" \
  "$output/standard-headers/asm-x86"
  fi
  if [ $arch = riscv ]; then
-cp "$tmpdir/include/asm/ptrace.h" "$output/linux-headers/asm-riscv/"
+cp "$hdrdir/include/asm/ptrace.h" "$output/linux-headers/asm-riscv/"
  fi
  done
  arch=
@@ -169,13 +171,13 @@ mkdir -p "$output/linux-headers/linux"
  for header in const.h stddef.h kvm.h vfio.h vfio_ccw.h vfio_zdev.h vhost.h \
psci.h psp-sev.h userfaultfd.h memfd.h mman.h nvme_ioctl.h \
vduse.h iommufd.h bits.h; do
-cp "$tmpdir/include/linux/$header" "$output/linux-headers/linux"
+cp "$hdrdir/include/linux/$header" "$output/linux-headers/linux"
  done
  
  rm -rf "$output/linux-headers/asm-generic"

  mkdir -p "$output/linux-headers/asm-generic"
  for header in unistd.h bitsperlong.h mman-common.h mman.h hugetlb_encode.h; do
-cp "$tmpdir/include/asm-generic/$header" 
"$output/linux-headers/asm-generic"
+cp "$hdrdir/include/asm-generic/$header" 
"$output/linux-headers/asm-generic"
  done
  
  if [ -L "$linux/source" ]; then

@@ -210,23 +212,23 @@ EOF
  
  rm -rf "$output/include/standard-headers/linux"

  mkdir -p "$output/include/standard-headers/linux"
-for i in "$tmpdir"/include/linux/*virtio*.h \
- "$tmpdir/include/linux/qemu_fw_cfg.h" \
- "$tmpdir/include/linux/fuse.h" \
- "$tmpdir/include/linux/input.h" \
- "$tmpdir/include/linux/input-event-codes.h" \
- "$tmpdir/include/linux/udmabuf.h" \
- "$tmpdir/include/linux/pci_regs.h" \
-     "$tmpdir/include/linux/ethtool.h" \
- "$tmpdir/include/linux/const.h" \
- "$tmpdir/include/linux/kernel.h" \
- "$tmpdir/include/linux/vhost_types.h" \
- "$tmpdir/include/linux/sysinfo.h" \
- "$tmpdir/include/misc/pvpanic.h"; do
+for i in "$hdrdir"/include/linux/*virtio*.h \
+ "$hdrdir/include/linux/qemu_fw_cfg.h" \
+ "$hdrdir/include/linux/fuse.h" \
+ "$hdrdir/include/linux/input.h" \
+ "$hdrdir/include/linux/input-event-codes.h" \
+ "$hdrdir/include/linux/udmabuf.h" \
+ "$hdrdir/include/linux/pci_regs.h" \
+ "$hdrdir/include/linux/ethtool.h" \
+ "$hdrdir/include/linux/const.h" \
+ "$hdrdir/include/linux/kernel.h" \
+ "$hdrdir/include/linux/vhost_types.h" \
+ "$hdrdir/include/linux/sysinfo.h" \
+ "$hdrdir/include/misc/pvpanic.h"; do
  cp_portable "$i" "$output/include/standard-headers/linux"
  done
  mkdir -p "$output/include/standard-headers/drm"
-cp_portable "$tmpdir/include/drm/drm_fourcc.h" \
+cp_portable "$hdrdir/include/drm/drm_fourcc.h" \
  "$output/include/standard-headers/drm"
  
  cat <$output/include/standard-headers/linux/types.h


Reviewed-by: Pierrick Bouvier 


Re: [PATCH 01/11] tests/tcg: don't append QEMU_OPTS for armv6m-undef test

2024-05-14 Thread Pierrick Bouvier

On 5/14/24 10:42, Alex Bennée wrote:

We don't want to build on the default machine setup here but define a
custom one for the microbit.

Signed-off-by: Alex Bennée 
---
  tests/tcg/arm/Makefile.softmmu-target | 2 +-
  1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tests/tcg/arm/Makefile.softmmu-target 
b/tests/tcg/arm/Makefile.softmmu-target
index 4c9264057f..39e01ce49d 100644
--- a/tests/tcg/arm/Makefile.softmmu-target
+++ b/tests/tcg/arm/Makefile.softmmu-target
@@ -16,7 +16,7 @@ test-armv6m-undef: test-armv6m-undef.S
$< -o $@ -nostdlib -N -static \
-T $(ARM_SRC)/$@.ld
  
-run-test-armv6m-undef: QEMU_OPTS+=-semihosting -M microbit -kernel

+run-test-armv6m-undef: QEMU_OPTS=-semihosting-config 
enable=on,target=native,chardev=output -M microbit -kernel
  
  ARM_TESTS+=test-armv6m-undef
  


Reviewed-by: Pierrick Bouvier 


Re: [PATCH v2 13/33] plugins: Use DisasContextBase for qemu_plugin_insn_haddr

2024-05-13 Thread Pierrick Bouvier

On 4/24/24 16:31, Richard Henderson wrote:

We can delay the computation of haddr until the plugin
actually requests it.

Signed-off-by: Richard Henderson 
---
  include/qemu/plugin.h  |  4 
  accel/tcg/plugin-gen.c | 20 
  plugins/api.c  | 25 -
  3 files changed, 24 insertions(+), 25 deletions(-)

diff --git a/include/qemu/plugin.h b/include/qemu/plugin.h
index 03081be543..3db0e75d16 100644
--- a/include/qemu/plugin.h
+++ b/include/qemu/plugin.h
@@ -98,7 +98,6 @@ struct qemu_plugin_dyn_cb {
  /* Internal context for instrumenting an instruction */
  struct qemu_plugin_insn {
  uint64_t vaddr;
-void *haddr;
  GArray *insn_cbs;
  GArray *mem_cbs;
  uint8_t len;
@@ -119,9 +118,6 @@ struct qemu_plugin_tb {
  GPtrArray *insns;
  size_t n;
  uint64_t vaddr;
-uint64_t vaddr2;
-void *haddr1;
-void *haddr2;
  
  /* if set, the TB calls helpers that might access guest memory */

  bool mem_helper;
diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c
index a4656859c6..b036773d3c 100644
--- a/accel/tcg/plugin-gen.c
+++ b/accel/tcg/plugin-gen.c
@@ -319,9 +319,6 @@ bool plugin_gen_tb_start(CPUState *cpu, const 
DisasContextBase *db)
  ret = true;
  
  ptb->vaddr = db->pc_first;

-ptb->vaddr2 = -1;
-ptb->haddr1 = db->host_addr[0];
-ptb->haddr2 = NULL;
  ptb->mem_helper = false;
  
  tcg_gen_plugin_cb(PLUGIN_GEN_FROM_TB);

@@ -363,23 +360,6 @@ void plugin_gen_insn_start(CPUState *cpu, const 
DisasContextBase *db)
  pc = db->pc_next;
  insn->vaddr = pc;
  
-/*

- * Detect page crossing to get the new host address.
- * Note that we skip this when haddr1 == NULL, e.g. when we're
- * fetching instructions from a region not backed by RAM.
- */
-if (ptb->haddr1 == NULL) {
-insn->haddr = NULL;
-} else if (is_same_page(db, db->pc_next)) {
-insn->haddr = ptb->haddr1 + pc - ptb->vaddr;
-} else {
-if (ptb->vaddr2 == -1) {
-ptb->vaddr2 = TARGET_PAGE_ALIGN(db->pc_first);
-get_page_addr_code_hostp(cpu_env(cpu), ptb->vaddr2, >haddr2);
-}
-insn->haddr = ptb->haddr2 + pc - ptb->vaddr2;
-}
-
  tcg_gen_plugin_cb(PLUGIN_GEN_FROM_INSN);
  }
  
diff --git a/plugins/api.c b/plugins/api.c

index 39895a1cb1..4b6690c7d6 100644
--- a/plugins/api.c
+++ b/plugins/api.c
@@ -242,7 +242,30 @@ uint64_t qemu_plugin_insn_vaddr(const struct 
qemu_plugin_insn *insn)
  
  void *qemu_plugin_insn_haddr(const struct qemu_plugin_insn *insn)

  {
-return insn->haddr;
+const DisasContextBase *db = tcg_ctx->plugin_db;
+vaddr page0_last = db->pc_first | ~TARGET_PAGE_MASK;
+
+if (db->fake_insn) {
+return NULL;
+}
+
+/*
+ * ??? The return value is not intended for use of host memory,
+ * but as a proxy for address space and physical address.
+ * Thus we are only interested in the first byte and do not
+ * care about spanning pages.
+ */
+if (insn->vaddr <= page0_last) {
+if (db->host_addr[0] == NULL) {
+return NULL;
+}
+return db->host_addr[0] + insn->vaddr - db->pc_first;
+} else {
+if (db->host_addr[1] == NULL) {
+return NULL;
+}
+return db->host_addr[1] + insn->vaddr - (page0_last + 1);
+}
  }
  
  char *qemu_plugin_insn_disas(const struct qemu_plugin_insn *insn)


Reviewed-by: Pierrick Bouvier 



Re: [PATCH v6 0/9] TCG plugins new inline operations

2024-05-07 Thread Pierrick Bouvier

On 5/7/24 09:07, Alex Bennée wrote:

Pierrick Bouvier  writes:


This series implement two new operations for plugins:
- Store inline allows to write a specific value to a scoreboard.
- Conditional callback executes a callback only when a given condition is true.
   The condition is evaluated inline.

It's possible to mix various inline operations (add, store) with conditional
callbacks, allowing efficient "trap" based counters.

It builds on top of new scoreboard API, introduced in the previous
series.



Queued to plugins/next, thanks.



Thanks Alex.


[PATCH v6 3/9] plugins: add new inline op STORE_U64

2024-05-02 Thread Pierrick Bouvier
This new operation can store an immediate u64 value to a given
scoreboard.

Reviewed-by: Richard Henderson 
Signed-off-by: Pierrick Bouvier 
---
 include/qemu/plugin.h  |  1 +
 include/qemu/qemu-plugin.h |  4 ++--
 accel/tcg/plugin-gen.c | 13 +
 plugins/core.c |  6 ++
 4 files changed, 22 insertions(+), 2 deletions(-)

diff --git a/include/qemu/plugin.h b/include/qemu/plugin.h
index bee1647cfc4..0c5df7fa90a 100644
--- a/include/qemu/plugin.h
+++ b/include/qemu/plugin.h
@@ -70,6 +70,7 @@ enum plugin_dyn_cb_type {
 PLUGIN_CB_REGULAR,
 PLUGIN_CB_MEM_REGULAR,
 PLUGIN_CB_INLINE_ADD_U64,
+PLUGIN_CB_INLINE_STORE_U64,
 };
 
 /*
diff --git a/include/qemu/qemu-plugin.h b/include/qemu/qemu-plugin.h
index 4fc6c3739b2..c5cac897a0b 100644
--- a/include/qemu/qemu-plugin.h
+++ b/include/qemu/qemu-plugin.h
@@ -305,12 +305,12 @@ void qemu_plugin_register_vcpu_tb_exec_cb(struct 
qemu_plugin_tb *tb,
  * enum qemu_plugin_op - describes an inline op
  *
  * @QEMU_PLUGIN_INLINE_ADD_U64: add an immediate value uint64_t
- *
- * Note: currently only a single inline op is supported.
+ * @QEMU_PLUGIN_INLINE_STORE_U64: store an immediate value uint64_t
  */
 
 enum qemu_plugin_op {
 QEMU_PLUGIN_INLINE_ADD_U64,
+QEMU_PLUGIN_INLINE_STORE_U64,
 };
 
 /**
diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c
index 8e2c3ef94f6..a5313cbbb2f 100644
--- a/accel/tcg/plugin-gen.c
+++ b/accel/tcg/plugin-gen.c
@@ -145,6 +145,16 @@ static void gen_inline_add_u64_cb(struct 
qemu_plugin_dyn_cb *cb)
 tcg_temp_free_ptr(ptr);
 }
 
+static void gen_inline_store_u64_cb(struct qemu_plugin_dyn_cb *cb)
+{
+TCGv_ptr ptr = gen_plugin_u64_ptr(cb->inline_insn.entry);
+TCGv_i64 val = tcg_constant_i64(cb->inline_insn.imm);
+
+tcg_gen_st_i64(val, ptr, 0);
+
+tcg_temp_free_ptr(ptr);
+}
+
 static void gen_mem_cb(struct qemu_plugin_dyn_cb *cb,
qemu_plugin_meminfo_t meminfo, TCGv_i64 addr)
 {
@@ -170,6 +180,9 @@ static void inject_cb(struct qemu_plugin_dyn_cb *cb)
 case PLUGIN_CB_INLINE_ADD_U64:
 gen_inline_add_u64_cb(cb);
 break;
+case PLUGIN_CB_INLINE_STORE_U64:
+gen_inline_store_u64_cb(cb);
+break;
 default:
 g_assert_not_reached();
 }
diff --git a/plugins/core.c b/plugins/core.c
index a8557b54ff7..e1bf0dc3717 100644
--- a/plugins/core.c
+++ b/plugins/core.c
@@ -321,6 +321,8 @@ static enum plugin_dyn_cb_type op_to_cb_type(enum 
qemu_plugin_op op)
 switch (op) {
 case QEMU_PLUGIN_INLINE_ADD_U64:
 return PLUGIN_CB_INLINE_ADD_U64;
+case QEMU_PLUGIN_INLINE_STORE_U64:
+return PLUGIN_CB_INLINE_STORE_U64;
 default:
 g_assert_not_reached();
 }
@@ -535,6 +537,9 @@ void exec_inline_op(struct qemu_plugin_dyn_cb *cb, int 
cpu_index)
 case QEMU_PLUGIN_INLINE_ADD_U64:
 *val += cb->inline_insn.imm;
 break;
+case QEMU_PLUGIN_INLINE_STORE_U64:
+*val = cb->inline_insn.imm;
+break;
 default:
 g_assert_not_reached();
 }
@@ -562,6 +567,7 @@ void qemu_plugin_vcpu_mem_cb(CPUState *cpu, uint64_t vaddr,
vaddr, cb->userp);
 break;
 case PLUGIN_CB_INLINE_ADD_U64:
+case PLUGIN_CB_INLINE_STORE_U64:
 exec_inline_op(cb, cpu->cpu_index);
 break;
 default:
-- 
2.39.2




[PATCH v6 6/9] tests/plugin/inline: add test for conditional callback

2024-05-02 Thread Pierrick Bouvier
Count number of tb and insn executed using a conditional callback. We
ensure the callback has been called expected number of time (per vcpu).

Reviewed-by: Richard Henderson 
Signed-off-by: Pierrick Bouvier 
---
 tests/plugin/inline.c | 89 +--
 1 file changed, 86 insertions(+), 3 deletions(-)

diff --git a/tests/plugin/inline.c b/tests/plugin/inline.c
index 103c3a22f6e..cd63827b7d8 100644
--- a/tests/plugin/inline.c
+++ b/tests/plugin/inline.c
@@ -20,8 +20,14 @@ typedef struct {
 uint64_t count_insn_inline;
 uint64_t count_mem;
 uint64_t count_mem_inline;
+uint64_t tb_cond_num_trigger;
+uint64_t tb_cond_track_count;
+uint64_t insn_cond_num_trigger;
+uint64_t insn_cond_track_count;
 } CPUCount;
 
+static const uint64_t cond_trigger_limit = 100;
+
 typedef struct {
 uint64_t data_insn;
 uint64_t data_tb;
@@ -35,6 +41,10 @@ static qemu_plugin_u64 count_insn;
 static qemu_plugin_u64 count_insn_inline;
 static qemu_plugin_u64 count_mem;
 static qemu_plugin_u64 count_mem_inline;
+static qemu_plugin_u64 tb_cond_num_trigger;
+static qemu_plugin_u64 tb_cond_track_count;
+static qemu_plugin_u64 insn_cond_num_trigger;
+static qemu_plugin_u64 insn_cond_track_count;
 static struct qemu_plugin_scoreboard *data;
 static qemu_plugin_u64 data_insn;
 static qemu_plugin_u64 data_tb;
@@ -56,12 +66,19 @@ static void stats_insn(void)
 const uint64_t per_vcpu = qemu_plugin_u64_sum(count_insn);
 const uint64_t inl_per_vcpu =
 qemu_plugin_u64_sum(count_insn_inline);
+const uint64_t cond_num_trigger =
+qemu_plugin_u64_sum(insn_cond_num_trigger);
+const uint64_t cond_track_left = 
qemu_plugin_u64_sum(insn_cond_track_count);
+const uint64_t conditional =
+cond_num_trigger * cond_trigger_limit + cond_track_left;
 printf("insn: %" PRIu64 "\n", expected);
 printf("insn: %" PRIu64 " (per vcpu)\n", per_vcpu);
 printf("insn: %" PRIu64 " (per vcpu inline)\n", inl_per_vcpu);
+printf("insn: %" PRIu64 " (cond cb)\n", conditional);
 g_assert(expected > 0);
 g_assert(per_vcpu == expected);
 g_assert(inl_per_vcpu == expected);
+g_assert(conditional == expected);
 }
 
 static void stats_tb(void)
@@ -70,12 +87,18 @@ static void stats_tb(void)
 const uint64_t per_vcpu = qemu_plugin_u64_sum(count_tb);
 const uint64_t inl_per_vcpu =
 qemu_plugin_u64_sum(count_tb_inline);
+const uint64_t cond_num_trigger = qemu_plugin_u64_sum(tb_cond_num_trigger);
+const uint64_t cond_track_left = qemu_plugin_u64_sum(tb_cond_track_count);
+const uint64_t conditional =
+cond_num_trigger * cond_trigger_limit + cond_track_left;
 printf("tb: %" PRIu64 "\n", expected);
 printf("tb: %" PRIu64 " (per vcpu)\n", per_vcpu);
 printf("tb: %" PRIu64 " (per vcpu inline)\n", inl_per_vcpu);
+printf("tb: %" PRIu64 " (conditional cb)\n", conditional);
 g_assert(expected > 0);
 g_assert(per_vcpu == expected);
 g_assert(inl_per_vcpu == expected);
+g_assert(conditional == expected);
 }
 
 static void stats_mem(void)
@@ -104,14 +127,35 @@ static void plugin_exit(qemu_plugin_id_t id, void *udata)
 const uint64_t insn_inline = qemu_plugin_u64_get(count_insn_inline, i);
 const uint64_t mem = qemu_plugin_u64_get(count_mem, i);
 const uint64_t mem_inline = qemu_plugin_u64_get(count_mem_inline, i);
-printf("cpu %d: tb (%" PRIu64 ", %" PRIu64 ") | "
-   "insn (%" PRIu64 ", %" PRIu64 ") | "
+const uint64_t tb_cond_trigger =
+qemu_plugin_u64_get(tb_cond_num_trigger, i);
+const uint64_t tb_cond_left =
+qemu_plugin_u64_get(tb_cond_track_count, i);
+const uint64_t insn_cond_trigger =
+qemu_plugin_u64_get(insn_cond_num_trigger, i);
+const uint64_t insn_cond_left =
+qemu_plugin_u64_get(insn_cond_track_count, i);
+printf("cpu %d: tb (%" PRIu64 ", %" PRIu64
+   ", %" PRIu64 " * %" PRIu64 " + %" PRIu64
+   ") | "
+   "insn (%" PRIu64 ", %" PRIu64
+   ", %" PRIu64 " * %" PRIu64 " + %" PRIu64
+   ") | "
"mem (%" PRIu64 ", %" PRIu64 ")"
"\n",
-   i, tb, tb_inline, insn, insn_inline, mem, mem_inline);
+   i,
+   tb, tb_inline,
+   tb_cond_trigger, cond_trigger_limit, tb_cond_left,
+   insn, insn_inline,
+   insn_cond_trigger, cond_trigger_limit, insn_cond_left,
+   mem, mem_inline);
 g_assert(tb == t

[PATCH v6 2/9] plugins: extract generate ptr for qemu_plugin_u64

2024-05-02 Thread Pierrick Bouvier
Plugin operations can access a scoreboard. This function factorizes code
generation for accessing entry associated to a given vcpu.

Reviewed-by: Richard Henderson 
Signed-off-by: Pierrick Bouvier 
---
 accel/tcg/plugin-gen.c | 27 ++-
 1 file changed, 18 insertions(+), 9 deletions(-)

diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c
index 8028ae76c3a..8e2c3ef94f6 100644
--- a/accel/tcg/plugin-gen.c
+++ b/accel/tcg/plugin-gen.c
@@ -113,24 +113,33 @@ static void gen_udata_cb(struct qemu_plugin_dyn_cb *cb)
 tcg_temp_free_i32(cpu_index);
 }
 
-static void gen_inline_add_u64_cb(struct qemu_plugin_dyn_cb *cb)
+static TCGv_ptr gen_plugin_u64_ptr(qemu_plugin_u64 entry)
 {
-GArray *arr = cb->inline_insn.entry.score->data;
-size_t offset = cb->inline_insn.entry.offset;
-TCGv_i32 cpu_index = tcg_temp_ebb_new_i32();
-TCGv_i64 val = tcg_temp_ebb_new_i64();
 TCGv_ptr ptr = tcg_temp_ebb_new_ptr();
 
+GArray *arr = entry.score->data;
+char *base_ptr = arr->data + entry.offset;
+size_t entry_size = g_array_get_element_size(arr);
+
+TCGv_i32 cpu_index = tcg_temp_ebb_new_i32();
 tcg_gen_ld_i32(cpu_index, tcg_env,
-offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index));
-tcg_gen_muli_i32(cpu_index, cpu_index, g_array_get_element_size(arr));
+tcg_gen_muli_i32(cpu_index, cpu_index, entry_size);
 tcg_gen_ext_i32_ptr(ptr, cpu_index);
 tcg_temp_free_i32(cpu_index);
+tcg_gen_addi_ptr(ptr, ptr, (intptr_t) base_ptr);
 
-tcg_gen_addi_ptr(ptr, ptr, (intptr_t)arr->data);
-tcg_gen_ld_i64(val, ptr, offset);
+return ptr;
+}
+
+static void gen_inline_add_u64_cb(struct qemu_plugin_dyn_cb *cb)
+{
+TCGv_ptr ptr = gen_plugin_u64_ptr(cb->inline_insn.entry);
+TCGv_i64 val = tcg_temp_ebb_new_i64();
+
+tcg_gen_ld_i64(val, ptr, 0);
 tcg_gen_addi_i64(val, val, cb->inline_insn.imm);
-tcg_gen_st_i64(val, ptr, offset);
+tcg_gen_st_i64(val, ptr, 0);
 
 tcg_temp_free_i64(val);
 tcg_temp_free_ptr(ptr);
-- 
2.39.2




[PATCH v6 4/9] tests/plugin/inline: add test for STORE_U64 inline op

2024-05-02 Thread Pierrick Bouvier
Reviewed-by: Richard Henderson 
Signed-off-by: Pierrick Bouvier 
---
 tests/plugin/inline.c | 41 +
 1 file changed, 37 insertions(+), 4 deletions(-)

diff --git a/tests/plugin/inline.c b/tests/plugin/inline.c
index 0163e9b51c5..103c3a22f6e 100644
--- a/tests/plugin/inline.c
+++ b/tests/plugin/inline.c
@@ -22,6 +22,12 @@ typedef struct {
 uint64_t count_mem_inline;
 } CPUCount;
 
+typedef struct {
+uint64_t data_insn;
+uint64_t data_tb;
+uint64_t data_mem;
+} CPUData;
+
 static struct qemu_plugin_scoreboard *counts;
 static qemu_plugin_u64 count_tb;
 static qemu_plugin_u64 count_tb_inline;
@@ -29,6 +35,10 @@ static qemu_plugin_u64 count_insn;
 static qemu_plugin_u64 count_insn_inline;
 static qemu_plugin_u64 count_mem;
 static qemu_plugin_u64 count_mem_inline;
+static struct qemu_plugin_scoreboard *data;
+static qemu_plugin_u64 data_insn;
+static qemu_plugin_u64 data_tb;
+static qemu_plugin_u64 data_mem;
 
 static uint64_t global_count_tb;
 static uint64_t global_count_insn;
@@ -109,11 +119,13 @@ static void plugin_exit(qemu_plugin_id_t id, void *udata)
 stats_mem();
 
 qemu_plugin_scoreboard_free(counts);
+qemu_plugin_scoreboard_free(data);
 }
 
 static void vcpu_tb_exec(unsigned int cpu_index, void *udata)
 {
 qemu_plugin_u64_add(count_tb, cpu_index, 1);
+g_assert(qemu_plugin_u64_get(data_tb, cpu_index) == (uintptr_t) udata);
 g_mutex_lock(_lock);
 max_cpu_index = MAX(max_cpu_index, cpu_index);
 global_count_tb++;
@@ -123,6 +135,7 @@ static void vcpu_tb_exec(unsigned int cpu_index, void 
*udata)
 static void vcpu_insn_exec(unsigned int cpu_index, void *udata)
 {
 qemu_plugin_u64_add(count_insn, cpu_index, 1);
+g_assert(qemu_plugin_u64_get(data_insn, cpu_index) == (uintptr_t) udata);
 g_mutex_lock(_lock);
 global_count_insn++;
 g_mutex_unlock(_lock);
@@ -131,9 +144,10 @@ static void vcpu_insn_exec(unsigned int cpu_index, void 
*udata)
 static void vcpu_mem_access(unsigned int cpu_index,
 qemu_plugin_meminfo_t info,
 uint64_t vaddr,
-void *userdata)
+void *udata)
 {
 qemu_plugin_u64_add(count_mem, cpu_index, 1);
+g_assert(qemu_plugin_u64_get(data_mem, cpu_index) == (uintptr_t) udata);
 g_mutex_lock(_lock);
 global_count_mem++;
 g_mutex_unlock(_lock);
@@ -141,20 +155,34 @@ static void vcpu_mem_access(unsigned int cpu_index,
 
 static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb)
 {
+void *tb_store = tb;
+qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu(
+tb, QEMU_PLUGIN_INLINE_STORE_U64, data_tb, (uintptr_t) tb_store);
 qemu_plugin_register_vcpu_tb_exec_cb(
-tb, vcpu_tb_exec, QEMU_PLUGIN_CB_NO_REGS, 0);
+tb, vcpu_tb_exec, QEMU_PLUGIN_CB_NO_REGS, tb_store);
 qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu(
 tb, QEMU_PLUGIN_INLINE_ADD_U64, count_tb_inline, 1);
 
 for (int idx = 0; idx < qemu_plugin_tb_n_insns(tb); ++idx) {
 struct qemu_plugin_insn *insn = qemu_plugin_tb_get_insn(tb, idx);
+void *insn_store = insn;
+void *mem_store = (char *)insn_store + 0xff;
+
+qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu(
+insn, QEMU_PLUGIN_INLINE_STORE_U64, data_insn,
+(uintptr_t) insn_store);
 qemu_plugin_register_vcpu_insn_exec_cb(
-insn, vcpu_insn_exec, QEMU_PLUGIN_CB_NO_REGS, 0);
+insn, vcpu_insn_exec, QEMU_PLUGIN_CB_NO_REGS, insn_store);
 qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu(
 insn, QEMU_PLUGIN_INLINE_ADD_U64, count_insn_inline, 1);
+
+qemu_plugin_register_vcpu_mem_inline_per_vcpu(
+insn, QEMU_PLUGIN_MEM_RW,
+QEMU_PLUGIN_INLINE_STORE_U64,
+data_mem, (uintptr_t) mem_store);
 qemu_plugin_register_vcpu_mem_cb(insn, _mem_access,
  QEMU_PLUGIN_CB_NO_REGS,
- QEMU_PLUGIN_MEM_RW, 0);
+ QEMU_PLUGIN_MEM_RW, mem_store);
 qemu_plugin_register_vcpu_mem_inline_per_vcpu(
 insn, QEMU_PLUGIN_MEM_RW,
 QEMU_PLUGIN_INLINE_ADD_U64,
@@ -179,6 +207,11 @@ int qemu_plugin_install(qemu_plugin_id_t id, const 
qemu_info_t *info,
 counts, CPUCount, count_insn_inline);
 count_mem_inline = qemu_plugin_scoreboard_u64_in_struct(
 counts, CPUCount, count_mem_inline);
+data = qemu_plugin_scoreboard_new(sizeof(CPUData));
+data_insn = qemu_plugin_scoreboard_u64_in_struct(data, CPUData, data_insn);
+data_tb = qemu_plugin_scoreboard_u64_in_struct(data, CPUData, data_tb);
+data_mem = qemu_plugin_scoreboard_u64_in_struct(data, CPUData, data_mem);
+
 qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans);
 qemu_plugin_register_atexit_cb(id, plugin_exit, NULL);
 
-- 
2.39.2




[PATCH v6 5/9] plugins: conditional callbacks

2024-05-02 Thread Pierrick Bouvier
Extend plugins API to support callback called with a given criteria
(evaluated inline).

Added functions:
- qemu_plugin_register_vcpu_tb_exec_cond_cb
- qemu_plugin_register_vcpu_insn_exec_cond_cb

They expect as parameter a condition, a qemu_plugin_u64_t (op1) and an
immediate (op2). Callback is called if op1 |cond| op2 is true.

Reviewed-by: Richard Henderson 
Signed-off-by: Pierrick Bouvier 
---
 include/qemu/plugin.h|  8 
 include/qemu/qemu-plugin.h   | 76 
 plugins/plugin.h |  8 
 accel/tcg/plugin-gen.c   | 48 +++
 plugins/api.c| 39 ++
 plugins/core.c   | 32 +++
 plugins/qemu-plugins.symbols |  2 +
 7 files changed, 213 insertions(+)

diff --git a/include/qemu/plugin.h b/include/qemu/plugin.h
index 0c5df7fa90a..0c0aae09e6f 100644
--- a/include/qemu/plugin.h
+++ b/include/qemu/plugin.h
@@ -68,6 +68,7 @@ union qemu_plugin_cb_sig {
 
 enum plugin_dyn_cb_type {
 PLUGIN_CB_REGULAR,
+PLUGIN_CB_COND,
 PLUGIN_CB_MEM_REGULAR,
 PLUGIN_CB_INLINE_ADD_U64,
 PLUGIN_CB_INLINE_STORE_U64,
@@ -89,6 +90,13 @@ struct qemu_plugin_dyn_cb {
 union qemu_plugin_cb_sig f;
 TCGHelperInfo *info;
 } regular;
+struct {
+union qemu_plugin_cb_sig f;
+TCGHelperInfo *info;
+qemu_plugin_u64 entry;
+enum qemu_plugin_cond cond;
+uint64_t imm;
+} cond;
 struct {
 qemu_plugin_u64 entry;
 enum qemu_plugin_op op;
diff --git a/include/qemu/qemu-plugin.h b/include/qemu/qemu-plugin.h
index c5cac897a0b..337de25ece7 100644
--- a/include/qemu/qemu-plugin.h
+++ b/include/qemu/qemu-plugin.h
@@ -262,6 +262,29 @@ enum qemu_plugin_mem_rw {
 QEMU_PLUGIN_MEM_RW,
 };
 
+/**
+ * enum qemu_plugin_cond - condition to enable callback
+ *
+ * @QEMU_PLUGIN_COND_NEVER: false
+ * @QEMU_PLUGIN_COND_ALWAYS: true
+ * @QEMU_PLUGIN_COND_EQ: is equal?
+ * @QEMU_PLUGIN_COND_NE: is not equal?
+ * @QEMU_PLUGIN_COND_LT: is less than?
+ * @QEMU_PLUGIN_COND_LE: is less than or equal?
+ * @QEMU_PLUGIN_COND_GT: is greater than?
+ * @QEMU_PLUGIN_COND_GE: is greater than or equal?
+ */
+enum qemu_plugin_cond {
+QEMU_PLUGIN_COND_NEVER,
+QEMU_PLUGIN_COND_ALWAYS,
+QEMU_PLUGIN_COND_EQ,
+QEMU_PLUGIN_COND_NE,
+QEMU_PLUGIN_COND_LT,
+QEMU_PLUGIN_COND_LE,
+QEMU_PLUGIN_COND_GT,
+QEMU_PLUGIN_COND_GE,
+};
+
 /**
  * typedef qemu_plugin_vcpu_tb_trans_cb_t - translation callback
  * @id: unique plugin id
@@ -301,6 +324,32 @@ void qemu_plugin_register_vcpu_tb_exec_cb(struct 
qemu_plugin_tb *tb,
   enum qemu_plugin_cb_flags flags,
   void *userdata);
 
+/**
+ * qemu_plugin_register_vcpu_tb_exec_cond_cb() - register conditional callback
+ * @tb: the opaque qemu_plugin_tb handle for the translation
+ * @cb: callback function
+ * @cond: condition to enable callback
+ * @entry: first operand for condition
+ * @imm: second operand for condition
+ * @flags: does the plugin read or write the CPU's registers?
+ * @userdata: any plugin data to pass to the @cb?
+ *
+ * The @cb function is called when a translated unit executes if
+ * entry @cond imm is true.
+ * If condition is QEMU_PLUGIN_COND_ALWAYS, condition is never interpreted and
+ * this function is equivalent to qemu_plugin_register_vcpu_tb_exec_cb.
+ * If condition QEMU_PLUGIN_COND_NEVER, condition is never interpreted and
+ * callback is never installed.
+ */
+QEMU_PLUGIN_API
+void qemu_plugin_register_vcpu_tb_exec_cond_cb(struct qemu_plugin_tb *tb,
+   qemu_plugin_vcpu_udata_cb_t cb,
+   enum qemu_plugin_cb_flags flags,
+   enum qemu_plugin_cond cond,
+   qemu_plugin_u64 entry,
+   uint64_t imm,
+   void *userdata);
+
 /**
  * enum qemu_plugin_op - describes an inline op
  *
@@ -344,6 +393,33 @@ void qemu_plugin_register_vcpu_insn_exec_cb(struct 
qemu_plugin_insn *insn,
 enum qemu_plugin_cb_flags flags,
 void *userdata);
 
+/**
+ * qemu_plugin_register_vcpu_insn_exec_cond_cb() - conditional insn execution 
cb
+ * @insn: the opaque qemu_plugin_insn handle for an instruction
+ * @cb: callback function
+ * @flags: does the plugin read or write the CPU's registers?
+ * @cond: condition to enable callback
+ * @entry: first operand for condition
+ * @imm: second operand for condition
+ * @userdata: any plugin data to pass to the @cb?
+ *
+ * The @cb function is called when an instruction executes if
+ * entry @cond imm is true.
+ * If condition is QEMU_PLUGIN_COND_ALWAYS, condition is never

[PATCH v6 8/9] plugins: extract cpu_index generate

2024-05-02 Thread Pierrick Bouvier
Factorizes function to access current cpu index for a given vcpu.

Reviewed-by: Richard Henderson 
Signed-off-by: Pierrick Bouvier 
---
 accel/tcg/plugin-gen.c | 28 +---
 1 file changed, 13 insertions(+), 15 deletions(-)

diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c
index b829a959398..7b73520e788 100644
--- a/accel/tcg/plugin-gen.c
+++ b/accel/tcg/plugin-gen.c
@@ -101,12 +101,17 @@ static void gen_disable_mem_helper(void)
offsetof(ArchCPU, env));
 }
 
+static TCGv_i32 gen_cpu_index(void)
+{
+TCGv_i32 cpu_index = tcg_temp_ebb_new_i32();
+tcg_gen_ld_i32(cpu_index, tcg_env,
+   -offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index));
+return cpu_index;
+}
+
 static void gen_udata_cb(struct qemu_plugin_regular_cb *cb)
 {
-TCGv_i32 cpu_index = tcg_temp_ebb_new_i32();
-
-tcg_gen_ld_i32(cpu_index, tcg_env,
-   -offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index));
+TCGv_i32 cpu_index = gen_cpu_index();
 tcg_gen_call2(cb->f.vcpu_udata, cb->info, NULL,
   tcgv_i32_temp(cpu_index),
   tcgv_ptr_temp(tcg_constant_ptr(cb->userp)));
@@ -121,9 +126,7 @@ static TCGv_ptr gen_plugin_u64_ptr(qemu_plugin_u64 entry)
 char *base_ptr = arr->data + entry.offset;
 size_t entry_size = g_array_get_element_size(arr);
 
-TCGv_i32 cpu_index = tcg_temp_ebb_new_i32();
-tcg_gen_ld_i32(cpu_index, tcg_env,
-   -offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index));
+TCGv_i32 cpu_index = gen_cpu_index();
 tcg_gen_muli_i32(cpu_index, cpu_index, entry_size);
 tcg_gen_ext_i32_ptr(ptr, cpu_index);
 tcg_temp_free_i32(cpu_index);
@@ -156,7 +159,6 @@ static TCGCond plugin_cond_to_tcgcond(enum qemu_plugin_cond 
cond)
 static void gen_udata_cond_cb(struct qemu_plugin_conditional_cb *cb)
 {
 TCGv_ptr ptr = gen_plugin_u64_ptr(cb->entry);
-TCGv_i32 cpu_index = tcg_temp_ebb_new_i32();
 TCGv_i64 val = tcg_temp_ebb_new_i64();
 TCGLabel *after_cb = gen_new_label();
 
@@ -165,15 +167,14 @@ static void gen_udata_cond_cb(struct 
qemu_plugin_conditional_cb *cb)
 
 tcg_gen_ld_i64(val, ptr, 0);
 tcg_gen_brcondi_i64(cond, val, cb->imm, after_cb);
-tcg_gen_ld_i32(cpu_index, tcg_env,
-   -offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index));
+TCGv_i32 cpu_index = gen_cpu_index();
 tcg_gen_call2(cb->f.vcpu_udata, cb->info, NULL,
   tcgv_i32_temp(cpu_index),
   tcgv_ptr_temp(tcg_constant_ptr(cb->userp)));
+tcg_temp_free_i32(cpu_index);
 gen_set_label(after_cb);
 
 tcg_temp_free_i64(val);
-tcg_temp_free_i32(cpu_index);
 tcg_temp_free_ptr(ptr);
 }
 
@@ -203,10 +204,7 @@ static void gen_inline_store_u64_cb(struct 
qemu_plugin_inline_cb *cb)
 static void gen_mem_cb(struct qemu_plugin_regular_cb *cb,
qemu_plugin_meminfo_t meminfo, TCGv_i64 addr)
 {
-TCGv_i32 cpu_index = tcg_temp_ebb_new_i32();
-
-tcg_gen_ld_i32(cpu_index, tcg_env,
-   -offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index));
+TCGv_i32 cpu_index = gen_cpu_index();
 tcg_gen_call4(cb->f.vcpu_mem, cb->info, NULL,
   tcgv_i32_temp(cpu_index),
   tcgv_i32_temp(tcg_constant_i32(meminfo)),
-- 
2.39.2




Re: [PATCH v5 0/9] TCG plugins new inline operations

2024-05-02 Thread Pierrick Bouvier

On 5/2/24 13:48, Gustavo Romero wrote:

Hi Pierrick,

On 5/2/24 5:09 PM, Gustavo Romero wrote:

On 5/2/24 4:45 PM, Pierrick Bouvier wrote:

Hi Gustavo,

On 5/2/24 12:16, Gustavo Romero wrote:

Hi Pierrick,

On 5/2/24 3:08 PM, Pierrick Bouvier wrote:

This series implement two new operations for plugins:
- Store inline allows to write a specific value to a scoreboard.
- Conditional callback executes a callback only when a given condition is true.
     The condition is evaluated inline.

It's possible to mix various inline operations (add, store) with conditional
callbacks, allowing efficient "trap" based counters.

It builds on top of new scoreboard API, introduced in the previous series.

NOTE: Two patches still need review

v2
--

- fixed issue with udata not being passed to conditional callback
- added specific test for this in tests/plugin/inline.c (udata was NULL before).

v3
--

- rebased on top of "plugins: Rewrite plugin code generation":
     20240316015720.3661236-1-richard.hender...@linaro.org
- single pass code generation
- small cleanups for code generation

v4
--

- remove op field from qemu_plugin_inline_cb
- use tcg_constant_i64 to load immediate value to store

v5
--

- rebase on top of master now that Richard's series was merged

Pierrick Bouvier (9):
     plugins: prepare introduction of new inline ops
     plugins: extract generate ptr for qemu_plugin_u64
     plugins: add new inline op STORE_U64
     tests/plugin/inline: add test for STORE_U64 inline op
     plugins: conditional callbacks
     tests/plugin/inline: add test for conditional callback
     plugins: distinct types for callbacks
     plugins: extract cpu_index generate
     plugins: remove op from qemu_plugin_inline_cb

    include/qemu/plugin.h    |  42 +++
    include/qemu/qemu-plugin.h   |  80 -
    plugins/plugin.h |  12 +++-
    accel/tcg/plugin-gen.c   | 136 +++
    plugins/api.c    |  39 ++
    plugins/core.c   | 109 
    tests/plugin/inline.c    | 130 +++--
    plugins/qemu-plugins.symbols |   2 +
    8 files changed, 466 insertions(+), 84 deletions(-)


The description in the commit message of patches 1/9, 2/9, 6/9, 7/9, and 8/9 is 
missing.

Is this intentional?



Do you mean there is no multiline commit message for those changes?
Indeed, for some of those patches, the change is a single line commit message.


I just see a commit title and the Signed-off-by. For example, in 8/9
I see the following on git log:

commit f518898aa09b42e317b887237bb75a432b477c6d
Author: Pierrick Bouvier 
Date:   Thu May 2 11:08:46 2024 -0700

      plugins: extract cpu_index generate
      Reviewed-by: Richard Henderson 
      Signed-off-by: Pierrick Bouvier 

It has only the title: "plugins: extract cpu_index generate"
and R-b and S-b, so no description about the changes.


The description (the body of the commit message) is mentioned here:

https://www.qemu.org/docs/master/devel/submitting-a-patch.html#write-a-meaningful-commit-message

After quickly looking at the committed logs, I do notice some commits missing 
it,
but I really believe it's important to have it for the reasons outlined in these
guidelines.



You're right, I just sent a v6 with more elaborated commit messages.

Thanks,
Pierrick



Cheers,
Gustavo


[PATCH v6 0/9] TCG plugins new inline operations

2024-05-02 Thread Pierrick Bouvier
This series implement two new operations for plugins:
- Store inline allows to write a specific value to a scoreboard.
- Conditional callback executes a callback only when a given condition is true.
  The condition is evaluated inline.

It's possible to mix various inline operations (add, store) with conditional
callbacks, allowing efficient "trap" based counters.

It builds on top of new scoreboard API, introduced in the previous series.

v2
--

- fixed issue with udata not being passed to conditional callback
- added specific test for this in tests/plugin/inline.c (udata was NULL before).

v3
--

- rebased on top of "plugins: Rewrite plugin code generation":
  20240316015720.3661236-1-richard.hender...@linaro.org
- single pass code generation
- small cleanups for code generation

v4
--

- remove op field from qemu_plugin_inline_cb
- use tcg_constant_i64 to load immediate value to store

v5
--

- rebase on top of master now that Richard's series was merged

v6
--

- more elaborated commit messages

Pierrick Bouvier (9):
  plugins: prepare introduction of new inline ops
  plugins: extract generate ptr for qemu_plugin_u64
  plugins: add new inline op STORE_U64
  tests/plugin/inline: add test for STORE_U64 inline op
  plugins: conditional callbacks
  tests/plugin/inline: add test for conditional callback
  plugins: distinct types for callbacks
  plugins: extract cpu_index generate
  plugins: remove op from qemu_plugin_inline_cb

 include/qemu/plugin.h|  42 +++
 include/qemu/qemu-plugin.h   |  80 -
 plugins/plugin.h |  12 +++-
 accel/tcg/plugin-gen.c   | 136 +++
 plugins/api.c|  39 ++
 plugins/core.c   | 109 
 tests/plugin/inline.c| 130 +++--
 plugins/qemu-plugins.symbols |   2 +
 8 files changed, 466 insertions(+), 84 deletions(-)

-- 
2.39.2




[PATCH v6 1/9] plugins: prepare introduction of new inline ops

2024-05-02 Thread Pierrick Bouvier
Until now, only add_u64 was available, and all functions assumed this or
were named uniquely.

Reviewed-by: Richard Henderson 
Signed-off-by: Pierrick Bouvier 
---
 include/qemu/plugin.h  |  2 +-
 accel/tcg/plugin-gen.c |  6 +++---
 plugins/core.c | 14 --
 3 files changed, 16 insertions(+), 6 deletions(-)

diff --git a/include/qemu/plugin.h b/include/qemu/plugin.h
index 18062528c17..bee1647cfc4 100644
--- a/include/qemu/plugin.h
+++ b/include/qemu/plugin.h
@@ -69,7 +69,7 @@ union qemu_plugin_cb_sig {
 enum plugin_dyn_cb_type {
 PLUGIN_CB_REGULAR,
 PLUGIN_CB_MEM_REGULAR,
-PLUGIN_CB_INLINE,
+PLUGIN_CB_INLINE_ADD_U64,
 };
 
 /*
diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c
index 3db74ae9bfe..8028ae76c3a 100644
--- a/accel/tcg/plugin-gen.c
+++ b/accel/tcg/plugin-gen.c
@@ -113,7 +113,7 @@ static void gen_udata_cb(struct qemu_plugin_dyn_cb *cb)
 tcg_temp_free_i32(cpu_index);
 }
 
-static void gen_inline_cb(struct qemu_plugin_dyn_cb *cb)
+static void gen_inline_add_u64_cb(struct qemu_plugin_dyn_cb *cb)
 {
 GArray *arr = cb->inline_insn.entry.score->data;
 size_t offset = cb->inline_insn.entry.offset;
@@ -158,8 +158,8 @@ static void inject_cb(struct qemu_plugin_dyn_cb *cb)
 case PLUGIN_CB_REGULAR:
 gen_udata_cb(cb);
 break;
-case PLUGIN_CB_INLINE:
-gen_inline_cb(cb);
+case PLUGIN_CB_INLINE_ADD_U64:
+gen_inline_add_u64_cb(cb);
 break;
 default:
 g_assert_not_reached();
diff --git a/plugins/core.c b/plugins/core.c
index 0213513ec65..a8557b54ff7 100644
--- a/plugins/core.c
+++ b/plugins/core.c
@@ -316,6 +316,16 @@ static struct qemu_plugin_dyn_cb *plugin_get_dyn_cb(GArray 
**arr)
 return _array_index(cbs, struct qemu_plugin_dyn_cb, cbs->len - 1);
 }
 
+static enum plugin_dyn_cb_type op_to_cb_type(enum qemu_plugin_op op)
+{
+switch (op) {
+case QEMU_PLUGIN_INLINE_ADD_U64:
+return PLUGIN_CB_INLINE_ADD_U64;
+default:
+g_assert_not_reached();
+}
+}
+
 void plugin_register_inline_op_on_entry(GArray **arr,
 enum qemu_plugin_mem_rw rw,
 enum qemu_plugin_op op,
@@ -326,7 +336,7 @@ void plugin_register_inline_op_on_entry(GArray **arr,
 
 dyn_cb = plugin_get_dyn_cb(arr);
 dyn_cb->userp = NULL;
-dyn_cb->type = PLUGIN_CB_INLINE;
+dyn_cb->type = op_to_cb_type(op);
 dyn_cb->rw = rw;
 dyn_cb->inline_insn.entry = entry;
 dyn_cb->inline_insn.op = op;
@@ -551,7 +561,7 @@ void qemu_plugin_vcpu_mem_cb(CPUState *cpu, uint64_t vaddr,
 cb->regular.f.vcpu_mem(cpu->cpu_index, make_plugin_meminfo(oi, rw),
vaddr, cb->userp);
 break;
-case PLUGIN_CB_INLINE:
+case PLUGIN_CB_INLINE_ADD_U64:
 exec_inline_op(cb, cpu->cpu_index);
 break;
 default:
-- 
2.39.2




[PATCH v6 7/9] plugins: distinct types for callbacks

2024-05-02 Thread Pierrick Bouvier
To prevent errors when writing new types of callbacks or inline
operations, we split callbacks data to distinct types.

Reviewed-by: Richard Henderson 
Signed-off-by: Pierrick Bouvier 
---
 include/qemu/plugin.h  | 46 ++---
 plugins/plugin.h   |  2 +-
 accel/tcg/plugin-gen.c | 58 +---
 plugins/core.c | 76 ++
 4 files changed, 98 insertions(+), 84 deletions(-)

diff --git a/include/qemu/plugin.h b/include/qemu/plugin.h
index 0c0aae09e6f..313b7c72684 100644
--- a/include/qemu/plugin.h
+++ b/include/qemu/plugin.h
@@ -74,34 +74,40 @@ enum plugin_dyn_cb_type {
 PLUGIN_CB_INLINE_STORE_U64,
 };
 
+struct qemu_plugin_regular_cb {
+union qemu_plugin_cb_sig f;
+TCGHelperInfo *info;
+void *userp;
+enum qemu_plugin_mem_rw rw;
+};
+
+struct qemu_plugin_inline_cb {
+qemu_plugin_u64 entry;
+enum qemu_plugin_op op;
+uint64_t imm;
+enum qemu_plugin_mem_rw rw;
+};
+
+struct qemu_plugin_conditional_cb {
+union qemu_plugin_cb_sig f;
+TCGHelperInfo *info;
+void *userp;
+qemu_plugin_u64 entry;
+enum qemu_plugin_cond cond;
+uint64_t imm;
+};
+
 /*
  * 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 {
-void *userp;
 enum plugin_dyn_cb_type type;
-/* @rw applies to mem callbacks only (both regular and inline) */
-enum qemu_plugin_mem_rw rw;
-/* fields specific to each dyn_cb type go here */
 union {
-struct {
-union qemu_plugin_cb_sig f;
-TCGHelperInfo *info;
-} regular;
-struct {
-union qemu_plugin_cb_sig f;
-TCGHelperInfo *info;
-qemu_plugin_u64 entry;
-enum qemu_plugin_cond cond;
-uint64_t imm;
-} cond;
-struct {
-qemu_plugin_u64 entry;
-enum qemu_plugin_op op;
-uint64_t imm;
-} inline_insn;
+struct qemu_plugin_regular_cb regular;
+struct qemu_plugin_conditional_cb cond;
+struct qemu_plugin_inline_cb inline_insn;
 };
 };
 
diff --git a/plugins/plugin.h b/plugins/plugin.h
index 7d4b4e21f7c..80d5daa9171 100644
--- a/plugins/plugin.h
+++ b/plugins/plugin.h
@@ -108,7 +108,7 @@ void plugin_register_vcpu_mem_cb(GArray **arr,
  enum qemu_plugin_mem_rw rw,
  void *udata);
 
-void exec_inline_op(struct qemu_plugin_dyn_cb *cb, int cpu_index);
+void exec_inline_op(struct qemu_plugin_inline_cb *cb, int cpu_index);
 
 int plugin_num_vcpus(void);
 
diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c
index 9deddd74c42..b829a959398 100644
--- a/accel/tcg/plugin-gen.c
+++ b/accel/tcg/plugin-gen.c
@@ -101,13 +101,13 @@ static void gen_disable_mem_helper(void)
offsetof(ArchCPU, env));
 }
 
-static void gen_udata_cb(struct qemu_plugin_dyn_cb *cb)
+static void gen_udata_cb(struct qemu_plugin_regular_cb *cb)
 {
 TCGv_i32 cpu_index = tcg_temp_ebb_new_i32();
 
 tcg_gen_ld_i32(cpu_index, tcg_env,
-offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index));
-tcg_gen_call2(cb->regular.f.vcpu_udata, cb->regular.info, NULL,
+tcg_gen_call2(cb->f.vcpu_udata, cb->info, NULL,
   tcgv_i32_temp(cpu_index),
   tcgv_ptr_temp(tcg_constant_ptr(cb->userp)));
 tcg_temp_free_i32(cpu_index);
@@ -153,21 +153,21 @@ static TCGCond plugin_cond_to_tcgcond(enum 
qemu_plugin_cond cond)
 }
 }
 
-static void gen_udata_cond_cb(struct qemu_plugin_dyn_cb *cb)
+static void gen_udata_cond_cb(struct qemu_plugin_conditional_cb *cb)
 {
-TCGv_ptr ptr = gen_plugin_u64_ptr(cb->cond.entry);
+TCGv_ptr ptr = gen_plugin_u64_ptr(cb->entry);
 TCGv_i32 cpu_index = tcg_temp_ebb_new_i32();
 TCGv_i64 val = tcg_temp_ebb_new_i64();
 TCGLabel *after_cb = gen_new_label();
 
 /* Condition should be negated, as calling the cb is the "else" path */
-TCGCond cond = tcg_invert_cond(plugin_cond_to_tcgcond(cb->cond.cond));
+TCGCond cond = tcg_invert_cond(plugin_cond_to_tcgcond(cb->cond));
 
 tcg_gen_ld_i64(val, ptr, 0);
-tcg_gen_brcondi_i64(cond, val, cb->cond.imm, after_cb);
+tcg_gen_brcondi_i64(cond, val, cb->imm, after_cb);
 tcg_gen_ld_i32(cpu_index, tcg_env,
-offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index));
-tcg_gen_call2(cb->cond.f.vcpu_udata, cb->cond.info, NULL,
+tcg_gen_call2(cb->f.vcpu_udata, cb->info, NULL,
   tcgv_i32_temp(cpu_index),
   tcgv_ptr_temp(tcg_constant_ptr(cb->userp)));
 gen_set_label(after_cb);
@@ -177,37 +177,37 @@ static void gen_udata_cond_cb(struct qemu_plugin_d

[PATCH v6 9/9] plugins: remove op from qemu_plugin_inline_cb

2024-05-02 Thread Pierrick Bouvier
This field is not needed as the callback type already holds this
information.

Reviewed-by: Richard Henderson 
Signed-off-by: Pierrick Bouvier 
---
 include/qemu/plugin.h |  1 -
 plugins/plugin.h  |  4 +++-
 plugins/core.c| 13 +++--
 3 files changed, 10 insertions(+), 8 deletions(-)

diff --git a/include/qemu/plugin.h b/include/qemu/plugin.h
index 313b7c72684..1ad8a59209b 100644
--- a/include/qemu/plugin.h
+++ b/include/qemu/plugin.h
@@ -83,7 +83,6 @@ struct qemu_plugin_regular_cb {
 
 struct qemu_plugin_inline_cb {
 qemu_plugin_u64 entry;
-enum qemu_plugin_op op;
 uint64_t imm;
 enum qemu_plugin_mem_rw rw;
 };
diff --git a/plugins/plugin.h b/plugins/plugin.h
index 80d5daa9171..30e2299a54d 100644
--- a/plugins/plugin.h
+++ b/plugins/plugin.h
@@ -108,7 +108,9 @@ void plugin_register_vcpu_mem_cb(GArray **arr,
  enum qemu_plugin_mem_rw rw,
  void *udata);
 
-void exec_inline_op(struct qemu_plugin_inline_cb *cb, int cpu_index);
+void exec_inline_op(enum plugin_dyn_cb_type type,
+struct qemu_plugin_inline_cb *cb,
+int cpu_index);
 
 int plugin_num_vcpus(void);
 
diff --git a/plugins/core.c b/plugins/core.c
index 7ea2ee208db..a9f19e197aa 100644
--- a/plugins/core.c
+++ b/plugins/core.c
@@ -338,7 +338,6 @@ void plugin_register_inline_op_on_entry(GArray **arr,
 
 struct qemu_plugin_inline_cb inline_cb = { .rw = rw,
.entry = entry,
-   .op = op,
.imm = imm };
 dyn_cb = plugin_get_dyn_cb(arr);
 dyn_cb->type = op_to_cb_type(op);
@@ -557,7 +556,9 @@ void qemu_plugin_flush_cb(void)
 plugin_cb__simple(QEMU_PLUGIN_EV_FLUSH);
 }
 
-void exec_inline_op(struct qemu_plugin_inline_cb *cb, int cpu_index)
+void exec_inline_op(enum plugin_dyn_cb_type type,
+struct qemu_plugin_inline_cb *cb,
+int cpu_index)
 {
 char *ptr = cb->entry.score->data->data;
 size_t elem_size = g_array_get_element_size(
@@ -565,11 +566,11 @@ void exec_inline_op(struct qemu_plugin_inline_cb *cb, int 
cpu_index)
 size_t offset = cb->entry.offset;
 uint64_t *val = (uint64_t *)(ptr + offset + cpu_index * elem_size);
 
-switch (cb->op) {
-case QEMU_PLUGIN_INLINE_ADD_U64:
+switch (type) {
+case PLUGIN_CB_INLINE_ADD_U64:
 *val += cb->imm;
 break;
-case QEMU_PLUGIN_INLINE_STORE_U64:
+case PLUGIN_CB_INLINE_STORE_U64:
 *val = cb->imm;
 break;
 default:
@@ -601,7 +602,7 @@ void qemu_plugin_vcpu_mem_cb(CPUState *cpu, uint64_t vaddr,
 case PLUGIN_CB_INLINE_ADD_U64:
 case PLUGIN_CB_INLINE_STORE_U64:
 if (rw && cb->inline_insn.rw) {
-exec_inline_op(>inline_insn, cpu->cpu_index);
+exec_inline_op(cb->type, >inline_insn, cpu->cpu_index);
 }
 break;
 default:
-- 
2.39.2




Re: [PATCH v5 0/9] TCG plugins new inline operations

2024-05-02 Thread Pierrick Bouvier

Hi Gustavo,

On 5/2/24 12:16, Gustavo Romero wrote:

Hi Pierrick,

On 5/2/24 3:08 PM, Pierrick Bouvier wrote:

This series implement two new operations for plugins:
- Store inline allows to write a specific value to a scoreboard.
- Conditional callback executes a callback only when a given condition is true.
The condition is evaluated inline.

It's possible to mix various inline operations (add, store) with conditional
callbacks, allowing efficient "trap" based counters.

It builds on top of new scoreboard API, introduced in the previous series.

NOTE: Two patches still need review

v2
--

- fixed issue with udata not being passed to conditional callback
- added specific test for this in tests/plugin/inline.c (udata was NULL before).

v3
--

- rebased on top of "plugins: Rewrite plugin code generation":
20240316015720.3661236-1-richard.hender...@linaro.org
- single pass code generation
- small cleanups for code generation

v4
--

- remove op field from qemu_plugin_inline_cb
- use tcg_constant_i64 to load immediate value to store

v5
--

- rebase on top of master now that Richard's series was merged

Pierrick Bouvier (9):
plugins: prepare introduction of new inline ops
plugins: extract generate ptr for qemu_plugin_u64
plugins: add new inline op STORE_U64
tests/plugin/inline: add test for STORE_U64 inline op
plugins: conditional callbacks
tests/plugin/inline: add test for conditional callback
plugins: distinct types for callbacks
plugins: extract cpu_index generate
plugins: remove op from qemu_plugin_inline_cb

   include/qemu/plugin.h|  42 +++
   include/qemu/qemu-plugin.h   |  80 -
   plugins/plugin.h |  12 +++-
   accel/tcg/plugin-gen.c   | 136 +++
   plugins/api.c|  39 ++
   plugins/core.c   | 109 
   tests/plugin/inline.c| 130 +++--
   plugins/qemu-plugins.symbols |   2 +
   8 files changed, 466 insertions(+), 84 deletions(-)


The description in the commit message of patches 1/9, 2/9, 6/9, 7/9, and 8/9 is 
missing.

Is this intentional?



Do you mean there is no multiline commit message for those changes?
Indeed, for some of those patches, the change is a single line commit 
message.




Cheers,
Gustavo




Re: [PATCH v5 0/9] TCG plugins new inline operations

2024-05-02 Thread Pierrick Bouvier
Contrary to what the cover letter says, all patches have been reviewed 
in the series since v4.


On 5/2/24 11:08, Pierrick Bouvier wrote:

This series implement two new operations for plugins:
- Store inline allows to write a specific value to a scoreboard.
- Conditional callback executes a callback only when a given condition is true.
   The condition is evaluated inline.

It's possible to mix various inline operations (add, store) with conditional
callbacks, allowing efficient "trap" based counters.

It builds on top of new scoreboard API, introduced in the previous series.

NOTE: Two patches still need review

v2
--

- fixed issue with udata not being passed to conditional callback
- added specific test for this in tests/plugin/inline.c (udata was NULL before).

v3
--

- rebased on top of "plugins: Rewrite plugin code generation":
   20240316015720.3661236-1-richard.hender...@linaro.org
- single pass code generation
- small cleanups for code generation

v4
--

- remove op field from qemu_plugin_inline_cb
- use tcg_constant_i64 to load immediate value to store

v5
--

- rebase on top of master now that Richard's series was merged

Pierrick Bouvier (9):
   plugins: prepare introduction of new inline ops
   plugins: extract generate ptr for qemu_plugin_u64
   plugins: add new inline op STORE_U64
   tests/plugin/inline: add test for STORE_U64 inline op
   plugins: conditional callbacks
   tests/plugin/inline: add test for conditional callback
   plugins: distinct types for callbacks
   plugins: extract cpu_index generate
   plugins: remove op from qemu_plugin_inline_cb

  include/qemu/plugin.h|  42 +++
  include/qemu/qemu-plugin.h   |  80 -
  plugins/plugin.h |  12 +++-
  accel/tcg/plugin-gen.c   | 136 +++
  plugins/api.c|  39 ++
  plugins/core.c   | 109 
  tests/plugin/inline.c| 130 +++--
  plugins/qemu-plugins.symbols |   2 +
  8 files changed, 466 insertions(+), 84 deletions(-)





[PATCH v5 2/9] plugins: extract generate ptr for qemu_plugin_u64

2024-05-02 Thread Pierrick Bouvier
Reviewed-by: Richard Henderson 
Signed-off-by: Pierrick Bouvier 
---
 accel/tcg/plugin-gen.c | 27 ++-
 1 file changed, 18 insertions(+), 9 deletions(-)

diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c
index 8028ae76c3a..8e2c3ef94f6 100644
--- a/accel/tcg/plugin-gen.c
+++ b/accel/tcg/plugin-gen.c
@@ -113,24 +113,33 @@ static void gen_udata_cb(struct qemu_plugin_dyn_cb *cb)
 tcg_temp_free_i32(cpu_index);
 }
 
-static void gen_inline_add_u64_cb(struct qemu_plugin_dyn_cb *cb)
+static TCGv_ptr gen_plugin_u64_ptr(qemu_plugin_u64 entry)
 {
-GArray *arr = cb->inline_insn.entry.score->data;
-size_t offset = cb->inline_insn.entry.offset;
-TCGv_i32 cpu_index = tcg_temp_ebb_new_i32();
-TCGv_i64 val = tcg_temp_ebb_new_i64();
 TCGv_ptr ptr = tcg_temp_ebb_new_ptr();
 
+GArray *arr = entry.score->data;
+char *base_ptr = arr->data + entry.offset;
+size_t entry_size = g_array_get_element_size(arr);
+
+TCGv_i32 cpu_index = tcg_temp_ebb_new_i32();
 tcg_gen_ld_i32(cpu_index, tcg_env,
-offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index));
-tcg_gen_muli_i32(cpu_index, cpu_index, g_array_get_element_size(arr));
+tcg_gen_muli_i32(cpu_index, cpu_index, entry_size);
 tcg_gen_ext_i32_ptr(ptr, cpu_index);
 tcg_temp_free_i32(cpu_index);
+tcg_gen_addi_ptr(ptr, ptr, (intptr_t) base_ptr);
 
-tcg_gen_addi_ptr(ptr, ptr, (intptr_t)arr->data);
-tcg_gen_ld_i64(val, ptr, offset);
+return ptr;
+}
+
+static void gen_inline_add_u64_cb(struct qemu_plugin_dyn_cb *cb)
+{
+TCGv_ptr ptr = gen_plugin_u64_ptr(cb->inline_insn.entry);
+TCGv_i64 val = tcg_temp_ebb_new_i64();
+
+tcg_gen_ld_i64(val, ptr, 0);
 tcg_gen_addi_i64(val, val, cb->inline_insn.imm);
-tcg_gen_st_i64(val, ptr, offset);
+tcg_gen_st_i64(val, ptr, 0);
 
 tcg_temp_free_i64(val);
 tcg_temp_free_ptr(ptr);
-- 
2.39.2




[PATCH v5 4/9] tests/plugin/inline: add test for STORE_U64 inline op

2024-05-02 Thread Pierrick Bouvier
Reviewed-by: Richard Henderson 
Signed-off-by: Pierrick Bouvier 
---
 tests/plugin/inline.c | 41 +
 1 file changed, 37 insertions(+), 4 deletions(-)

diff --git a/tests/plugin/inline.c b/tests/plugin/inline.c
index 0163e9b51c5..103c3a22f6e 100644
--- a/tests/plugin/inline.c
+++ b/tests/plugin/inline.c
@@ -22,6 +22,12 @@ typedef struct {
 uint64_t count_mem_inline;
 } CPUCount;
 
+typedef struct {
+uint64_t data_insn;
+uint64_t data_tb;
+uint64_t data_mem;
+} CPUData;
+
 static struct qemu_plugin_scoreboard *counts;
 static qemu_plugin_u64 count_tb;
 static qemu_plugin_u64 count_tb_inline;
@@ -29,6 +35,10 @@ static qemu_plugin_u64 count_insn;
 static qemu_plugin_u64 count_insn_inline;
 static qemu_plugin_u64 count_mem;
 static qemu_plugin_u64 count_mem_inline;
+static struct qemu_plugin_scoreboard *data;
+static qemu_plugin_u64 data_insn;
+static qemu_plugin_u64 data_tb;
+static qemu_plugin_u64 data_mem;
 
 static uint64_t global_count_tb;
 static uint64_t global_count_insn;
@@ -109,11 +119,13 @@ static void plugin_exit(qemu_plugin_id_t id, void *udata)
 stats_mem();
 
 qemu_plugin_scoreboard_free(counts);
+qemu_plugin_scoreboard_free(data);
 }
 
 static void vcpu_tb_exec(unsigned int cpu_index, void *udata)
 {
 qemu_plugin_u64_add(count_tb, cpu_index, 1);
+g_assert(qemu_plugin_u64_get(data_tb, cpu_index) == (uintptr_t) udata);
 g_mutex_lock(_lock);
 max_cpu_index = MAX(max_cpu_index, cpu_index);
 global_count_tb++;
@@ -123,6 +135,7 @@ static void vcpu_tb_exec(unsigned int cpu_index, void 
*udata)
 static void vcpu_insn_exec(unsigned int cpu_index, void *udata)
 {
 qemu_plugin_u64_add(count_insn, cpu_index, 1);
+g_assert(qemu_plugin_u64_get(data_insn, cpu_index) == (uintptr_t) udata);
 g_mutex_lock(_lock);
 global_count_insn++;
 g_mutex_unlock(_lock);
@@ -131,9 +144,10 @@ static void vcpu_insn_exec(unsigned int cpu_index, void 
*udata)
 static void vcpu_mem_access(unsigned int cpu_index,
 qemu_plugin_meminfo_t info,
 uint64_t vaddr,
-void *userdata)
+void *udata)
 {
 qemu_plugin_u64_add(count_mem, cpu_index, 1);
+g_assert(qemu_plugin_u64_get(data_mem, cpu_index) == (uintptr_t) udata);
 g_mutex_lock(_lock);
 global_count_mem++;
 g_mutex_unlock(_lock);
@@ -141,20 +155,34 @@ static void vcpu_mem_access(unsigned int cpu_index,
 
 static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb)
 {
+void *tb_store = tb;
+qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu(
+tb, QEMU_PLUGIN_INLINE_STORE_U64, data_tb, (uintptr_t) tb_store);
 qemu_plugin_register_vcpu_tb_exec_cb(
-tb, vcpu_tb_exec, QEMU_PLUGIN_CB_NO_REGS, 0);
+tb, vcpu_tb_exec, QEMU_PLUGIN_CB_NO_REGS, tb_store);
 qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu(
 tb, QEMU_PLUGIN_INLINE_ADD_U64, count_tb_inline, 1);
 
 for (int idx = 0; idx < qemu_plugin_tb_n_insns(tb); ++idx) {
 struct qemu_plugin_insn *insn = qemu_plugin_tb_get_insn(tb, idx);
+void *insn_store = insn;
+void *mem_store = (char *)insn_store + 0xff;
+
+qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu(
+insn, QEMU_PLUGIN_INLINE_STORE_U64, data_insn,
+(uintptr_t) insn_store);
 qemu_plugin_register_vcpu_insn_exec_cb(
-insn, vcpu_insn_exec, QEMU_PLUGIN_CB_NO_REGS, 0);
+insn, vcpu_insn_exec, QEMU_PLUGIN_CB_NO_REGS, insn_store);
 qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu(
 insn, QEMU_PLUGIN_INLINE_ADD_U64, count_insn_inline, 1);
+
+qemu_plugin_register_vcpu_mem_inline_per_vcpu(
+insn, QEMU_PLUGIN_MEM_RW,
+QEMU_PLUGIN_INLINE_STORE_U64,
+data_mem, (uintptr_t) mem_store);
 qemu_plugin_register_vcpu_mem_cb(insn, _mem_access,
  QEMU_PLUGIN_CB_NO_REGS,
- QEMU_PLUGIN_MEM_RW, 0);
+ QEMU_PLUGIN_MEM_RW, mem_store);
 qemu_plugin_register_vcpu_mem_inline_per_vcpu(
 insn, QEMU_PLUGIN_MEM_RW,
 QEMU_PLUGIN_INLINE_ADD_U64,
@@ -179,6 +207,11 @@ int qemu_plugin_install(qemu_plugin_id_t id, const 
qemu_info_t *info,
 counts, CPUCount, count_insn_inline);
 count_mem_inline = qemu_plugin_scoreboard_u64_in_struct(
 counts, CPUCount, count_mem_inline);
+data = qemu_plugin_scoreboard_new(sizeof(CPUData));
+data_insn = qemu_plugin_scoreboard_u64_in_struct(data, CPUData, data_insn);
+data_tb = qemu_plugin_scoreboard_u64_in_struct(data, CPUData, data_tb);
+data_mem = qemu_plugin_scoreboard_u64_in_struct(data, CPUData, data_mem);
+
 qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans);
 qemu_plugin_register_atexit_cb(id, plugin_exit, NULL);
 
-- 
2.39.2




[PATCH v5 6/9] tests/plugin/inline: add test for conditional callback

2024-05-02 Thread Pierrick Bouvier
Count number of tb and insn executed using a conditional callback. We
ensure the callback has been called expected number of time (per vcpu).

Reviewed-by: Richard Henderson 
Signed-off-by: Pierrick Bouvier 
---
 tests/plugin/inline.c | 89 +--
 1 file changed, 86 insertions(+), 3 deletions(-)

diff --git a/tests/plugin/inline.c b/tests/plugin/inline.c
index 103c3a22f6e..cd63827b7d8 100644
--- a/tests/plugin/inline.c
+++ b/tests/plugin/inline.c
@@ -20,8 +20,14 @@ typedef struct {
 uint64_t count_insn_inline;
 uint64_t count_mem;
 uint64_t count_mem_inline;
+uint64_t tb_cond_num_trigger;
+uint64_t tb_cond_track_count;
+uint64_t insn_cond_num_trigger;
+uint64_t insn_cond_track_count;
 } CPUCount;
 
+static const uint64_t cond_trigger_limit = 100;
+
 typedef struct {
 uint64_t data_insn;
 uint64_t data_tb;
@@ -35,6 +41,10 @@ static qemu_plugin_u64 count_insn;
 static qemu_plugin_u64 count_insn_inline;
 static qemu_plugin_u64 count_mem;
 static qemu_plugin_u64 count_mem_inline;
+static qemu_plugin_u64 tb_cond_num_trigger;
+static qemu_plugin_u64 tb_cond_track_count;
+static qemu_plugin_u64 insn_cond_num_trigger;
+static qemu_plugin_u64 insn_cond_track_count;
 static struct qemu_plugin_scoreboard *data;
 static qemu_plugin_u64 data_insn;
 static qemu_plugin_u64 data_tb;
@@ -56,12 +66,19 @@ static void stats_insn(void)
 const uint64_t per_vcpu = qemu_plugin_u64_sum(count_insn);
 const uint64_t inl_per_vcpu =
 qemu_plugin_u64_sum(count_insn_inline);
+const uint64_t cond_num_trigger =
+qemu_plugin_u64_sum(insn_cond_num_trigger);
+const uint64_t cond_track_left = 
qemu_plugin_u64_sum(insn_cond_track_count);
+const uint64_t conditional =
+cond_num_trigger * cond_trigger_limit + cond_track_left;
 printf("insn: %" PRIu64 "\n", expected);
 printf("insn: %" PRIu64 " (per vcpu)\n", per_vcpu);
 printf("insn: %" PRIu64 " (per vcpu inline)\n", inl_per_vcpu);
+printf("insn: %" PRIu64 " (cond cb)\n", conditional);
 g_assert(expected > 0);
 g_assert(per_vcpu == expected);
 g_assert(inl_per_vcpu == expected);
+g_assert(conditional == expected);
 }
 
 static void stats_tb(void)
@@ -70,12 +87,18 @@ static void stats_tb(void)
 const uint64_t per_vcpu = qemu_plugin_u64_sum(count_tb);
 const uint64_t inl_per_vcpu =
 qemu_plugin_u64_sum(count_tb_inline);
+const uint64_t cond_num_trigger = qemu_plugin_u64_sum(tb_cond_num_trigger);
+const uint64_t cond_track_left = qemu_plugin_u64_sum(tb_cond_track_count);
+const uint64_t conditional =
+cond_num_trigger * cond_trigger_limit + cond_track_left;
 printf("tb: %" PRIu64 "\n", expected);
 printf("tb: %" PRIu64 " (per vcpu)\n", per_vcpu);
 printf("tb: %" PRIu64 " (per vcpu inline)\n", inl_per_vcpu);
+printf("tb: %" PRIu64 " (conditional cb)\n", conditional);
 g_assert(expected > 0);
 g_assert(per_vcpu == expected);
 g_assert(inl_per_vcpu == expected);
+g_assert(conditional == expected);
 }
 
 static void stats_mem(void)
@@ -104,14 +127,35 @@ static void plugin_exit(qemu_plugin_id_t id, void *udata)
 const uint64_t insn_inline = qemu_plugin_u64_get(count_insn_inline, i);
 const uint64_t mem = qemu_plugin_u64_get(count_mem, i);
 const uint64_t mem_inline = qemu_plugin_u64_get(count_mem_inline, i);
-printf("cpu %d: tb (%" PRIu64 ", %" PRIu64 ") | "
-   "insn (%" PRIu64 ", %" PRIu64 ") | "
+const uint64_t tb_cond_trigger =
+qemu_plugin_u64_get(tb_cond_num_trigger, i);
+const uint64_t tb_cond_left =
+qemu_plugin_u64_get(tb_cond_track_count, i);
+const uint64_t insn_cond_trigger =
+qemu_plugin_u64_get(insn_cond_num_trigger, i);
+const uint64_t insn_cond_left =
+qemu_plugin_u64_get(insn_cond_track_count, i);
+printf("cpu %d: tb (%" PRIu64 ", %" PRIu64
+   ", %" PRIu64 " * %" PRIu64 " + %" PRIu64
+   ") | "
+   "insn (%" PRIu64 ", %" PRIu64
+   ", %" PRIu64 " * %" PRIu64 " + %" PRIu64
+   ") | "
"mem (%" PRIu64 ", %" PRIu64 ")"
"\n",
-   i, tb, tb_inline, insn, insn_inline, mem, mem_inline);
+   i,
+   tb, tb_inline,
+   tb_cond_trigger, cond_trigger_limit, tb_cond_left,
+   insn, insn_inline,
+   insn_cond_trigger, cond_trigger_limit, insn_cond_left,
+   mem, mem_inline);
 g_assert(tb == t

[PATCH v5 7/9] plugins: distinct types for callbacks

2024-05-02 Thread Pierrick Bouvier
To prevent errors when writing new types of callbacks or inline
operations, we split callbacks data to distinct types.

Reviewed-by: Richard Henderson 
Signed-off-by: Pierrick Bouvier 
---
 include/qemu/plugin.h  | 46 ++---
 plugins/plugin.h   |  2 +-
 accel/tcg/plugin-gen.c | 58 +---
 plugins/core.c | 76 ++
 4 files changed, 98 insertions(+), 84 deletions(-)

diff --git a/include/qemu/plugin.h b/include/qemu/plugin.h
index 0c0aae09e6f..313b7c72684 100644
--- a/include/qemu/plugin.h
+++ b/include/qemu/plugin.h
@@ -74,34 +74,40 @@ enum plugin_dyn_cb_type {
 PLUGIN_CB_INLINE_STORE_U64,
 };
 
+struct qemu_plugin_regular_cb {
+union qemu_plugin_cb_sig f;
+TCGHelperInfo *info;
+void *userp;
+enum qemu_plugin_mem_rw rw;
+};
+
+struct qemu_plugin_inline_cb {
+qemu_plugin_u64 entry;
+enum qemu_plugin_op op;
+uint64_t imm;
+enum qemu_plugin_mem_rw rw;
+};
+
+struct qemu_plugin_conditional_cb {
+union qemu_plugin_cb_sig f;
+TCGHelperInfo *info;
+void *userp;
+qemu_plugin_u64 entry;
+enum qemu_plugin_cond cond;
+uint64_t imm;
+};
+
 /*
  * 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 {
-void *userp;
 enum plugin_dyn_cb_type type;
-/* @rw applies to mem callbacks only (both regular and inline) */
-enum qemu_plugin_mem_rw rw;
-/* fields specific to each dyn_cb type go here */
 union {
-struct {
-union qemu_plugin_cb_sig f;
-TCGHelperInfo *info;
-} regular;
-struct {
-union qemu_plugin_cb_sig f;
-TCGHelperInfo *info;
-qemu_plugin_u64 entry;
-enum qemu_plugin_cond cond;
-uint64_t imm;
-} cond;
-struct {
-qemu_plugin_u64 entry;
-enum qemu_plugin_op op;
-uint64_t imm;
-} inline_insn;
+struct qemu_plugin_regular_cb regular;
+struct qemu_plugin_conditional_cb cond;
+struct qemu_plugin_inline_cb inline_insn;
 };
 };
 
diff --git a/plugins/plugin.h b/plugins/plugin.h
index 7d4b4e21f7c..80d5daa9171 100644
--- a/plugins/plugin.h
+++ b/plugins/plugin.h
@@ -108,7 +108,7 @@ void plugin_register_vcpu_mem_cb(GArray **arr,
  enum qemu_plugin_mem_rw rw,
  void *udata);
 
-void exec_inline_op(struct qemu_plugin_dyn_cb *cb, int cpu_index);
+void exec_inline_op(struct qemu_plugin_inline_cb *cb, int cpu_index);
 
 int plugin_num_vcpus(void);
 
diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c
index 9deddd74c42..b829a959398 100644
--- a/accel/tcg/plugin-gen.c
+++ b/accel/tcg/plugin-gen.c
@@ -101,13 +101,13 @@ static void gen_disable_mem_helper(void)
offsetof(ArchCPU, env));
 }
 
-static void gen_udata_cb(struct qemu_plugin_dyn_cb *cb)
+static void gen_udata_cb(struct qemu_plugin_regular_cb *cb)
 {
 TCGv_i32 cpu_index = tcg_temp_ebb_new_i32();
 
 tcg_gen_ld_i32(cpu_index, tcg_env,
-offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index));
-tcg_gen_call2(cb->regular.f.vcpu_udata, cb->regular.info, NULL,
+tcg_gen_call2(cb->f.vcpu_udata, cb->info, NULL,
   tcgv_i32_temp(cpu_index),
   tcgv_ptr_temp(tcg_constant_ptr(cb->userp)));
 tcg_temp_free_i32(cpu_index);
@@ -153,21 +153,21 @@ static TCGCond plugin_cond_to_tcgcond(enum 
qemu_plugin_cond cond)
 }
 }
 
-static void gen_udata_cond_cb(struct qemu_plugin_dyn_cb *cb)
+static void gen_udata_cond_cb(struct qemu_plugin_conditional_cb *cb)
 {
-TCGv_ptr ptr = gen_plugin_u64_ptr(cb->cond.entry);
+TCGv_ptr ptr = gen_plugin_u64_ptr(cb->entry);
 TCGv_i32 cpu_index = tcg_temp_ebb_new_i32();
 TCGv_i64 val = tcg_temp_ebb_new_i64();
 TCGLabel *after_cb = gen_new_label();
 
 /* Condition should be negated, as calling the cb is the "else" path */
-TCGCond cond = tcg_invert_cond(plugin_cond_to_tcgcond(cb->cond.cond));
+TCGCond cond = tcg_invert_cond(plugin_cond_to_tcgcond(cb->cond));
 
 tcg_gen_ld_i64(val, ptr, 0);
-tcg_gen_brcondi_i64(cond, val, cb->cond.imm, after_cb);
+tcg_gen_brcondi_i64(cond, val, cb->imm, after_cb);
 tcg_gen_ld_i32(cpu_index, tcg_env,
-offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index));
-tcg_gen_call2(cb->cond.f.vcpu_udata, cb->cond.info, NULL,
+tcg_gen_call2(cb->f.vcpu_udata, cb->info, NULL,
   tcgv_i32_temp(cpu_index),
   tcgv_ptr_temp(tcg_constant_ptr(cb->userp)));
 gen_set_label(after_cb);
@@ -177,37 +177,37 @@ static void gen_udata_cond_cb(struct qemu_plugin_d

[PATCH v5 5/9] plugins: conditional callbacks

2024-05-02 Thread Pierrick Bouvier
Extend plugins API to support callback called with a given criteria
(evaluated inline).

Added functions:
- qemu_plugin_register_vcpu_tb_exec_cond_cb
- qemu_plugin_register_vcpu_insn_exec_cond_cb

They expect as parameter a condition, a qemu_plugin_u64_t (op1) and an
immediate (op2). Callback is called if op1 |cond| op2 is true.

Reviewed-by: Richard Henderson 
Signed-off-by: Pierrick Bouvier 
---
 include/qemu/plugin.h|  8 
 include/qemu/qemu-plugin.h   | 76 
 plugins/plugin.h |  8 
 accel/tcg/plugin-gen.c   | 48 +++
 plugins/api.c| 39 ++
 plugins/core.c   | 32 +++
 plugins/qemu-plugins.symbols |  2 +
 7 files changed, 213 insertions(+)

diff --git a/include/qemu/plugin.h b/include/qemu/plugin.h
index 0c5df7fa90a..0c0aae09e6f 100644
--- a/include/qemu/plugin.h
+++ b/include/qemu/plugin.h
@@ -68,6 +68,7 @@ union qemu_plugin_cb_sig {
 
 enum plugin_dyn_cb_type {
 PLUGIN_CB_REGULAR,
+PLUGIN_CB_COND,
 PLUGIN_CB_MEM_REGULAR,
 PLUGIN_CB_INLINE_ADD_U64,
 PLUGIN_CB_INLINE_STORE_U64,
@@ -89,6 +90,13 @@ struct qemu_plugin_dyn_cb {
 union qemu_plugin_cb_sig f;
 TCGHelperInfo *info;
 } regular;
+struct {
+union qemu_plugin_cb_sig f;
+TCGHelperInfo *info;
+qemu_plugin_u64 entry;
+enum qemu_plugin_cond cond;
+uint64_t imm;
+} cond;
 struct {
 qemu_plugin_u64 entry;
 enum qemu_plugin_op op;
diff --git a/include/qemu/qemu-plugin.h b/include/qemu/qemu-plugin.h
index c5cac897a0b..337de25ece7 100644
--- a/include/qemu/qemu-plugin.h
+++ b/include/qemu/qemu-plugin.h
@@ -262,6 +262,29 @@ enum qemu_plugin_mem_rw {
 QEMU_PLUGIN_MEM_RW,
 };
 
+/**
+ * enum qemu_plugin_cond - condition to enable callback
+ *
+ * @QEMU_PLUGIN_COND_NEVER: false
+ * @QEMU_PLUGIN_COND_ALWAYS: true
+ * @QEMU_PLUGIN_COND_EQ: is equal?
+ * @QEMU_PLUGIN_COND_NE: is not equal?
+ * @QEMU_PLUGIN_COND_LT: is less than?
+ * @QEMU_PLUGIN_COND_LE: is less than or equal?
+ * @QEMU_PLUGIN_COND_GT: is greater than?
+ * @QEMU_PLUGIN_COND_GE: is greater than or equal?
+ */
+enum qemu_plugin_cond {
+QEMU_PLUGIN_COND_NEVER,
+QEMU_PLUGIN_COND_ALWAYS,
+QEMU_PLUGIN_COND_EQ,
+QEMU_PLUGIN_COND_NE,
+QEMU_PLUGIN_COND_LT,
+QEMU_PLUGIN_COND_LE,
+QEMU_PLUGIN_COND_GT,
+QEMU_PLUGIN_COND_GE,
+};
+
 /**
  * typedef qemu_plugin_vcpu_tb_trans_cb_t - translation callback
  * @id: unique plugin id
@@ -301,6 +324,32 @@ void qemu_plugin_register_vcpu_tb_exec_cb(struct 
qemu_plugin_tb *tb,
   enum qemu_plugin_cb_flags flags,
   void *userdata);
 
+/**
+ * qemu_plugin_register_vcpu_tb_exec_cond_cb() - register conditional callback
+ * @tb: the opaque qemu_plugin_tb handle for the translation
+ * @cb: callback function
+ * @cond: condition to enable callback
+ * @entry: first operand for condition
+ * @imm: second operand for condition
+ * @flags: does the plugin read or write the CPU's registers?
+ * @userdata: any plugin data to pass to the @cb?
+ *
+ * The @cb function is called when a translated unit executes if
+ * entry @cond imm is true.
+ * If condition is QEMU_PLUGIN_COND_ALWAYS, condition is never interpreted and
+ * this function is equivalent to qemu_plugin_register_vcpu_tb_exec_cb.
+ * If condition QEMU_PLUGIN_COND_NEVER, condition is never interpreted and
+ * callback is never installed.
+ */
+QEMU_PLUGIN_API
+void qemu_plugin_register_vcpu_tb_exec_cond_cb(struct qemu_plugin_tb *tb,
+   qemu_plugin_vcpu_udata_cb_t cb,
+   enum qemu_plugin_cb_flags flags,
+   enum qemu_plugin_cond cond,
+   qemu_plugin_u64 entry,
+   uint64_t imm,
+   void *userdata);
+
 /**
  * enum qemu_plugin_op - describes an inline op
  *
@@ -344,6 +393,33 @@ void qemu_plugin_register_vcpu_insn_exec_cb(struct 
qemu_plugin_insn *insn,
 enum qemu_plugin_cb_flags flags,
 void *userdata);
 
+/**
+ * qemu_plugin_register_vcpu_insn_exec_cond_cb() - conditional insn execution 
cb
+ * @insn: the opaque qemu_plugin_insn handle for an instruction
+ * @cb: callback function
+ * @flags: does the plugin read or write the CPU's registers?
+ * @cond: condition to enable callback
+ * @entry: first operand for condition
+ * @imm: second operand for condition
+ * @userdata: any plugin data to pass to the @cb?
+ *
+ * The @cb function is called when an instruction executes if
+ * entry @cond imm is true.
+ * If condition is QEMU_PLUGIN_COND_ALWAYS, condition is never

[PATCH v5 8/9] plugins: extract cpu_index generate

2024-05-02 Thread Pierrick Bouvier
Reviewed-by: Richard Henderson 
Signed-off-by: Pierrick Bouvier 
---
 accel/tcg/plugin-gen.c | 28 +---
 1 file changed, 13 insertions(+), 15 deletions(-)

diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c
index b829a959398..7b73520e788 100644
--- a/accel/tcg/plugin-gen.c
+++ b/accel/tcg/plugin-gen.c
@@ -101,12 +101,17 @@ static void gen_disable_mem_helper(void)
offsetof(ArchCPU, env));
 }
 
+static TCGv_i32 gen_cpu_index(void)
+{
+TCGv_i32 cpu_index = tcg_temp_ebb_new_i32();
+tcg_gen_ld_i32(cpu_index, tcg_env,
+   -offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index));
+return cpu_index;
+}
+
 static void gen_udata_cb(struct qemu_plugin_regular_cb *cb)
 {
-TCGv_i32 cpu_index = tcg_temp_ebb_new_i32();
-
-tcg_gen_ld_i32(cpu_index, tcg_env,
-   -offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index));
+TCGv_i32 cpu_index = gen_cpu_index();
 tcg_gen_call2(cb->f.vcpu_udata, cb->info, NULL,
   tcgv_i32_temp(cpu_index),
   tcgv_ptr_temp(tcg_constant_ptr(cb->userp)));
@@ -121,9 +126,7 @@ static TCGv_ptr gen_plugin_u64_ptr(qemu_plugin_u64 entry)
 char *base_ptr = arr->data + entry.offset;
 size_t entry_size = g_array_get_element_size(arr);
 
-TCGv_i32 cpu_index = tcg_temp_ebb_new_i32();
-tcg_gen_ld_i32(cpu_index, tcg_env,
-   -offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index));
+TCGv_i32 cpu_index = gen_cpu_index();
 tcg_gen_muli_i32(cpu_index, cpu_index, entry_size);
 tcg_gen_ext_i32_ptr(ptr, cpu_index);
 tcg_temp_free_i32(cpu_index);
@@ -156,7 +159,6 @@ static TCGCond plugin_cond_to_tcgcond(enum qemu_plugin_cond 
cond)
 static void gen_udata_cond_cb(struct qemu_plugin_conditional_cb *cb)
 {
 TCGv_ptr ptr = gen_plugin_u64_ptr(cb->entry);
-TCGv_i32 cpu_index = tcg_temp_ebb_new_i32();
 TCGv_i64 val = tcg_temp_ebb_new_i64();
 TCGLabel *after_cb = gen_new_label();
 
@@ -165,15 +167,14 @@ static void gen_udata_cond_cb(struct 
qemu_plugin_conditional_cb *cb)
 
 tcg_gen_ld_i64(val, ptr, 0);
 tcg_gen_brcondi_i64(cond, val, cb->imm, after_cb);
-tcg_gen_ld_i32(cpu_index, tcg_env,
-   -offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index));
+TCGv_i32 cpu_index = gen_cpu_index();
 tcg_gen_call2(cb->f.vcpu_udata, cb->info, NULL,
   tcgv_i32_temp(cpu_index),
   tcgv_ptr_temp(tcg_constant_ptr(cb->userp)));
+tcg_temp_free_i32(cpu_index);
 gen_set_label(after_cb);
 
 tcg_temp_free_i64(val);
-tcg_temp_free_i32(cpu_index);
 tcg_temp_free_ptr(ptr);
 }
 
@@ -203,10 +204,7 @@ static void gen_inline_store_u64_cb(struct 
qemu_plugin_inline_cb *cb)
 static void gen_mem_cb(struct qemu_plugin_regular_cb *cb,
qemu_plugin_meminfo_t meminfo, TCGv_i64 addr)
 {
-TCGv_i32 cpu_index = tcg_temp_ebb_new_i32();
-
-tcg_gen_ld_i32(cpu_index, tcg_env,
-   -offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index));
+TCGv_i32 cpu_index = gen_cpu_index();
 tcg_gen_call4(cb->f.vcpu_mem, cb->info, NULL,
   tcgv_i32_temp(cpu_index),
   tcgv_i32_temp(tcg_constant_i32(meminfo)),
-- 
2.39.2




[PATCH v5 3/9] plugins: add new inline op STORE_U64

2024-05-02 Thread Pierrick Bouvier
Reviewed-by: Richard Henderson 
Signed-off-by: Pierrick Bouvier 
---
 include/qemu/plugin.h  |  1 +
 include/qemu/qemu-plugin.h |  4 ++--
 accel/tcg/plugin-gen.c | 13 +
 plugins/core.c |  6 ++
 4 files changed, 22 insertions(+), 2 deletions(-)

diff --git a/include/qemu/plugin.h b/include/qemu/plugin.h
index bee1647cfc4..0c5df7fa90a 100644
--- a/include/qemu/plugin.h
+++ b/include/qemu/plugin.h
@@ -70,6 +70,7 @@ enum plugin_dyn_cb_type {
 PLUGIN_CB_REGULAR,
 PLUGIN_CB_MEM_REGULAR,
 PLUGIN_CB_INLINE_ADD_U64,
+PLUGIN_CB_INLINE_STORE_U64,
 };
 
 /*
diff --git a/include/qemu/qemu-plugin.h b/include/qemu/qemu-plugin.h
index 4fc6c3739b2..c5cac897a0b 100644
--- a/include/qemu/qemu-plugin.h
+++ b/include/qemu/qemu-plugin.h
@@ -305,12 +305,12 @@ void qemu_plugin_register_vcpu_tb_exec_cb(struct 
qemu_plugin_tb *tb,
  * enum qemu_plugin_op - describes an inline op
  *
  * @QEMU_PLUGIN_INLINE_ADD_U64: add an immediate value uint64_t
- *
- * Note: currently only a single inline op is supported.
+ * @QEMU_PLUGIN_INLINE_STORE_U64: store an immediate value uint64_t
  */
 
 enum qemu_plugin_op {
 QEMU_PLUGIN_INLINE_ADD_U64,
+QEMU_PLUGIN_INLINE_STORE_U64,
 };
 
 /**
diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c
index 8e2c3ef94f6..a5313cbbb2f 100644
--- a/accel/tcg/plugin-gen.c
+++ b/accel/tcg/plugin-gen.c
@@ -145,6 +145,16 @@ static void gen_inline_add_u64_cb(struct 
qemu_plugin_dyn_cb *cb)
 tcg_temp_free_ptr(ptr);
 }
 
+static void gen_inline_store_u64_cb(struct qemu_plugin_dyn_cb *cb)
+{
+TCGv_ptr ptr = gen_plugin_u64_ptr(cb->inline_insn.entry);
+TCGv_i64 val = tcg_constant_i64(cb->inline_insn.imm);
+
+tcg_gen_st_i64(val, ptr, 0);
+
+tcg_temp_free_ptr(ptr);
+}
+
 static void gen_mem_cb(struct qemu_plugin_dyn_cb *cb,
qemu_plugin_meminfo_t meminfo, TCGv_i64 addr)
 {
@@ -170,6 +180,9 @@ static void inject_cb(struct qemu_plugin_dyn_cb *cb)
 case PLUGIN_CB_INLINE_ADD_U64:
 gen_inline_add_u64_cb(cb);
 break;
+case PLUGIN_CB_INLINE_STORE_U64:
+gen_inline_store_u64_cb(cb);
+break;
 default:
 g_assert_not_reached();
 }
diff --git a/plugins/core.c b/plugins/core.c
index a8557b54ff7..e1bf0dc3717 100644
--- a/plugins/core.c
+++ b/plugins/core.c
@@ -321,6 +321,8 @@ static enum plugin_dyn_cb_type op_to_cb_type(enum 
qemu_plugin_op op)
 switch (op) {
 case QEMU_PLUGIN_INLINE_ADD_U64:
 return PLUGIN_CB_INLINE_ADD_U64;
+case QEMU_PLUGIN_INLINE_STORE_U64:
+return PLUGIN_CB_INLINE_STORE_U64;
 default:
 g_assert_not_reached();
 }
@@ -535,6 +537,9 @@ void exec_inline_op(struct qemu_plugin_dyn_cb *cb, int 
cpu_index)
 case QEMU_PLUGIN_INLINE_ADD_U64:
 *val += cb->inline_insn.imm;
 break;
+case QEMU_PLUGIN_INLINE_STORE_U64:
+*val = cb->inline_insn.imm;
+break;
 default:
 g_assert_not_reached();
 }
@@ -562,6 +567,7 @@ void qemu_plugin_vcpu_mem_cb(CPUState *cpu, uint64_t vaddr,
vaddr, cb->userp);
 break;
 case PLUGIN_CB_INLINE_ADD_U64:
+case PLUGIN_CB_INLINE_STORE_U64:
 exec_inline_op(cb, cpu->cpu_index);
 break;
 default:
-- 
2.39.2




[PATCH v5 1/9] plugins: prepare introduction of new inline ops

2024-05-02 Thread Pierrick Bouvier
Until now, only add_u64 was available, and all functions assumed this or
were named uniquely.

Reviewed-by: Richard Henderson 
Signed-off-by: Pierrick Bouvier 
---
 include/qemu/plugin.h  |  2 +-
 accel/tcg/plugin-gen.c |  6 +++---
 plugins/core.c | 14 --
 3 files changed, 16 insertions(+), 6 deletions(-)

diff --git a/include/qemu/plugin.h b/include/qemu/plugin.h
index 18062528c17..bee1647cfc4 100644
--- a/include/qemu/plugin.h
+++ b/include/qemu/plugin.h
@@ -69,7 +69,7 @@ union qemu_plugin_cb_sig {
 enum plugin_dyn_cb_type {
 PLUGIN_CB_REGULAR,
 PLUGIN_CB_MEM_REGULAR,
-PLUGIN_CB_INLINE,
+PLUGIN_CB_INLINE_ADD_U64,
 };
 
 /*
diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c
index 3db74ae9bfe..8028ae76c3a 100644
--- a/accel/tcg/plugin-gen.c
+++ b/accel/tcg/plugin-gen.c
@@ -113,7 +113,7 @@ static void gen_udata_cb(struct qemu_plugin_dyn_cb *cb)
 tcg_temp_free_i32(cpu_index);
 }
 
-static void gen_inline_cb(struct qemu_plugin_dyn_cb *cb)
+static void gen_inline_add_u64_cb(struct qemu_plugin_dyn_cb *cb)
 {
 GArray *arr = cb->inline_insn.entry.score->data;
 size_t offset = cb->inline_insn.entry.offset;
@@ -158,8 +158,8 @@ static void inject_cb(struct qemu_plugin_dyn_cb *cb)
 case PLUGIN_CB_REGULAR:
 gen_udata_cb(cb);
 break;
-case PLUGIN_CB_INLINE:
-gen_inline_cb(cb);
+case PLUGIN_CB_INLINE_ADD_U64:
+gen_inline_add_u64_cb(cb);
 break;
 default:
 g_assert_not_reached();
diff --git a/plugins/core.c b/plugins/core.c
index 0213513ec65..a8557b54ff7 100644
--- a/plugins/core.c
+++ b/plugins/core.c
@@ -316,6 +316,16 @@ static struct qemu_plugin_dyn_cb *plugin_get_dyn_cb(GArray 
**arr)
 return _array_index(cbs, struct qemu_plugin_dyn_cb, cbs->len - 1);
 }
 
+static enum plugin_dyn_cb_type op_to_cb_type(enum qemu_plugin_op op)
+{
+switch (op) {
+case QEMU_PLUGIN_INLINE_ADD_U64:
+return PLUGIN_CB_INLINE_ADD_U64;
+default:
+g_assert_not_reached();
+}
+}
+
 void plugin_register_inline_op_on_entry(GArray **arr,
 enum qemu_plugin_mem_rw rw,
 enum qemu_plugin_op op,
@@ -326,7 +336,7 @@ void plugin_register_inline_op_on_entry(GArray **arr,
 
 dyn_cb = plugin_get_dyn_cb(arr);
 dyn_cb->userp = NULL;
-dyn_cb->type = PLUGIN_CB_INLINE;
+dyn_cb->type = op_to_cb_type(op);
 dyn_cb->rw = rw;
 dyn_cb->inline_insn.entry = entry;
 dyn_cb->inline_insn.op = op;
@@ -551,7 +561,7 @@ void qemu_plugin_vcpu_mem_cb(CPUState *cpu, uint64_t vaddr,
 cb->regular.f.vcpu_mem(cpu->cpu_index, make_plugin_meminfo(oi, rw),
vaddr, cb->userp);
 break;
-case PLUGIN_CB_INLINE:
+case PLUGIN_CB_INLINE_ADD_U64:
 exec_inline_op(cb, cpu->cpu_index);
 break;
 default:
-- 
2.39.2




[PATCH v5 9/9] plugins: remove op from qemu_plugin_inline_cb

2024-05-02 Thread Pierrick Bouvier
Reviewed-by: Richard Henderson 
Signed-off-by: Pierrick Bouvier 
---
 include/qemu/plugin.h |  1 -
 plugins/plugin.h  |  4 +++-
 plugins/core.c| 13 +++--
 3 files changed, 10 insertions(+), 8 deletions(-)

diff --git a/include/qemu/plugin.h b/include/qemu/plugin.h
index 313b7c72684..1ad8a59209b 100644
--- a/include/qemu/plugin.h
+++ b/include/qemu/plugin.h
@@ -83,7 +83,6 @@ struct qemu_plugin_regular_cb {
 
 struct qemu_plugin_inline_cb {
 qemu_plugin_u64 entry;
-enum qemu_plugin_op op;
 uint64_t imm;
 enum qemu_plugin_mem_rw rw;
 };
diff --git a/plugins/plugin.h b/plugins/plugin.h
index 80d5daa9171..30e2299a54d 100644
--- a/plugins/plugin.h
+++ b/plugins/plugin.h
@@ -108,7 +108,9 @@ void plugin_register_vcpu_mem_cb(GArray **arr,
  enum qemu_plugin_mem_rw rw,
  void *udata);
 
-void exec_inline_op(struct qemu_plugin_inline_cb *cb, int cpu_index);
+void exec_inline_op(enum plugin_dyn_cb_type type,
+struct qemu_plugin_inline_cb *cb,
+int cpu_index);
 
 int plugin_num_vcpus(void);
 
diff --git a/plugins/core.c b/plugins/core.c
index 7ea2ee208db..a9f19e197aa 100644
--- a/plugins/core.c
+++ b/plugins/core.c
@@ -338,7 +338,6 @@ void plugin_register_inline_op_on_entry(GArray **arr,
 
 struct qemu_plugin_inline_cb inline_cb = { .rw = rw,
.entry = entry,
-   .op = op,
.imm = imm };
 dyn_cb = plugin_get_dyn_cb(arr);
 dyn_cb->type = op_to_cb_type(op);
@@ -557,7 +556,9 @@ void qemu_plugin_flush_cb(void)
 plugin_cb__simple(QEMU_PLUGIN_EV_FLUSH);
 }
 
-void exec_inline_op(struct qemu_plugin_inline_cb *cb, int cpu_index)
+void exec_inline_op(enum plugin_dyn_cb_type type,
+struct qemu_plugin_inline_cb *cb,
+int cpu_index)
 {
 char *ptr = cb->entry.score->data->data;
 size_t elem_size = g_array_get_element_size(
@@ -565,11 +566,11 @@ void exec_inline_op(struct qemu_plugin_inline_cb *cb, int 
cpu_index)
 size_t offset = cb->entry.offset;
 uint64_t *val = (uint64_t *)(ptr + offset + cpu_index * elem_size);
 
-switch (cb->op) {
-case QEMU_PLUGIN_INLINE_ADD_U64:
+switch (type) {
+case PLUGIN_CB_INLINE_ADD_U64:
 *val += cb->imm;
 break;
-case QEMU_PLUGIN_INLINE_STORE_U64:
+case PLUGIN_CB_INLINE_STORE_U64:
 *val = cb->imm;
 break;
 default:
@@ -601,7 +602,7 @@ void qemu_plugin_vcpu_mem_cb(CPUState *cpu, uint64_t vaddr,
 case PLUGIN_CB_INLINE_ADD_U64:
 case PLUGIN_CB_INLINE_STORE_U64:
 if (rw && cb->inline_insn.rw) {
-exec_inline_op(>inline_insn, cpu->cpu_index);
+exec_inline_op(cb->type, >inline_insn, cpu->cpu_index);
 }
 break;
 default:
-- 
2.39.2




[PATCH v5 0/9] TCG plugins new inline operations

2024-05-02 Thread Pierrick Bouvier
This series implement two new operations for plugins:
- Store inline allows to write a specific value to a scoreboard.
- Conditional callback executes a callback only when a given condition is true.
  The condition is evaluated inline.

It's possible to mix various inline operations (add, store) with conditional
callbacks, allowing efficient "trap" based counters.

It builds on top of new scoreboard API, introduced in the previous series.

NOTE: Two patches still need review

v2
--

- fixed issue with udata not being passed to conditional callback
- added specific test for this in tests/plugin/inline.c (udata was NULL before).

v3
--

- rebased on top of "plugins: Rewrite plugin code generation":
  20240316015720.3661236-1-richard.hender...@linaro.org
- single pass code generation
- small cleanups for code generation

v4
--

- remove op field from qemu_plugin_inline_cb
- use tcg_constant_i64 to load immediate value to store

v5
--

- rebase on top of master now that Richard's series was merged

Pierrick Bouvier (9):
  plugins: prepare introduction of new inline ops
  plugins: extract generate ptr for qemu_plugin_u64
  plugins: add new inline op STORE_U64
  tests/plugin/inline: add test for STORE_U64 inline op
  plugins: conditional callbacks
  tests/plugin/inline: add test for conditional callback
  plugins: distinct types for callbacks
  plugins: extract cpu_index generate
  plugins: remove op from qemu_plugin_inline_cb

 include/qemu/plugin.h|  42 +++
 include/qemu/qemu-plugin.h   |  80 -
 plugins/plugin.h |  12 +++-
 accel/tcg/plugin-gen.c   | 136 +++
 plugins/api.c|  39 ++
 plugins/core.c   | 109 
 tests/plugin/inline.c| 130 +++--
 plugins/qemu-plugins.symbols |   2 +
 8 files changed, 466 insertions(+), 84 deletions(-)

-- 
2.39.2




Re: [PATCH v3 07/20] plugins: Use emit_before_op for PLUGIN_GEN_FROM_TB

2024-04-29 Thread Pierrick Bouvier
   }
+break;
+
  default:
  g_assert_not_reached();
  }
@@ -807,25 +844,6 @@ static void plugin_gen_inject(struct qemu_plugin_tb 
*plugin_tb)
  enum plugin_gen_cb type = op->args[1];
  
  switch (from) {

-case PLUGIN_GEN_FROM_TB:
-{
-g_assert(insn_idx == -1);
-
-switch (type) {
-case PLUGIN_GEN_CB_UDATA:
-plugin_gen_tb_udata(plugin_tb, op);
-break;
-case PLUGIN_GEN_CB_UDATA_R:
-plugin_gen_tb_udata_r(plugin_tb, op);
-break;
-case PLUGIN_GEN_CB_INLINE:
-plugin_gen_tb_inline(plugin_tb, op);
-break;
-default:
-g_assert_not_reached();
-}
-break;
-}
  case PLUGIN_GEN_FROM_INSN:
  {
  g_assert(insn_idx >= 0);
diff --git a/plugins/api.c b/plugins/api.c
index 8fa5a600ac..5d119e8049 100644
--- a/plugins/api.c
+++ b/plugins/api.c
@@ -92,11 +92,7 @@ void qemu_plugin_register_vcpu_tb_exec_cb(struct 
qemu_plugin_tb *tb,
void *udata)
  {
  if (!tb->mem_only) {
-int index = flags == QEMU_PLUGIN_CB_R_REGS ||
-flags == QEMU_PLUGIN_CB_RW_REGS ?
-PLUGIN_CB_REGULAR_R : PLUGIN_CB_REGULAR;
-
-plugin_register_dyn_cb__udata(>cbs[index],
+plugin_register_dyn_cb__udata(>cbs[PLUGIN_CB_REGULAR],
        cb, flags, udata);
  }
  }


Reviewed-by: Pierrick Bouvier 



Re: [PATCH v3 06/20] plugins: Use emit_before_op for PLUGIN_GEN_AFTER_INSN

2024-04-29 Thread Pierrick Bouvier
 }
-case PLUGIN_GEN_AFTER_INSN:
-{
-g_assert(insn_idx >= 0);
-
-switch (type) {
-case PLUGIN_GEN_DISABLE_MEM_HELPER:
-plugin_gen_disable_mem_helper(plugin_tb, op, insn_idx);
-break;
-default:
-g_assert_not_reached();
-}
-break;
-}
  default:
  g_assert_not_reached();
  }
diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c
index aa6bc6f57d..0f2026c91c 100644
--- a/tcg/tcg-op.c
+++ b/tcg/tcg-op.c
@@ -312,6 +312,11 @@ void tcg_gen_mb(TCGBar mb_type)
  }
  }
  
+void tcg_gen_plugin_cb(unsigned from)

+{
+tcg_gen_op1(INDEX_op_plugin_cb, from);
+}
+
  void tcg_gen_plugin_cb_start(unsigned from, unsigned type, unsigned wr)
  {
  tcg_gen_op3(INDEX_op_plugin_cb_start, from, type, wr);


Reviewed-by: Pierrick Bouvier 



Re: [PATCH 18/24] plugins: Include missing 'qemu/bitmap.h' header

2024-04-26 Thread Pierrick Bouvier

On 4/18/24 12:25, Philippe Mathieu-Daudé wrote:

"qemu/plugin.h" uses DECLARE_BITMAP(), which is
declared in "qemu/bitmap.h".

Signed-off-by: Philippe Mathieu-Daudé 
---
  include/qemu/plugin.h | 1 +
  1 file changed, 1 insertion(+)

diff --git a/include/qemu/plugin.h b/include/qemu/plugin.h
index 12a96cea2a..41db748eda 100644
--- a/include/qemu/plugin.h
+++ b/include/qemu/plugin.h
@@ -13,6 +13,7 @@
  #include "qemu/queue.h"
  #include "qemu/option.h"
  #include "qemu/plugin-event.h"
+#include "qemu/bitmap.h"
  #include "exec/memopidx.h"
  #include "hw/core/cpu.h"
  


Reviewed-by: Pierrick Bouvier 


Re: [PATCH 7/7] target/i386: Implement TCGCPUOps for plugin register reads

2024-04-18 Thread Pierrick Bouvier

On 4/15/24 21:06, Richard Henderson wrote:

Signed-off-by: Richard Henderson 
---
  target/i386/tcg/tcg-cpu.c | 72 ++-
  1 file changed, 56 insertions(+), 16 deletions(-)

diff --git a/target/i386/tcg/tcg-cpu.c b/target/i386/tcg/tcg-cpu.c
index cca19cd40e..2370053df2 100644
--- a/target/i386/tcg/tcg-cpu.c
+++ b/target/i386/tcg/tcg-cpu.c
@@ -22,9 +22,11 @@
  #include "helper-tcg.h"
  #include "qemu/accel.h"
  #include "hw/core/accel-cpu.h"
-
+#include "gdbstub/helpers.h"
+#include "gdb-internal.h"
  #include "tcg-cpu.h"
  
+

  /* Frob eflags into and out of the CPU temporary format.  */
  
  static void x86_cpu_exec_enter(CPUState *cs)

@@ -61,38 +63,74 @@ static void x86_cpu_synchronize_from_tb(CPUState *cs,
  }
  }
  
-static void x86_restore_state_to_opc(CPUState *cs,

- const TranslationBlock *tb,
- const uint64_t *data)
+static uint64_t eip_from_unwind(CPUX86State *env, const TranslationBlock *tb,
+uint64_t data0)
  {
-X86CPU *cpu = X86_CPU(cs);
-CPUX86State *env = >env;
-int cc_op = data[1];
  uint64_t new_pc;
  
  if (tb_cflags(tb) & CF_PCREL) {

  /*
- * data[0] in PC-relative TBs is also a linear address, i.e. an 
address with
- * the CS base added, because it is not guaranteed that EIP bits 12 
and higher
- * stay the same across the translation block.  Add the CS base back 
before
- * replacing the low bits, and subtract it below just like for 
!CF_PCREL.
+ * data[0] in PC-relative TBs is also a linear address,
+ * i.e. an address with the CS base added, because it is
+ * not guaranteed that EIP bits 12 and higher stay the
+ * same across the translation block.  Add the CS base
+ * back before replacing the low bits, and subtract it
+ * below just like for !CF_PCREL.
   */
  uint64_t pc = env->eip + tb->cs_base;
-new_pc = (pc & TARGET_PAGE_MASK) | data[0];
+new_pc = (pc & TARGET_PAGE_MASK) | data0;
  } else {
-new_pc = data[0];
+new_pc = data0;
  }
  if (tb->flags & HF_CS64_MASK) {
-env->eip = new_pc;
-} else {
-env->eip = (uint32_t)(new_pc - tb->cs_base);
+return new_pc;
  }
+return (uint32_t)(new_pc - tb->cs_base);
+}
  
+static void x86_restore_state_to_opc(CPUState *cs,

+ const TranslationBlock *tb,
+ const uint64_t *data)
+{
+CPUX86State *env = cpu_env(cs);
+CCOp cc_op;
+
+env->eip = eip_from_unwind(env, tb, data[0]);
+
+cc_op = data[1];
  if (cc_op != CC_OP_DYNAMIC) {
  env->cc_op = cc_op;
  }
  }
  
+static bool x86_plugin_need_unwind_for_reg(CPUState *cs, int reg)

+{
+return reg == IDX_IP_REG || reg == IDX_FLAGS_REG;
+}
+
+static int x86_plugin_unwind_read_reg(CPUState *cs, GByteArray *buf, int reg,
+  const TranslationBlock *tb,
+  const uint64_t *data)
+{
+CPUX86State *env = cpu_env(cs);
+CCOp cc_op;
+
+switch (reg) {
+case IDX_IP_REG:
+return gdb_get_regl(buf, eip_from_unwind(env, tb, data[0]));
+
+case IDX_FLAGS_REG:
+cc_op = data[1];
+if (cc_op == CC_OP_DYNAMIC) {
+cc_op = env->cc_op;
+}
+return gdb_get_reg32(buf, cpu_compute_eflags_ccop(env, cc_op));
+
+default:
+g_assert_not_reached();
+}
+}
+
  #ifndef CONFIG_USER_ONLY
  static bool x86_debug_check_breakpoint(CPUState *cs)
  {
@@ -110,6 +148,8 @@ static const TCGCPUOps x86_tcg_ops = {
  .initialize = tcg_x86_init,
  .synchronize_from_tb = x86_cpu_synchronize_from_tb,
  .restore_state_to_opc = x86_restore_state_to_opc,
+.plugin_need_unwind_for_reg = x86_plugin_need_unwind_for_reg,
+.plugin_unwind_read_reg = x86_plugin_unwind_read_reg,
  .cpu_exec_enter = x86_cpu_exec_enter,
  .cpu_exec_exit = x86_cpu_exec_exit,
  #ifdef CONFIG_USER_ONLY


Reviewed-by: Pierrick Bouvier 



Re: [PATCH 6/7] target/i386: Introduce cpu_compute_eflags_ccop

2024-04-18 Thread Pierrick Bouvier

On 4/15/24 21:06, Richard Henderson wrote:

This is a generalization of cpu_compute_eflags, with a dynamic
value of cc_op, and is thus tcg specific.

Signed-off-by: Richard Henderson 
---
  target/i386/cpu.h   |  2 ++
  target/i386/tcg/cc_helper.c | 10 ++
  2 files changed, 12 insertions(+)

diff --git a/target/i386/cpu.h b/target/i386/cpu.h
index 6b05738079..285f26d99d 100644
--- a/target/i386/cpu.h
+++ b/target/i386/cpu.h
@@ -2379,6 +2379,8 @@ void cpu_x86_inject_mce(Monitor *mon, X86CPU *cpu, int 
bank,
  
  uint32_t cpu_cc_compute_all(CPUX86State *env1);
  
+uint32_t cpu_compute_eflags_ccop(CPUX86State *env, CCOp op);

+
  static inline uint32_t cpu_compute_eflags(CPUX86State *env)
  {
  uint32_t eflags = env->eflags;
diff --git a/target/i386/tcg/cc_helper.c b/target/i386/tcg/cc_helper.c
index f76e9cb8cf..8203682ca8 100644
--- a/target/i386/tcg/cc_helper.c
+++ b/target/i386/tcg/cc_helper.c
@@ -225,6 +225,16 @@ uint32_t cpu_cc_compute_all(CPUX86State *env)
  return helper_cc_compute_all(CC_DST, CC_SRC, CC_SRC2, CC_OP);
  }
  
+uint32_t cpu_compute_eflags_ccop(CPUX86State *env, CCOp op)

+{
+uint32_t eflags;
+
+eflags = helper_cc_compute_all(CC_DST, CC_SRC, CC_SRC2, op);
+eflags |= env->df & DF_MASK;
+eflags |= env->eflags & ~(VM_MASK | RF_MASK);
+return eflags;
+}
+
  target_ulong helper_cc_compute_c(target_ulong dst, target_ulong src1,
   target_ulong src2, int op)
  {


Reviewed-by: Pierrick Bouvier 



Re: [PATCH 5/7] target/i386: Split out gdb-internal.h

2024-04-18 Thread Pierrick Bouvier

On 4/15/24 21:06, Richard Henderson wrote:

Signed-off-by: Richard Henderson 
---
  target/i386/gdb-internal.h | 65 ++
  target/i386/gdbstub.c  |  1 +
  2 files changed, 66 insertions(+)
  create mode 100644 target/i386/gdb-internal.h

diff --git a/target/i386/gdb-internal.h b/target/i386/gdb-internal.h
new file mode 100644
index 00..7cf4c1a656
--- /dev/null
+++ b/target/i386/gdb-internal.h
@@ -0,0 +1,65 @@
+/*
+ * x86 gdb server stub
+ *
+ * Copyright (c) 2003-2005 Fabrice Bellard
+ * Copyright (c) 2013 SUSE LINUX Products GmbH
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef I386_GDB_INTERNAL_H
+#define I386_GDB_INTERNAL_H
+
+/*
+ * Keep these in sync with assignment to
+ * gdb_num_core_regs in target/i386/cpu.c
+ * and with the machine description
+ */
+
+/*
+ * SEG: 6 segments, plus fs_base, gs_base, kernel_gs_base
+ */
+
+/*
+ * general regs ->  8 or 16
+ */
+#define IDX_NB_IP   1
+#define IDX_NB_FLAGS1
+#define IDX_NB_SEG  (6 + 3)
+#define IDX_NB_CTL  6
+#define IDX_NB_FP   16
+/*
+ * fpu regs --> 8 or 16
+ */
+#define IDX_NB_MXCSR1
+/*
+ *  total > 8+1+1+9+6+16+8+1=50 or 16+1+1+9+6+16+16+1=66
+ */
+
+#define IDX_IP_REG  CPU_NB_REGS
+#define IDX_FLAGS_REG   (IDX_IP_REG + IDX_NB_IP)
+#define IDX_SEG_REGS(IDX_FLAGS_REG + IDX_NB_FLAGS)
+#define IDX_CTL_REGS(IDX_SEG_REGS + IDX_NB_SEG)
+#define IDX_FP_REGS (IDX_CTL_REGS + IDX_NB_CTL)
+#define IDX_XMM_REGS(IDX_FP_REGS + IDX_NB_FP)
+#define IDX_MXCSR_REG   (IDX_XMM_REGS + CPU_NB_REGS)
+
+#define IDX_CTL_CR0_REG (IDX_CTL_REGS + 0)
+#define IDX_CTL_CR2_REG (IDX_CTL_REGS + 1)
+#define IDX_CTL_CR3_REG (IDX_CTL_REGS + 2)
+#define IDX_CTL_CR4_REG (IDX_CTL_REGS + 3)
+#define IDX_CTL_CR8_REG (IDX_CTL_REGS + 4)
+#define IDX_CTL_EFER_REG(IDX_CTL_REGS + 5)
+
+#endif
diff --git a/target/i386/gdbstub.c b/target/i386/gdbstub.c
index ebb000df6a..9662509b82 100644
--- a/target/i386/gdbstub.c
+++ b/target/i386/gdbstub.c
@@ -20,6 +20,7 @@
  #include "qemu/osdep.h"
  #include "cpu.h"
  #include "include/gdbstub/helpers.h"
+#include "gdb-internal.h"
  
  #ifdef TARGET_X86_64

  static const int gpr_map[16] = {


Reviewed-by: Pierrick Bouvier 



Re: [PATCH 4/7] plugins: Introduce TCGCPUOps callbacks for mid-tb register reads

2024-04-18 Thread Pierrick Bouvier

On 4/15/24 21:06, Richard Henderson wrote:

Certain target registers are not updated continuously within
the translation block.  For normal exception handling we use
unwind info to re-generate the correct value when required.
Leverage that same info for reading those registers for plugins.

All targets will need updating for these new callbacks.

Signed-off-by: Richard Henderson 
---
  include/hw/core/tcg-cpu-ops.h | 13 +
  plugins/api.c | 36 +--
  2 files changed, 47 insertions(+), 2 deletions(-)

diff --git a/include/hw/core/tcg-cpu-ops.h b/include/hw/core/tcg-cpu-ops.h
index bf8ff8e3ee..e954d83edf 100644
--- a/include/hw/core/tcg-cpu-ops.h
+++ b/include/hw/core/tcg-cpu-ops.h
@@ -49,6 +49,19 @@ struct TCGCPUOps {
  /** @debug_excp_handler: Callback for handling debug exceptions */
  void (*debug_excp_handler)(CPUState *cpu);
  
+/**

+ * @plugin_need_unwind_for_reg:
+ * True if unwind info needed for reading reg.
+ */
+bool (*plugin_need_unwind_for_reg)(CPUState *cpu, int reg);
+/**
+ * @plugin_unwind_read_reg:
+ * Like CPUClass.gdb_read_register, but for registers that require
+ * regeneration using unwind info, like in @restore_state_to_opc.
+ */
+int (*plugin_unwind_read_reg)(CPUState *cpu, GByteArray *buf, int reg,
+  const TranslationBlock *tb,
+  const uint64_t *data);
  #ifdef NEED_CPU_H
  #ifdef CONFIG_USER_ONLY
  /**
diff --git a/plugins/api.c b/plugins/api.c
index 3912c9cc8f..3543647a89 100644
--- a/plugins/api.c
+++ b/plugins/api.c
@@ -40,10 +40,12 @@
  #include "qemu/plugin.h"
  #include "qemu/log.h"
  #include "tcg/tcg.h"
+#include "tcg/insn-start-words.h"
  #include "exec/exec-all.h"
  #include "exec/gdbstub.h"
  #include "exec/ram_addr.h"
  #include "disas/disas.h"
+#include "hw/core/tcg-cpu-ops.h"
  #include "plugin.h"
  #ifndef CONFIG_USER_ONLY
  #include "qemu/plugin-memory.h"
@@ -454,9 +456,39 @@ GArray *qemu_plugin_get_registers(void)
  
  int qemu_plugin_read_register(struct qemu_plugin_register *reg, GByteArray *buf)

  {
-g_assert(current_cpu);
+CPUState *cs;
+uintptr_t ra;
+int regno;
  
-return gdb_read_register(current_cpu, buf, GPOINTER_TO_INT(reg));

+assert(current_cpu);
+cs = current_cpu;
+ra = cs->neg.plugin_ra;
+regno = GPOINTER_TO_INT(reg);
+
+/*
+ * When plugin_ra is 0, we have no unwind info.  This will be true for
+ * TB callbacks that happen before any insns of the TB have started.
+ */
+if (ra) {
+const TCGCPUOps *tcg_ops = cs->cc->tcg_ops;
+
+/*
+ * For plugins in the middle of the TB, we may need to locate
+ * and use unwind data to reconstruct a register value.
+ * Usually this required for the PC, but there may be others.
+ */
+if (tcg_ops->plugin_need_unwind_for_reg &&
+tcg_ops->plugin_need_unwind_for_reg(cs, regno)) {
+uint64_t data[TARGET_INSN_START_WORDS];
+const TranslationBlock *tb;
+
+tb = cpu_unwind_state_data(cs, ra, data);
+assert(tb);
+return tcg_ops->plugin_unwind_read_reg(cs, buf, regno, tb, data);
+}
+}
+
+return gdb_read_register(cs, buf, regno);
  }
  
  struct qemu_plugin_scoreboard *qemu_plugin_scoreboard_new(size_t element_size)


Reviewed-by: Pierrick Bouvier 



Re: [PATCH 3/7] accel/tcg: Return the TranslationBlock from cpu_unwind_state_data

2024-04-18 Thread Pierrick Bouvier

On 4/15/24 21:06, Richard Henderson wrote:

Fix the i386 get_memio_eip function to use tb->cflags
instead of cs->tcg_cflags.

Signed-off-by: Richard Henderson 
---
  include/exec/cpu-common.h | 9 +
  accel/tcg/translate-all.c | 9 +
  target/i386/helper.c  | 6 --
  3 files changed, 14 insertions(+), 10 deletions(-)

diff --git a/include/exec/cpu-common.h b/include/exec/cpu-common.h
index 6346df17ce..f056132cab 100644
--- a/include/exec/cpu-common.h
+++ b/include/exec/cpu-common.h
@@ -176,12 +176,13 @@ void list_cpus(void);
   * @host_pc: the host pc within the translation
   * @data: output data
   *
- * Attempt to load the the unwind state for a host pc occurring in
- * translated code.  If @host_pc is not in translated code, the
- * function returns false; otherwise @data is loaded.
+ * Attempt to load the the unwind state for a host pc occurring in translated
+ * code.  If @host_pc is not in translated code, the function returns NULL;
+ * otherwise @data is loaded and the TranslationBlock is returned.
   * This is the same unwind info as given to restore_state_to_opc.
   */
-bool cpu_unwind_state_data(CPUState *cpu, uintptr_t host_pc, uint64_t *data);
+const TranslationBlock *cpu_unwind_state_data(CPUState *cpu, uintptr_t host_pc,
+  uint64_t *data);
  
  /**

   * cpu_restore_state:
diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c
index 83cc14fbde..c745bc5b6c 100644
--- a/accel/tcg/translate-all.c
+++ b/accel/tcg/translate-all.c
@@ -243,15 +243,16 @@ bool cpu_restore_state(CPUState *cpu, uintptr_t host_pc)
  return false;
  }
  
-bool cpu_unwind_state_data(CPUState *cpu, uintptr_t host_pc, uint64_t *data)

+const TranslationBlock *
+cpu_unwind_state_data(CPUState *cpu, uintptr_t host_pc, uint64_t *data)
  {
  if (in_code_gen_buffer((const void *)(host_pc - tcg_splitwx_diff))) {
  TranslationBlock *tb = tcg_tb_lookup(host_pc);
-if (tb) {
-return cpu_unwind_data_from_tb(tb, host_pc, data) >= 0;
+if (tb && cpu_unwind_data_from_tb(tb, host_pc, data) >= 0) {
+return tb;
  }
  }
-return false;
+return NULL;
  }
  
  void page_init(void)

diff --git a/target/i386/helper.c b/target/i386/helper.c
index 23ccb23a5b..eaa691a851 100644
--- a/target/i386/helper.c
+++ b/target/i386/helper.c
@@ -517,13 +517,15 @@ static inline target_ulong get_memio_eip(CPUX86State *env)
  #ifdef CONFIG_TCG
  uint64_t data[TARGET_INSN_START_WORDS];
  CPUState *cs = env_cpu(env);
+const TranslationBlock *tb;
  
-if (!cpu_unwind_state_data(cs, cs->mem_io_pc, data)) {

+tb = cpu_unwind_state_data(cs, cs->mem_io_pc, data);
+if (!tb) {
  return env->eip;
  }
  
  /* Per x86_restore_state_to_opc. */

-if (cs->tcg_cflags & CF_PCREL) {
+if (tb->cflags & CF_PCREL) {
  return (env->eip & TARGET_PAGE_MASK) | data[0];
  } else {
  return data[0] - env->segs[R_CS].base;


Reviewed-by: Pierrick Bouvier 



Re: [PATCH 2/7] accel/tcg: Set CPUState.plugin_ra before all plugin callbacks

2024-04-18 Thread Pierrick Bouvier

On 4/15/24 21:06, Richard Henderson wrote:

Signed-off-by: Richard Henderson 
---
  include/hw/core/cpu.h  |  1 +
  accel/tcg/plugin-gen.c | 50 +-
  2 files changed, 46 insertions(+), 5 deletions(-)

diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h
index 10cd492aff..f4af37c13d 100644
--- a/include/hw/core/cpu.h
+++ b/include/hw/core/cpu.h
@@ -350,6 +350,7 @@ typedef union IcountDecr {
  typedef struct CPUNegativeOffsetState {
  CPUTLB tlb;
  #ifdef CONFIG_PLUGIN
+uintptr_t plugin_ra;
  GArray *plugin_mem_cbs;
  #endif
  IcountDecr icount_decr;
diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c
index 36e9134a5d..f96b49cce6 100644
--- a/accel/tcg/plugin-gen.c
+++ b/accel/tcg/plugin-gen.c
@@ -37,6 +37,12 @@ enum plugin_gen_from {
  PLUGIN_GEN_AFTER_TB,
  };
  
+enum plugin_gen_ra {

+GEN_RA_DONE,
+GEN_RA_FROM_TB,
+GEN_RA_FROM_INSN,
+};
+
  /* called before finishing a TB with exit_tb, goto_tb or goto_ptr */
  void plugin_gen_disable_mem_helpers(void)
  {
@@ -151,11 +157,38 @@ static void gen_mem_cb(struct qemu_plugin_dyn_cb *cb,
  tcg_temp_free_i32(cpu_index);
  }
  
-static void inject_cb(struct qemu_plugin_dyn_cb *cb)

+static void inject_ra(enum plugin_gen_ra *gen_ra)
+{
+TCGv_ptr ra;
+
+switch (*gen_ra) {
+case GEN_RA_DONE:
+return;
+case GEN_RA_FROM_TB:
+ra = tcg_constant_ptr(NULL);
+break;
+case GEN_RA_FROM_INSN:
+ra = tcg_temp_ebb_new_ptr();
+tcg_gen_plugin_pc(ra);
+break;
+default:
+g_assert_not_reached();
+}
+
+tcg_gen_st_ptr(ra, tcg_env,
+   offsetof(CPUState, neg.plugin_ra) -
+   offsetof(ArchCPU, env));
+tcg_temp_free_ptr(ra);
+*gen_ra = GEN_RA_DONE;
+}
+
+static void inject_cb(struct qemu_plugin_dyn_cb *cb,
+  enum plugin_gen_ra *gen_ra)
  
  {

  switch (cb->type) {
  case PLUGIN_CB_REGULAR:
+inject_ra(gen_ra);
  gen_udata_cb(cb);
  break;
  case PLUGIN_CB_INLINE:
@@ -167,16 +200,18 @@ static void inject_cb(struct qemu_plugin_dyn_cb *cb)
  }
  
  static void inject_mem_cb(struct qemu_plugin_dyn_cb *cb,

+  enum plugin_gen_ra *gen_ra,
enum qemu_plugin_mem_rw rw,
qemu_plugin_meminfo_t meminfo, TCGv_i64 addr)
  {
  if (cb->rw & rw) {
  switch (cb->type) {
  case PLUGIN_CB_MEM_REGULAR:
+inject_ra(gen_ra);
  gen_mem_cb(cb, meminfo, addr);
  break;
  default:
-inject_cb(cb);
+inject_cb(cb, gen_ra);
  break;
  }
  }
@@ -186,6 +221,7 @@ static void plugin_gen_inject(struct qemu_plugin_tb 
*plugin_tb)
  {
  TCGOp *op, *next;
  int insn_idx = -1;
+enum plugin_gen_ra gen_ra;
  
  if (unlikely(qemu_loglevel_mask(LOG_TB_OP_PLUGIN)

   && qemu_log_in_addr_range(plugin_tb->vaddr))) {
@@ -205,10 +241,12 @@ static void plugin_gen_inject(struct qemu_plugin_tb 
*plugin_tb)
   */
  memset(tcg_ctx->free_temps, 0, sizeof(tcg_ctx->free_temps));
  
+gen_ra = GEN_RA_FROM_TB;

  QTAILQ_FOREACH_SAFE(op, _ctx->ops, link, next) {
  switch (op->opc) {
  case INDEX_op_insn_start:
  insn_idx++;
+gen_ra = GEN_RA_FROM_INSN;
  break;
  
  case INDEX_op_plugin_cb:

@@ -244,7 +282,8 @@ static void plugin_gen_inject(struct qemu_plugin_tb 
*plugin_tb)
  cbs = plugin_tb->cbs;
  for (i = 0, n = (cbs ? cbs->len : 0); i < n; i++) {
  inject_cb(
-_array_index(cbs, struct qemu_plugin_dyn_cb, i));
+_array_index(cbs, struct qemu_plugin_dyn_cb, i),
+_ra);
  }
  break;
  
@@ -256,7 +295,8 @@ static void plugin_gen_inject(struct qemu_plugin_tb *plugin_tb)

  cbs = insn->insn_cbs;
  for (i = 0, n = (cbs ? cbs->len : 0); i < n; i++) {
  inject_cb(
-_array_index(cbs, struct qemu_plugin_dyn_cb, i));
+_array_index(cbs, struct qemu_plugin_dyn_cb, i),
+_ra);
  }
  break;
  
@@ -288,7 +328,7 @@ static void plugin_gen_inject(struct qemu_plugin_tb *plugin_tb)

  cbs = insn->mem_cbs;
  for (i = 0, n = (cbs ? cbs->len : 0); i < n; i++) {
  inject_mem_cb(_array_index(cbs, struct qemu_plugin_dyn_cb, 
i),
-  rw, meminfo, addr);
+  _ra, rw, meminfo, addr);
  }
  
      tcg_ctx->emit_before_op = NULL;


Reviewed-by: Pierrick Bouvier 



Re: [PATCH 1/7] tcg: Introduce INDEX_op_plugin_pc

2024-04-18 Thread Pierrick Bouvier

On 4/15/24 21:06, Richard Henderson wrote:

Add an opcode to find a code address within the current insn,
for later use with unwinding.  Generate the code generically
using tcg_reg_alloc_do_movi.

Signed-off-by: Richard Henderson 
---
  include/tcg/tcg-op-common.h |  1 +
  include/tcg/tcg-opc.h   |  1 +
  tcg/tcg-op.c|  5 +
  tcg/tcg.c   | 10 ++
  4 files changed, 17 insertions(+)

diff --git a/include/tcg/tcg-op-common.h b/include/tcg/tcg-op-common.h
index 009e2778c5..a32c88a182 100644
--- a/include/tcg/tcg-op-common.h
+++ b/include/tcg/tcg-op-common.h
@@ -76,6 +76,7 @@ void tcg_gen_lookup_and_goto_ptr(void);
  
  void tcg_gen_plugin_cb(unsigned from);

  void tcg_gen_plugin_mem_cb(TCGv_i64 addr, unsigned meminfo);
+void tcg_gen_plugin_pc(TCGv_ptr);
  
  /* 32 bit ops */
  
diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h

index 546eb49c11..087d1b82da 100644
--- a/include/tcg/tcg-opc.h
+++ b/include/tcg/tcg-opc.h
@@ -199,6 +199,7 @@ DEF(goto_ptr, 0, 1, 0, TCG_OPF_BB_EXIT | TCG_OPF_BB_END)
  
  DEF(plugin_cb, 0, 0, 1, TCG_OPF_NOT_PRESENT)

  DEF(plugin_mem_cb, 0, 1, 1, TCG_OPF_NOT_PRESENT)
+DEF(plugin_pc, 1, 0, 0, TCG_OPF_NOT_PRESENT)
  
  /* Replicate ld/st ops for 32 and 64-bit guest addresses. */

  DEF(qemu_ld_a32_i32, 1, 1, 1,
diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c
index eff3728622..b8ca78cbe4 100644
--- a/tcg/tcg-op.c
+++ b/tcg/tcg-op.c
@@ -322,6 +322,11 @@ void tcg_gen_plugin_mem_cb(TCGv_i64 addr, unsigned meminfo)
  tcg_gen_op2(INDEX_op_plugin_mem_cb, tcgv_i64_arg(addr), meminfo);
  }
  
+void tcg_gen_plugin_pc(TCGv_ptr arg)

+{
+tcg_gen_op1(INDEX_op_plugin_pc, tcgv_ptr_arg(arg));
+}
+
  /* 32 bit ops */
  
  void tcg_gen_discard_i32(TCGv_i32 arg)

diff --git a/tcg/tcg.c b/tcg/tcg.c
index d248c52e96..42e2b53729 100644
--- a/tcg/tcg.c
+++ b/tcg/tcg.c
@@ -4701,6 +4701,13 @@ static void tcg_reg_alloc_mov(TCGContext *s, const TCGOp 
*op)
  }
  }
  
+static void tcg_reg_alloc_plugin_pc(TCGContext *s, const TCGOp *op)

+{
+tcg_reg_alloc_do_movi(s, arg_temp(op->args[0]),
+  (uintptr_t)tcg_splitwx_to_rx(s->code_ptr),
+  op->life, output_pref(op, 0));
+}
+
  /*
   * Specialized code generation for INDEX_op_dup_vec.
   */
@@ -6208,6 +6215,9 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb, 
uint64_t pc_start)
  case INDEX_op_mov_vec:
  tcg_reg_alloc_mov(s, op);
  break;
+case INDEX_op_plugin_pc:
+tcg_reg_alloc_plugin_pc(s, op);
+break;
  case INDEX_op_dup_vec:
  tcg_reg_alloc_dup(s, op);
  break;


Reviewed-by: Pierrick Bouvier 



Re: [PATCH 0/7] plugins: Use unwind info for special gdb registers

2024-04-17 Thread Pierrick Bouvier

On 4/16/24 19:40, Richard Henderson wrote:

On 4/16/24 17:35, Pierrick Bouvier wrote:

On 4/15/24 21:06, Richard Henderson wrote:

Based-on: 20240404230611.21231-1-richard.hender...@linaro.org
("[PATCH v2 00/21] Rewrite plugin code generation")

This is an attempt to fix
https://gitlab.com/qemu-project/qemu/-/issues/2208
("PC is not updated for each instruction in TCG plugins")

I have only updated target/i386 so far, but basically all targets
need updating for the new callbacks.  Extra points to anyone who
sees how to avoid the extra code duplication.  :-)



Thanks for the series Richard. It looks good to me.

Besides reviewing individual commits, I have a more general question.
  From some discussions we had, it seems like that previously gdb stub was 
correctly
updating all register values, and that it has been dropped at some point.


Normally gdbstub does not run in the middle of a TB -- we end normally 
(single-step,
breakpoint) or raise an exception (watchpoint).  Only then, after TCG state has 
been made
consistent, does gdbstub have access to the CPUState.



That makes sense.
 >

Was it for performance reasons, or an architectural change in QEMU?
Is gdb stub the right way to poke register values for plugins?

I don't know exactly why some registers are not updated correctly in this 
context, but it
seems like we are trying to fix this afterward, instead of identifying root 
cause.


The one or two registers are not updated continuously for performance reasons.  
And
because they are not updated during initial code generation, it's not easy to 
do so later
with plugin injection.  But recovering that data is what the unwind info is for 
-- a bit
expensive to access that way, but overall less, with the expectation that it is 
rare.



Thanks for the description, I understand better the approach you picked 
for that issue.




r~


Re: [PATCH 0/7] plugins: Use unwind info for special gdb registers

2024-04-16 Thread Pierrick Bouvier

On 4/15/24 21:06, Richard Henderson wrote:

Based-on: 20240404230611.21231-1-richard.hender...@linaro.org
("[PATCH v2 00/21] Rewrite plugin code generation")

This is an attempt to fix
https://gitlab.com/qemu-project/qemu/-/issues/2208
("PC is not updated for each instruction in TCG plugins")

I have only updated target/i386 so far, but basically all targets
need updating for the new callbacks.  Extra points to anyone who
sees how to avoid the extra code duplication.  :-)



Thanks for the series Richard. It looks good to me.

Besides reviewing individual commits, I have a more general question.
From some discussions we had, it seems like that previously gdb stub 
was correctly updating all register values, and that it has been dropped 
at some point.


Was it for performance reasons, or an architectural change in QEMU?
Is gdb stub the right way to poke register values for plugins?

I don't know exactly why some registers are not updated correctly in 
this context, but it seems like we are trying to fix this afterward, 
instead of identifying root cause.


Sorry if my question is irrelevant, I'm trying to understand the full 
context here.


Thanks,
Pierrick



r~


Richard Henderson (7):
   tcg: Introduce INDEX_op_plugin_pc
   accel/tcg: Set CPUState.plugin_ra before all plugin callbacks
   accel/tcg: Return the TranslationBlock from cpu_unwind_state_data
   plugins: Introduce TCGCPUOps callbacks for mid-tb register reads
   target/i386: Split out gdb-internal.h
   target/i386: Introduce cpu_compute_eflags_ccop
   target/i386: Implement TCGCPUOps for plugin register reads

  include/exec/cpu-common.h |  9 +++--
  include/hw/core/cpu.h |  1 +
  include/hw/core/tcg-cpu-ops.h | 13 +++
  include/tcg/tcg-op-common.h   |  1 +
  include/tcg/tcg-opc.h |  1 +
  target/i386/cpu.h |  2 +
  target/i386/gdb-internal.h| 65 +++
  accel/tcg/plugin-gen.c| 50 +---
  accel/tcg/translate-all.c |  9 +++--
  plugins/api.c | 36 +-
  target/i386/gdbstub.c |  1 +
  target/i386/helper.c  |  6 ++-
  target/i386/tcg/cc_helper.c   | 10 +
  target/i386/tcg/tcg-cpu.c | 72 +++
  tcg/tcg-op.c  |  5 +++
  tcg/tcg.c | 10 +
  16 files changed, 258 insertions(+), 33 deletions(-)
  create mode 100644 target/i386/gdb-internal.h





Re: [PATCH 14/19] plugins: fix -Werror=maybe-uninitialized false-positive

2024-03-28 Thread Pierrick Bouvier

On 3/28/24 14:20, marcandre.lur...@redhat.com wrote:

From: Marc-André Lureau 

../plugins/loader.c:405:15: error: ‘ctx’ may be used uninitialized 
[-Werror=maybe-uninitialized]

Signed-off-by: Marc-André Lureau 
---
  plugins/loader.c | 2 +-
  1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/plugins/loader.c b/plugins/loader.c
index 9768b78eb6..513a429c57 100644
--- a/plugins/loader.c
+++ b/plugins/loader.c
@@ -390,7 +390,7 @@ void plugin_reset_uninstall(qemu_plugin_id_t id,
  bool reset)
  {
  struct qemu_plugin_reset_data *data;
-struct qemu_plugin_ctx *ctx;
+struct qemu_plugin_ctx *ctx = NULL;
  
  WITH_QEMU_LOCK_GUARD() {

  ctx = plugin_id_to_ctx_locked(id);


Reviewed-by: Pierrick Bouvier 


Re: [PATCH v3] contrib/plugins/execlog: Fix compiler warning

2024-03-26 Thread Pierrick Bouvier



On 3/26/24 13:54, Philippe Mathieu-Daudé wrote:


I will post a small PR later today, so until Peter has something
else planned, I can take it, since the patch LGTM now.



Thanks Philippe :)


[PATCH v4 6/9] tests/plugin/inline: add test for conditional callback

2024-03-26 Thread Pierrick Bouvier
Count number of tb and insn executed using a conditional callback. We
ensure the callback has been called expected number of time (per vcpu).

Signed-off-by: Pierrick Bouvier 
---
 tests/plugin/inline.c | 89 +--
 1 file changed, 86 insertions(+), 3 deletions(-)

diff --git a/tests/plugin/inline.c b/tests/plugin/inline.c
index 103c3a22f6e..cd63827b7d8 100644
--- a/tests/plugin/inline.c
+++ b/tests/plugin/inline.c
@@ -20,8 +20,14 @@ typedef struct {
 uint64_t count_insn_inline;
 uint64_t count_mem;
 uint64_t count_mem_inline;
+uint64_t tb_cond_num_trigger;
+uint64_t tb_cond_track_count;
+uint64_t insn_cond_num_trigger;
+uint64_t insn_cond_track_count;
 } CPUCount;
 
+static const uint64_t cond_trigger_limit = 100;
+
 typedef struct {
 uint64_t data_insn;
 uint64_t data_tb;
@@ -35,6 +41,10 @@ static qemu_plugin_u64 count_insn;
 static qemu_plugin_u64 count_insn_inline;
 static qemu_plugin_u64 count_mem;
 static qemu_plugin_u64 count_mem_inline;
+static qemu_plugin_u64 tb_cond_num_trigger;
+static qemu_plugin_u64 tb_cond_track_count;
+static qemu_plugin_u64 insn_cond_num_trigger;
+static qemu_plugin_u64 insn_cond_track_count;
 static struct qemu_plugin_scoreboard *data;
 static qemu_plugin_u64 data_insn;
 static qemu_plugin_u64 data_tb;
@@ -56,12 +66,19 @@ static void stats_insn(void)
 const uint64_t per_vcpu = qemu_plugin_u64_sum(count_insn);
 const uint64_t inl_per_vcpu =
 qemu_plugin_u64_sum(count_insn_inline);
+const uint64_t cond_num_trigger =
+qemu_plugin_u64_sum(insn_cond_num_trigger);
+const uint64_t cond_track_left = 
qemu_plugin_u64_sum(insn_cond_track_count);
+const uint64_t conditional =
+cond_num_trigger * cond_trigger_limit + cond_track_left;
 printf("insn: %" PRIu64 "\n", expected);
 printf("insn: %" PRIu64 " (per vcpu)\n", per_vcpu);
 printf("insn: %" PRIu64 " (per vcpu inline)\n", inl_per_vcpu);
+printf("insn: %" PRIu64 " (cond cb)\n", conditional);
 g_assert(expected > 0);
 g_assert(per_vcpu == expected);
 g_assert(inl_per_vcpu == expected);
+g_assert(conditional == expected);
 }
 
 static void stats_tb(void)
@@ -70,12 +87,18 @@ static void stats_tb(void)
 const uint64_t per_vcpu = qemu_plugin_u64_sum(count_tb);
 const uint64_t inl_per_vcpu =
 qemu_plugin_u64_sum(count_tb_inline);
+const uint64_t cond_num_trigger = qemu_plugin_u64_sum(tb_cond_num_trigger);
+const uint64_t cond_track_left = qemu_plugin_u64_sum(tb_cond_track_count);
+const uint64_t conditional =
+cond_num_trigger * cond_trigger_limit + cond_track_left;
 printf("tb: %" PRIu64 "\n", expected);
 printf("tb: %" PRIu64 " (per vcpu)\n", per_vcpu);
 printf("tb: %" PRIu64 " (per vcpu inline)\n", inl_per_vcpu);
+printf("tb: %" PRIu64 " (conditional cb)\n", conditional);
 g_assert(expected > 0);
 g_assert(per_vcpu == expected);
 g_assert(inl_per_vcpu == expected);
+g_assert(conditional == expected);
 }
 
 static void stats_mem(void)
@@ -104,14 +127,35 @@ static void plugin_exit(qemu_plugin_id_t id, void *udata)
 const uint64_t insn_inline = qemu_plugin_u64_get(count_insn_inline, i);
 const uint64_t mem = qemu_plugin_u64_get(count_mem, i);
 const uint64_t mem_inline = qemu_plugin_u64_get(count_mem_inline, i);
-printf("cpu %d: tb (%" PRIu64 ", %" PRIu64 ") | "
-   "insn (%" PRIu64 ", %" PRIu64 ") | "
+const uint64_t tb_cond_trigger =
+qemu_plugin_u64_get(tb_cond_num_trigger, i);
+const uint64_t tb_cond_left =
+qemu_plugin_u64_get(tb_cond_track_count, i);
+const uint64_t insn_cond_trigger =
+qemu_plugin_u64_get(insn_cond_num_trigger, i);
+const uint64_t insn_cond_left =
+qemu_plugin_u64_get(insn_cond_track_count, i);
+printf("cpu %d: tb (%" PRIu64 ", %" PRIu64
+   ", %" PRIu64 " * %" PRIu64 " + %" PRIu64
+   ") | "
+   "insn (%" PRIu64 ", %" PRIu64
+   ", %" PRIu64 " * %" PRIu64 " + %" PRIu64
+   ") | "
"mem (%" PRIu64 ", %" PRIu64 ")"
"\n",
-   i, tb, tb_inline, insn, insn_inline, mem, mem_inline);
+   i,
+   tb, tb_inline,
+   tb_cond_trigger, cond_trigger_limit, tb_cond_left,
+   insn, insn_inline,
+   insn_cond_trigger, cond_trigger_limit, insn_cond_left,
+   mem, mem_inline);
 g_assert(tb == tb_inline);
 g_assert(insn == 

[PATCH v4 8/9] plugins: extract cpu_index generate

2024-03-26 Thread Pierrick Bouvier
Reviewed-by: Richard Henderson 
Signed-off-by: Pierrick Bouvier 
---
 accel/tcg/plugin-gen.c | 28 +---
 1 file changed, 13 insertions(+), 15 deletions(-)

diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c
index d00a2b3fd67..eaa0160cab1 100644
--- a/accel/tcg/plugin-gen.c
+++ b/accel/tcg/plugin-gen.c
@@ -108,12 +108,17 @@ static void gen_disable_mem_helper(void)
offsetof(ArchCPU, env));
 }
 
+static TCGv_i32 gen_cpu_index(void)
+{
+TCGv_i32 cpu_index = tcg_temp_ebb_new_i32();
+tcg_gen_ld_i32(cpu_index, tcg_env,
+   -offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index));
+return cpu_index;
+}
+
 static void gen_udata_cb(struct qemu_plugin_regular_cb *cb)
 {
-TCGv_i32 cpu_index = tcg_temp_ebb_new_i32();
-
-tcg_gen_ld_i32(cpu_index, tcg_env,
-   -offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index));
+TCGv_i32 cpu_index = gen_cpu_index();
 tcg_gen_call2(cb->f.vcpu_udata, cb->info, NULL,
   tcgv_i32_temp(cpu_index),
   tcgv_ptr_temp(tcg_constant_ptr(cb->userp)));
@@ -128,9 +133,7 @@ static TCGv_ptr gen_plugin_u64_ptr(qemu_plugin_u64 entry)
 char *base_ptr = arr->data + entry.offset;
 size_t entry_size = g_array_get_element_size(arr);
 
-TCGv_i32 cpu_index = tcg_temp_ebb_new_i32();
-tcg_gen_ld_i32(cpu_index, tcg_env,
-   -offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index));
+TCGv_i32 cpu_index = gen_cpu_index();
 tcg_gen_muli_i32(cpu_index, cpu_index, entry_size);
 tcg_gen_ext_i32_ptr(ptr, cpu_index);
 tcg_temp_free_i32(cpu_index);
@@ -163,7 +166,6 @@ static TCGCond plugin_cond_to_tcgcond(enum qemu_plugin_cond 
cond)
 static void gen_udata_cond_cb(struct qemu_plugin_conditional_cb *cb)
 {
 TCGv_ptr ptr = gen_plugin_u64_ptr(cb->entry);
-TCGv_i32 cpu_index = tcg_temp_ebb_new_i32();
 TCGv_i64 val = tcg_temp_ebb_new_i64();
 TCGLabel *after_cb = gen_new_label();
 
@@ -172,15 +174,14 @@ static void gen_udata_cond_cb(struct 
qemu_plugin_conditional_cb *cb)
 
 tcg_gen_ld_i64(val, ptr, 0);
 tcg_gen_brcondi_i64(cond, val, cb->imm, after_cb);
-tcg_gen_ld_i32(cpu_index, tcg_env,
-   -offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index));
+TCGv_i32 cpu_index = gen_cpu_index();
 tcg_gen_call2(cb->f.vcpu_udata, cb->info, NULL,
   tcgv_i32_temp(cpu_index),
   tcgv_ptr_temp(tcg_constant_ptr(cb->userp)));
+tcg_temp_free_i32(cpu_index);
 gen_set_label(after_cb);
 
 tcg_temp_free_i64(val);
-tcg_temp_free_i32(cpu_index);
 tcg_temp_free_ptr(ptr);
 }
 
@@ -210,10 +211,7 @@ static void gen_inline_store_u64_cb(struct 
qemu_plugin_inline_cb *cb)
 static void gen_mem_cb(struct qemu_plugin_regular_cb *cb,
qemu_plugin_meminfo_t meminfo, TCGv_i64 addr)
 {
-TCGv_i32 cpu_index = tcg_temp_ebb_new_i32();
-
-tcg_gen_ld_i32(cpu_index, tcg_env,
-   -offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index));
+TCGv_i32 cpu_index = gen_cpu_index();
 tcg_gen_call4(cb->f.vcpu_mem, cb->info, NULL,
   tcgv_i32_temp(cpu_index),
   tcgv_i32_temp(tcg_constant_i32(meminfo)),
-- 
2.39.2




[PATCH v4 1/9] plugins: prepare introduction of new inline ops

2024-03-26 Thread Pierrick Bouvier
Until now, only add_u64 was available, and all functions assumed this or
were named uniquely.

Reviewed-by: Richard Henderson 
Signed-off-by: Pierrick Bouvier 
---
 include/qemu/plugin.h  |  2 +-
 accel/tcg/plugin-gen.c |  6 +++---
 plugins/core.c | 14 --
 3 files changed, 16 insertions(+), 6 deletions(-)

diff --git a/include/qemu/plugin.h b/include/qemu/plugin.h
index 201889cbeec..23271fbe36a 100644
--- a/include/qemu/plugin.h
+++ b/include/qemu/plugin.h
@@ -68,7 +68,7 @@ union qemu_plugin_cb_sig {
 enum plugin_dyn_cb_type {
 PLUGIN_CB_REGULAR,
 PLUGIN_CB_MEM_REGULAR,
-PLUGIN_CB_INLINE,
+PLUGIN_CB_INLINE_ADD_U64,
 };
 
 /*
diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c
index c3548257798..41d4d83f547 100644
--- a/accel/tcg/plugin-gen.c
+++ b/accel/tcg/plugin-gen.c
@@ -120,7 +120,7 @@ static void gen_udata_cb(struct qemu_plugin_dyn_cb *cb)
 tcg_temp_free_i32(cpu_index);
 }
 
-static void gen_inline_cb(struct qemu_plugin_dyn_cb *cb)
+static void gen_inline_add_u64_cb(struct qemu_plugin_dyn_cb *cb)
 {
 GArray *arr = cb->inline_insn.entry.score->data;
 size_t offset = cb->inline_insn.entry.offset;
@@ -165,8 +165,8 @@ static void inject_cb(struct qemu_plugin_dyn_cb *cb)
 case PLUGIN_CB_REGULAR:
 gen_udata_cb(cb);
 break;
-case PLUGIN_CB_INLINE:
-gen_inline_cb(cb);
+case PLUGIN_CB_INLINE_ADD_U64:
+gen_inline_add_u64_cb(cb);
 break;
 default:
 g_assert_not_reached();
diff --git a/plugins/core.c b/plugins/core.c
index 0213513ec65..a8557b54ff7 100644
--- a/plugins/core.c
+++ b/plugins/core.c
@@ -316,6 +316,16 @@ static struct qemu_plugin_dyn_cb *plugin_get_dyn_cb(GArray 
**arr)
 return _array_index(cbs, struct qemu_plugin_dyn_cb, cbs->len - 1);
 }
 
+static enum plugin_dyn_cb_type op_to_cb_type(enum qemu_plugin_op op)
+{
+switch (op) {
+case QEMU_PLUGIN_INLINE_ADD_U64:
+return PLUGIN_CB_INLINE_ADD_U64;
+default:
+g_assert_not_reached();
+}
+}
+
 void plugin_register_inline_op_on_entry(GArray **arr,
 enum qemu_plugin_mem_rw rw,
 enum qemu_plugin_op op,
@@ -326,7 +336,7 @@ void plugin_register_inline_op_on_entry(GArray **arr,
 
 dyn_cb = plugin_get_dyn_cb(arr);
 dyn_cb->userp = NULL;
-dyn_cb->type = PLUGIN_CB_INLINE;
+dyn_cb->type = op_to_cb_type(op);
 dyn_cb->rw = rw;
 dyn_cb->inline_insn.entry = entry;
 dyn_cb->inline_insn.op = op;
@@ -551,7 +561,7 @@ void qemu_plugin_vcpu_mem_cb(CPUState *cpu, uint64_t vaddr,
 cb->regular.f.vcpu_mem(cpu->cpu_index, make_plugin_meminfo(oi, rw),
vaddr, cb->userp);
 break;
-case PLUGIN_CB_INLINE:
+case PLUGIN_CB_INLINE_ADD_U64:
 exec_inline_op(cb, cpu->cpu_index);
 break;
 default:
-- 
2.39.2




[PATCH v4 9/9] plugins: remove op from qemu_plugin_inline_cb

2024-03-26 Thread Pierrick Bouvier
Signed-off-by: Pierrick Bouvier 
---
 include/qemu/plugin.h |  1 -
 plugins/plugin.h  |  4 +++-
 plugins/core.c| 13 +++--
 3 files changed, 10 insertions(+), 8 deletions(-)

diff --git a/include/qemu/plugin.h b/include/qemu/plugin.h
index a078229942f..7f7b915e495 100644
--- a/include/qemu/plugin.h
+++ b/include/qemu/plugin.h
@@ -82,7 +82,6 @@ struct qemu_plugin_regular_cb {
 
 struct qemu_plugin_inline_cb {
 qemu_plugin_u64 entry;
-enum qemu_plugin_op op;
 uint64_t imm;
 enum qemu_plugin_mem_rw rw;
 };
diff --git a/plugins/plugin.h b/plugins/plugin.h
index 80d5daa9171..30e2299a54d 100644
--- a/plugins/plugin.h
+++ b/plugins/plugin.h
@@ -108,7 +108,9 @@ void plugin_register_vcpu_mem_cb(GArray **arr,
  enum qemu_plugin_mem_rw rw,
  void *udata);
 
-void exec_inline_op(struct qemu_plugin_inline_cb *cb, int cpu_index);
+void exec_inline_op(enum plugin_dyn_cb_type type,
+struct qemu_plugin_inline_cb *cb,
+int cpu_index);
 
 int plugin_num_vcpus(void);
 
diff --git a/plugins/core.c b/plugins/core.c
index 7ea2ee208db..a9f19e197aa 100644
--- a/plugins/core.c
+++ b/plugins/core.c
@@ -338,7 +338,6 @@ void plugin_register_inline_op_on_entry(GArray **arr,
 
 struct qemu_plugin_inline_cb inline_cb = { .rw = rw,
.entry = entry,
-   .op = op,
.imm = imm };
 dyn_cb = plugin_get_dyn_cb(arr);
 dyn_cb->type = op_to_cb_type(op);
@@ -557,7 +556,9 @@ void qemu_plugin_flush_cb(void)
 plugin_cb__simple(QEMU_PLUGIN_EV_FLUSH);
 }
 
-void exec_inline_op(struct qemu_plugin_inline_cb *cb, int cpu_index)
+void exec_inline_op(enum plugin_dyn_cb_type type,
+struct qemu_plugin_inline_cb *cb,
+int cpu_index)
 {
 char *ptr = cb->entry.score->data->data;
 size_t elem_size = g_array_get_element_size(
@@ -565,11 +566,11 @@ void exec_inline_op(struct qemu_plugin_inline_cb *cb, int 
cpu_index)
 size_t offset = cb->entry.offset;
 uint64_t *val = (uint64_t *)(ptr + offset + cpu_index * elem_size);
 
-switch (cb->op) {
-case QEMU_PLUGIN_INLINE_ADD_U64:
+switch (type) {
+case PLUGIN_CB_INLINE_ADD_U64:
 *val += cb->imm;
 break;
-case QEMU_PLUGIN_INLINE_STORE_U64:
+case PLUGIN_CB_INLINE_STORE_U64:
 *val = cb->imm;
 break;
 default:
@@ -601,7 +602,7 @@ void qemu_plugin_vcpu_mem_cb(CPUState *cpu, uint64_t vaddr,
 case PLUGIN_CB_INLINE_ADD_U64:
 case PLUGIN_CB_INLINE_STORE_U64:
 if (rw && cb->inline_insn.rw) {
-exec_inline_op(>inline_insn, cpu->cpu_index);
+exec_inline_op(cb->type, >inline_insn, cpu->cpu_index);
 }
 break;
 default:
-- 
2.39.2




[PATCH v4 3/9] plugins: add new inline op STORE_U64

2024-03-26 Thread Pierrick Bouvier
Reviewed-by: Richard Henderson 
Signed-off-by: Pierrick Bouvier 
---
 include/qemu/plugin.h  |  1 +
 include/qemu/qemu-plugin.h |  4 ++--
 accel/tcg/plugin-gen.c | 13 +
 plugins/core.c |  6 ++
 4 files changed, 22 insertions(+), 2 deletions(-)

diff --git a/include/qemu/plugin.h b/include/qemu/plugin.h
index 23271fbe36a..d1d9b4490df 100644
--- a/include/qemu/plugin.h
+++ b/include/qemu/plugin.h
@@ -69,6 +69,7 @@ enum plugin_dyn_cb_type {
 PLUGIN_CB_REGULAR,
 PLUGIN_CB_MEM_REGULAR,
 PLUGIN_CB_INLINE_ADD_U64,
+PLUGIN_CB_INLINE_STORE_U64,
 };
 
 /*
diff --git a/include/qemu/qemu-plugin.h b/include/qemu/qemu-plugin.h
index 4fc6c3739b2..c5cac897a0b 100644
--- a/include/qemu/qemu-plugin.h
+++ b/include/qemu/qemu-plugin.h
@@ -305,12 +305,12 @@ void qemu_plugin_register_vcpu_tb_exec_cb(struct 
qemu_plugin_tb *tb,
  * enum qemu_plugin_op - describes an inline op
  *
  * @QEMU_PLUGIN_INLINE_ADD_U64: add an immediate value uint64_t
- *
- * Note: currently only a single inline op is supported.
+ * @QEMU_PLUGIN_INLINE_STORE_U64: store an immediate value uint64_t
  */
 
 enum qemu_plugin_op {
 QEMU_PLUGIN_INLINE_ADD_U64,
+QEMU_PLUGIN_INLINE_STORE_U64,
 };
 
 /**
diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c
index d3667203546..1cfd7908df1 100644
--- a/accel/tcg/plugin-gen.c
+++ b/accel/tcg/plugin-gen.c
@@ -152,6 +152,16 @@ static void gen_inline_add_u64_cb(struct 
qemu_plugin_dyn_cb *cb)
 tcg_temp_free_ptr(ptr);
 }
 
+static void gen_inline_store_u64_cb(struct qemu_plugin_dyn_cb *cb)
+{
+TCGv_ptr ptr = gen_plugin_u64_ptr(cb->inline_insn.entry);
+TCGv_i64 val = tcg_constant_i64(cb->inline_insn.imm);
+
+tcg_gen_st_i64(val, ptr, 0);
+
+tcg_temp_free_ptr(ptr);
+}
+
 static void gen_mem_cb(struct qemu_plugin_dyn_cb *cb,
qemu_plugin_meminfo_t meminfo, TCGv_i64 addr)
 {
@@ -177,6 +187,9 @@ static void inject_cb(struct qemu_plugin_dyn_cb *cb)
 case PLUGIN_CB_INLINE_ADD_U64:
 gen_inline_add_u64_cb(cb);
 break;
+case PLUGIN_CB_INLINE_STORE_U64:
+gen_inline_store_u64_cb(cb);
+break;
 default:
 g_assert_not_reached();
 }
diff --git a/plugins/core.c b/plugins/core.c
index a8557b54ff7..e1bf0dc3717 100644
--- a/plugins/core.c
+++ b/plugins/core.c
@@ -321,6 +321,8 @@ static enum plugin_dyn_cb_type op_to_cb_type(enum 
qemu_plugin_op op)
 switch (op) {
 case QEMU_PLUGIN_INLINE_ADD_U64:
 return PLUGIN_CB_INLINE_ADD_U64;
+case QEMU_PLUGIN_INLINE_STORE_U64:
+return PLUGIN_CB_INLINE_STORE_U64;
 default:
 g_assert_not_reached();
 }
@@ -535,6 +537,9 @@ void exec_inline_op(struct qemu_plugin_dyn_cb *cb, int 
cpu_index)
 case QEMU_PLUGIN_INLINE_ADD_U64:
 *val += cb->inline_insn.imm;
 break;
+case QEMU_PLUGIN_INLINE_STORE_U64:
+*val = cb->inline_insn.imm;
+break;
 default:
 g_assert_not_reached();
 }
@@ -562,6 +567,7 @@ void qemu_plugin_vcpu_mem_cb(CPUState *cpu, uint64_t vaddr,
vaddr, cb->userp);
 break;
 case PLUGIN_CB_INLINE_ADD_U64:
+case PLUGIN_CB_INLINE_STORE_U64:
 exec_inline_op(cb, cpu->cpu_index);
 break;
 default:
-- 
2.39.2




[PATCH v4 4/9] tests/plugin/inline: add test for STORE_U64 inline op

2024-03-26 Thread Pierrick Bouvier
Reviewed-by: Richard Henderson 
Signed-off-by: Pierrick Bouvier 
---
 tests/plugin/inline.c | 41 +
 1 file changed, 37 insertions(+), 4 deletions(-)

diff --git a/tests/plugin/inline.c b/tests/plugin/inline.c
index 0163e9b51c5..103c3a22f6e 100644
--- a/tests/plugin/inline.c
+++ b/tests/plugin/inline.c
@@ -22,6 +22,12 @@ typedef struct {
 uint64_t count_mem_inline;
 } CPUCount;
 
+typedef struct {
+uint64_t data_insn;
+uint64_t data_tb;
+uint64_t data_mem;
+} CPUData;
+
 static struct qemu_plugin_scoreboard *counts;
 static qemu_plugin_u64 count_tb;
 static qemu_plugin_u64 count_tb_inline;
@@ -29,6 +35,10 @@ static qemu_plugin_u64 count_insn;
 static qemu_plugin_u64 count_insn_inline;
 static qemu_plugin_u64 count_mem;
 static qemu_plugin_u64 count_mem_inline;
+static struct qemu_plugin_scoreboard *data;
+static qemu_plugin_u64 data_insn;
+static qemu_plugin_u64 data_tb;
+static qemu_plugin_u64 data_mem;
 
 static uint64_t global_count_tb;
 static uint64_t global_count_insn;
@@ -109,11 +119,13 @@ static void plugin_exit(qemu_plugin_id_t id, void *udata)
 stats_mem();
 
 qemu_plugin_scoreboard_free(counts);
+qemu_plugin_scoreboard_free(data);
 }
 
 static void vcpu_tb_exec(unsigned int cpu_index, void *udata)
 {
 qemu_plugin_u64_add(count_tb, cpu_index, 1);
+g_assert(qemu_plugin_u64_get(data_tb, cpu_index) == (uintptr_t) udata);
 g_mutex_lock(_lock);
 max_cpu_index = MAX(max_cpu_index, cpu_index);
 global_count_tb++;
@@ -123,6 +135,7 @@ static void vcpu_tb_exec(unsigned int cpu_index, void 
*udata)
 static void vcpu_insn_exec(unsigned int cpu_index, void *udata)
 {
 qemu_plugin_u64_add(count_insn, cpu_index, 1);
+g_assert(qemu_plugin_u64_get(data_insn, cpu_index) == (uintptr_t) udata);
 g_mutex_lock(_lock);
 global_count_insn++;
 g_mutex_unlock(_lock);
@@ -131,9 +144,10 @@ static void vcpu_insn_exec(unsigned int cpu_index, void 
*udata)
 static void vcpu_mem_access(unsigned int cpu_index,
 qemu_plugin_meminfo_t info,
 uint64_t vaddr,
-void *userdata)
+void *udata)
 {
 qemu_plugin_u64_add(count_mem, cpu_index, 1);
+g_assert(qemu_plugin_u64_get(data_mem, cpu_index) == (uintptr_t) udata);
 g_mutex_lock(_lock);
 global_count_mem++;
 g_mutex_unlock(_lock);
@@ -141,20 +155,34 @@ static void vcpu_mem_access(unsigned int cpu_index,
 
 static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb)
 {
+void *tb_store = tb;
+qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu(
+tb, QEMU_PLUGIN_INLINE_STORE_U64, data_tb, (uintptr_t) tb_store);
 qemu_plugin_register_vcpu_tb_exec_cb(
-tb, vcpu_tb_exec, QEMU_PLUGIN_CB_NO_REGS, 0);
+tb, vcpu_tb_exec, QEMU_PLUGIN_CB_NO_REGS, tb_store);
 qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu(
 tb, QEMU_PLUGIN_INLINE_ADD_U64, count_tb_inline, 1);
 
 for (int idx = 0; idx < qemu_plugin_tb_n_insns(tb); ++idx) {
 struct qemu_plugin_insn *insn = qemu_plugin_tb_get_insn(tb, idx);
+void *insn_store = insn;
+void *mem_store = (char *)insn_store + 0xff;
+
+qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu(
+insn, QEMU_PLUGIN_INLINE_STORE_U64, data_insn,
+(uintptr_t) insn_store);
 qemu_plugin_register_vcpu_insn_exec_cb(
-insn, vcpu_insn_exec, QEMU_PLUGIN_CB_NO_REGS, 0);
+insn, vcpu_insn_exec, QEMU_PLUGIN_CB_NO_REGS, insn_store);
 qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu(
 insn, QEMU_PLUGIN_INLINE_ADD_U64, count_insn_inline, 1);
+
+qemu_plugin_register_vcpu_mem_inline_per_vcpu(
+insn, QEMU_PLUGIN_MEM_RW,
+QEMU_PLUGIN_INLINE_STORE_U64,
+data_mem, (uintptr_t) mem_store);
 qemu_plugin_register_vcpu_mem_cb(insn, _mem_access,
  QEMU_PLUGIN_CB_NO_REGS,
- QEMU_PLUGIN_MEM_RW, 0);
+ QEMU_PLUGIN_MEM_RW, mem_store);
 qemu_plugin_register_vcpu_mem_inline_per_vcpu(
 insn, QEMU_PLUGIN_MEM_RW,
 QEMU_PLUGIN_INLINE_ADD_U64,
@@ -179,6 +207,11 @@ int qemu_plugin_install(qemu_plugin_id_t id, const 
qemu_info_t *info,
 counts, CPUCount, count_insn_inline);
 count_mem_inline = qemu_plugin_scoreboard_u64_in_struct(
 counts, CPUCount, count_mem_inline);
+data = qemu_plugin_scoreboard_new(sizeof(CPUData));
+data_insn = qemu_plugin_scoreboard_u64_in_struct(data, CPUData, data_insn);
+data_tb = qemu_plugin_scoreboard_u64_in_struct(data, CPUData, data_tb);
+data_mem = qemu_plugin_scoreboard_u64_in_struct(data, CPUData, data_mem);
+
 qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans);
 qemu_plugin_register_atexit_cb(id, plugin_exit, NULL);
 
-- 
2.39.2




[PATCH v4 2/9] plugins: extract generate ptr for qemu_plugin_u64

2024-03-26 Thread Pierrick Bouvier
Reviewed-by: Richard Henderson 
Signed-off-by: Pierrick Bouvier 
---
 accel/tcg/plugin-gen.c | 27 ++-
 1 file changed, 18 insertions(+), 9 deletions(-)

diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c
index 41d4d83f547..d3667203546 100644
--- a/accel/tcg/plugin-gen.c
+++ b/accel/tcg/plugin-gen.c
@@ -120,24 +120,33 @@ static void gen_udata_cb(struct qemu_plugin_dyn_cb *cb)
 tcg_temp_free_i32(cpu_index);
 }
 
-static void gen_inline_add_u64_cb(struct qemu_plugin_dyn_cb *cb)
+static TCGv_ptr gen_plugin_u64_ptr(qemu_plugin_u64 entry)
 {
-GArray *arr = cb->inline_insn.entry.score->data;
-size_t offset = cb->inline_insn.entry.offset;
-TCGv_i32 cpu_index = tcg_temp_ebb_new_i32();
-TCGv_i64 val = tcg_temp_ebb_new_i64();
 TCGv_ptr ptr = tcg_temp_ebb_new_ptr();
 
+GArray *arr = entry.score->data;
+char *base_ptr = arr->data + entry.offset;
+size_t entry_size = g_array_get_element_size(arr);
+
+TCGv_i32 cpu_index = tcg_temp_ebb_new_i32();
 tcg_gen_ld_i32(cpu_index, tcg_env,
-offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index));
-tcg_gen_muli_i32(cpu_index, cpu_index, g_array_get_element_size(arr));
+tcg_gen_muli_i32(cpu_index, cpu_index, entry_size);
 tcg_gen_ext_i32_ptr(ptr, cpu_index);
 tcg_temp_free_i32(cpu_index);
+tcg_gen_addi_ptr(ptr, ptr, (intptr_t) base_ptr);
 
-tcg_gen_addi_ptr(ptr, ptr, (intptr_t)arr->data);
-tcg_gen_ld_i64(val, ptr, offset);
+return ptr;
+}
+
+static void gen_inline_add_u64_cb(struct qemu_plugin_dyn_cb *cb)
+{
+TCGv_ptr ptr = gen_plugin_u64_ptr(cb->inline_insn.entry);
+TCGv_i64 val = tcg_temp_ebb_new_i64();
+
+tcg_gen_ld_i64(val, ptr, 0);
 tcg_gen_addi_i64(val, val, cb->inline_insn.imm);
-tcg_gen_st_i64(val, ptr, offset);
+tcg_gen_st_i64(val, ptr, 0);
 
 tcg_temp_free_i64(val);
 tcg_temp_free_ptr(ptr);
-- 
2.39.2




[PATCH v4 0/9] TCG plugins new inline operations

2024-03-26 Thread Pierrick Bouvier
This series implement two new operations for plugins:
- Store inline allows to write a specific value to a scoreboard.
- Conditional callback executes a callback only when a given condition is true.
  The condition is evaluated inline.

It's possible to mix various inline operations (add, store) with conditional
callbacks, allowing efficient "trap" based counters.

It builds on top of new scoreboard API, introduced in the previous series.

NOTE: Two patches still need review

v2
--

- fixed issue with udata not being passed to conditional callback
- added specific test for this in tests/plugin/inline.c (udata was NULL before).

v3
--

- rebased on top of "plugins: Rewrite plugin code generation":
  20240316015720.3661236-1-richard.hender...@linaro.org
- single pass code generation
- small cleanups for code generation

v4
--

- remove op field from qemu_plugin_inline_cb
- use tcg_constant_i64 to load immediate value to store

Pierrick Bouvier (9):
  plugins: prepare introduction of new inline ops
  plugins: extract generate ptr for qemu_plugin_u64
  plugins: add new inline op STORE_U64
  tests/plugin/inline: add test for STORE_U64 inline op
  plugins: conditional callbacks
  tests/plugin/inline: add test for conditional callback
  plugins: distinct types for callbacks
  plugins: extract cpu_index generate
  plugins: remove op from qemu_plugin_inline_cb

 include/qemu/plugin.h|  42 +++
 include/qemu/qemu-plugin.h   |  80 -
 plugins/plugin.h |  12 +++-
 accel/tcg/plugin-gen.c   | 136 +++
 plugins/api.c|  39 ++
 plugins/core.c   | 109 
 tests/plugin/inline.c| 130 +++--
 plugins/qemu-plugins.symbols |   2 +
 8 files changed, 466 insertions(+), 84 deletions(-)

-- 
2.39.2




Re: [PATCH v3 7/8] plugins: distinct types for callbacks

2024-03-26 Thread Pierrick Bouvier

On 3/25/24 23:23, Richard Henderson wrote:

On 3/25/24 02:41, Pierrick Bouvier wrote:

To prevent errors when writing new types of callbacks or inline
operations, we split callbacks data to distinct types.

Signed-off-by: Pierrick Bouvier 
---
   include/qemu/plugin.h  | 46 ++---
   plugins/plugin.h   |  2 +-
   accel/tcg/plugin-gen.c | 58 +---
   plugins/core.c | 76 ++
   4 files changed, 98 insertions(+), 84 deletions(-)

diff --git a/include/qemu/plugin.h b/include/qemu/plugin.h
index bb224b8e4c7..a078229942f 100644
--- a/include/qemu/plugin.h
+++ b/include/qemu/plugin.h
@@ -73,34 +73,40 @@ enum plugin_dyn_cb_type {
   PLUGIN_CB_INLINE_STORE_U64,
   };
   
+struct qemu_plugin_regular_cb {

+union qemu_plugin_cb_sig f;
+TCGHelperInfo *info;
+void *userp;
+enum qemu_plugin_mem_rw rw;
+};
+
+struct qemu_plugin_inline_cb {
+qemu_plugin_u64 entry;
+enum qemu_plugin_op op;
+uint64_t imm;
+enum qemu_plugin_mem_rw rw;
+};


Do you still need 'op' anymore here?
It seems redundant with 'type'.



You're right, removed it in a new commit, will post new series.


Otherwise,
Reviewed-by: Richard Henderson 


r~




Re: [PATCH v3] contrib/plugins/execlog: Fix compiler warning

2024-03-25 Thread Pierrick Bouvier

On 3/26/24 05:52, Yao Xingtao wrote:

1. The g_pattern_match_string() is deprecated when glib2 version >= 2.70.
Use g_pattern_spec_match_string() instead to avoid this problem.

2. The type of second parameter in g_ptr_array_add() is
'gpointer' {aka 'void *'}, but the type of reg->name is 'const char*'.
Cast the type of reg->name to 'gpointer' to avoid this problem.

compiler warning message:
/root/qemu/contrib/plugins/execlog.c:330:17: warning: ‘g_pattern_match_string’
is deprecated: Use 'g_pattern_spec_match_string'
instead [-Wdeprecated-declarations]
   330 | if (g_pattern_match_string(pat, rd->name) ||
   | ^~
In file included from /usr/include/glib-2.0/glib.h:67,
  from /root/qemu/contrib/plugins/execlog.c:9:
/usr/include/glib-2.0/glib/gpattern.h:57:15: note: declared here
57 | gboolean  g_pattern_match_string   (GPatternSpec *pspec,
   |   ^~
/root/qemu/contrib/plugins/execlog.c:331:21: warning: ‘g_pattern_match_string’
is deprecated: Use 'g_pattern_spec_match_string'
instead [-Wdeprecated-declarations]
   331 | g_pattern_match_string(pat, rd_lower)) {
   | ^~
/usr/include/glib-2.0/glib/gpattern.h:57:15: note: declared here
57 | gboolean  g_pattern_match_string   (GPatternSpec *pspec,
   |   ^~
/root/qemu/contrib/plugins/execlog.c:339:63: warning: passing argument 2 of
‘g_ptr_array_add’ discards ‘const’ qualifier from pointer target type
[-Wdiscarded-qualifiers]
   339 | g_ptr_array_add(all_reg_names, reg->name);
   |~~~^~
In file included from /usr/include/glib-2.0/glib.h:33:
/usr/include/glib-2.0/glib/garray.h:198:62: note: expected
‘gpointer’ {aka ‘void *’} but argument is of type ‘const char *’
   198 |gpointer  data);
   |~~^~~~

Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2210
Signed-off-by: Yao Xingtao 
---
  contrib/plugins/execlog.c | 24 +---
  1 file changed, 21 insertions(+), 3 deletions(-)

diff --git a/contrib/plugins/execlog.c b/contrib/plugins/execlog.c
index a1dfd59ab7..fab18113d4 100644
--- a/contrib/plugins/execlog.c
+++ b/contrib/plugins/execlog.c
@@ -311,6 +311,24 @@ static Register 
*init_vcpu_register(qemu_plugin_reg_descriptor *desc)
  return reg;
  }
  
+/*

+ * g_pattern_match_string has been deprecated in Glib since 2.70 and
+ * will complain about it if you try to use it. Fortunately the
+ * signature of both functions is the same making it easy to work
+ * around.
+ */
+static inline
+gboolean g_pattern_spec_match_string_qemu(GPatternSpec *pspec,
+  const gchar *string)
+{
+#if GLIB_CHECK_VERSION(2, 70, 0)
+return g_pattern_spec_match_string(pspec, string);
+#else
+return g_pattern_match_string(pspec, string);
+#endif
+};
+#define g_pattern_spec_match_string(p, s) g_pattern_spec_match_string_qemu(p, 
s)
+
  static GPtrArray *registers_init(int vcpu_index)
  {
  g_autoptr(GPtrArray) registers = g_ptr_array_new();
@@ -327,8 +345,8 @@ static GPtrArray *registers_init(int vcpu_index)
  for (int p = 0; p < rmatches->len; p++) {
  g_autoptr(GPatternSpec) pat = 
g_pattern_spec_new(rmatches->pdata[p]);
  g_autofree gchar *rd_lower = g_utf8_strdown(rd->name, -1);
-if (g_pattern_match_string(pat, rd->name) ||
-g_pattern_match_string(pat, rd_lower)) {
+if (g_pattern_spec_match_string(pat, rd->name) ||
+g_pattern_spec_match_string(pat, rd_lower)) {
  Register *reg = init_vcpu_register(rd);
  g_ptr_array_add(registers, reg);
  
@@ -336,7 +354,7 @@ static GPtrArray *registers_init(int vcpu_index)

  if (disas_assist) {
  g_mutex_lock(_reg_name_lock);
  if (!g_ptr_array_find(all_reg_names, reg->name, 
NULL)) {
-g_ptr_array_add(all_reg_names, reg->name);
+g_ptr_array_add(all_reg_names, 
(gpointer)reg->name);
  }
  g_mutex_unlock(_reg_name_lock);
  }


Would be nice if it's still possible to merge this in 9.0 Peter.

Reviewed-by: Pierrick Bouvier 


[PATCH v3 8/8] plugins: extract cpu_index generate

2024-03-25 Thread Pierrick Bouvier
Signed-off-by: Pierrick Bouvier 
---
 accel/tcg/plugin-gen.c | 28 +---
 1 file changed, 13 insertions(+), 15 deletions(-)

diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c
index 16618adf1bc..e6fd6fdfae5 100644
--- a/accel/tcg/plugin-gen.c
+++ b/accel/tcg/plugin-gen.c
@@ -108,12 +108,17 @@ static void gen_disable_mem_helper(void)
offsetof(ArchCPU, env));
 }
 
+static TCGv_i32 gen_cpu_index(void)
+{
+TCGv_i32 cpu_index = tcg_temp_ebb_new_i32();
+tcg_gen_ld_i32(cpu_index, tcg_env,
+   -offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index));
+return cpu_index;
+}
+
 static void gen_udata_cb(struct qemu_plugin_regular_cb *cb)
 {
-TCGv_i32 cpu_index = tcg_temp_ebb_new_i32();
-
-tcg_gen_ld_i32(cpu_index, tcg_env,
-   -offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index));
+TCGv_i32 cpu_index = gen_cpu_index();
 tcg_gen_call2(cb->f.vcpu_udata, cb->info, NULL,
   tcgv_i32_temp(cpu_index),
   tcgv_ptr_temp(tcg_constant_ptr(cb->userp)));
@@ -128,9 +133,7 @@ static TCGv_ptr gen_plugin_u64_ptr(qemu_plugin_u64 entry)
 char *base_ptr = arr->data + entry.offset;
 size_t entry_size = g_array_get_element_size(arr);
 
-TCGv_i32 cpu_index = tcg_temp_ebb_new_i32();
-tcg_gen_ld_i32(cpu_index, tcg_env,
-   -offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index));
+TCGv_i32 cpu_index = gen_cpu_index();
 tcg_gen_muli_i32(cpu_index, cpu_index, entry_size);
 tcg_gen_ext_i32_ptr(ptr, cpu_index);
 tcg_temp_free_i32(cpu_index);
@@ -163,7 +166,6 @@ static TCGCond plugin_cond_to_tcgcond(enum qemu_plugin_cond 
cond)
 static void gen_udata_cond_cb(struct qemu_plugin_conditional_cb *cb)
 {
 TCGv_ptr ptr = gen_plugin_u64_ptr(cb->entry);
-TCGv_i32 cpu_index = tcg_temp_ebb_new_i32();
 TCGv_i64 val = tcg_temp_ebb_new_i64();
 TCGLabel *after_cb = gen_new_label();
 
@@ -172,15 +174,14 @@ static void gen_udata_cond_cb(struct 
qemu_plugin_conditional_cb *cb)
 
 tcg_gen_ld_i64(val, ptr, 0);
 tcg_gen_brcondi_i64(cond, val, cb->imm, after_cb);
-tcg_gen_ld_i32(cpu_index, tcg_env,
-   -offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index));
+TCGv_i32 cpu_index = gen_cpu_index();
 tcg_gen_call2(cb->f.vcpu_udata, cb->info, NULL,
   tcgv_i32_temp(cpu_index),
   tcgv_ptr_temp(tcg_constant_ptr(cb->userp)));
+tcg_temp_free_i32(cpu_index);
 gen_set_label(after_cb);
 
 tcg_temp_free_i64(val);
-tcg_temp_free_i32(cpu_index);
 tcg_temp_free_ptr(ptr);
 }
 
@@ -212,10 +213,7 @@ static void gen_inline_store_u64_cb(struct 
qemu_plugin_inline_cb *cb)
 static void gen_mem_cb(struct qemu_plugin_regular_cb *cb,
qemu_plugin_meminfo_t meminfo, TCGv_i64 addr)
 {
-TCGv_i32 cpu_index = tcg_temp_ebb_new_i32();
-
-tcg_gen_ld_i32(cpu_index, tcg_env,
-   -offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index));
+TCGv_i32 cpu_index = gen_cpu_index();
 tcg_gen_call4(cb->f.vcpu_mem, cb->info, NULL,
   tcgv_i32_temp(cpu_index),
   tcgv_i32_temp(tcg_constant_i32(meminfo)),
-- 
2.39.2




[PATCH v3 1/8] plugins: prepare introduction of new inline ops

2024-03-25 Thread Pierrick Bouvier
Until now, only add_u64 was available, and all functions assumed this or
were named uniquely.

Reviewed-by: Richard Henderson 
Signed-off-by: Pierrick Bouvier 
---
 include/qemu/plugin.h  |  2 +-
 accel/tcg/plugin-gen.c |  6 +++---
 plugins/core.c | 14 --
 3 files changed, 16 insertions(+), 6 deletions(-)

diff --git a/include/qemu/plugin.h b/include/qemu/plugin.h
index 201889cbeec..23271fbe36a 100644
--- a/include/qemu/plugin.h
+++ b/include/qemu/plugin.h
@@ -68,7 +68,7 @@ union qemu_plugin_cb_sig {
 enum plugin_dyn_cb_type {
 PLUGIN_CB_REGULAR,
 PLUGIN_CB_MEM_REGULAR,
-PLUGIN_CB_INLINE,
+PLUGIN_CB_INLINE_ADD_U64,
 };
 
 /*
diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c
index c3548257798..41d4d83f547 100644
--- a/accel/tcg/plugin-gen.c
+++ b/accel/tcg/plugin-gen.c
@@ -120,7 +120,7 @@ static void gen_udata_cb(struct qemu_plugin_dyn_cb *cb)
 tcg_temp_free_i32(cpu_index);
 }
 
-static void gen_inline_cb(struct qemu_plugin_dyn_cb *cb)
+static void gen_inline_add_u64_cb(struct qemu_plugin_dyn_cb *cb)
 {
 GArray *arr = cb->inline_insn.entry.score->data;
 size_t offset = cb->inline_insn.entry.offset;
@@ -165,8 +165,8 @@ static void inject_cb(struct qemu_plugin_dyn_cb *cb)
 case PLUGIN_CB_REGULAR:
 gen_udata_cb(cb);
 break;
-case PLUGIN_CB_INLINE:
-gen_inline_cb(cb);
+case PLUGIN_CB_INLINE_ADD_U64:
+gen_inline_add_u64_cb(cb);
 break;
 default:
 g_assert_not_reached();
diff --git a/plugins/core.c b/plugins/core.c
index 0213513ec65..a8557b54ff7 100644
--- a/plugins/core.c
+++ b/plugins/core.c
@@ -316,6 +316,16 @@ static struct qemu_plugin_dyn_cb *plugin_get_dyn_cb(GArray 
**arr)
 return _array_index(cbs, struct qemu_plugin_dyn_cb, cbs->len - 1);
 }
 
+static enum plugin_dyn_cb_type op_to_cb_type(enum qemu_plugin_op op)
+{
+switch (op) {
+case QEMU_PLUGIN_INLINE_ADD_U64:
+return PLUGIN_CB_INLINE_ADD_U64;
+default:
+g_assert_not_reached();
+}
+}
+
 void plugin_register_inline_op_on_entry(GArray **arr,
 enum qemu_plugin_mem_rw rw,
 enum qemu_plugin_op op,
@@ -326,7 +336,7 @@ void plugin_register_inline_op_on_entry(GArray **arr,
 
 dyn_cb = plugin_get_dyn_cb(arr);
 dyn_cb->userp = NULL;
-dyn_cb->type = PLUGIN_CB_INLINE;
+dyn_cb->type = op_to_cb_type(op);
 dyn_cb->rw = rw;
 dyn_cb->inline_insn.entry = entry;
 dyn_cb->inline_insn.op = op;
@@ -551,7 +561,7 @@ void qemu_plugin_vcpu_mem_cb(CPUState *cpu, uint64_t vaddr,
 cb->regular.f.vcpu_mem(cpu->cpu_index, make_plugin_meminfo(oi, rw),
vaddr, cb->userp);
 break;
-case PLUGIN_CB_INLINE:
+case PLUGIN_CB_INLINE_ADD_U64:
 exec_inline_op(cb, cpu->cpu_index);
 break;
 default:
-- 
2.39.2




[PATCH v3 5/8] plugins: conditional callbacks

2024-03-25 Thread Pierrick Bouvier
Extend plugins API to support callback called with a given criteria
(evaluated inline).

Added functions:
- qemu_plugin_register_vcpu_tb_exec_cond_cb
- qemu_plugin_register_vcpu_insn_exec_cond_cb

They expect as parameter a condition, a qemu_plugin_u64_t (op1) and an
immediate (op2). Callback is called if op1 |cond| op2 is true.

Signed-off-by: Pierrick Bouvier 
---
 include/qemu/plugin.h|  8 
 include/qemu/qemu-plugin.h   | 76 
 plugins/plugin.h |  8 
 accel/tcg/plugin-gen.c   | 48 +++
 plugins/api.c| 39 ++
 plugins/core.c   | 32 +++
 plugins/qemu-plugins.symbols |  2 +
 7 files changed, 213 insertions(+)

diff --git a/include/qemu/plugin.h b/include/qemu/plugin.h
index d1d9b4490df..bb224b8e4c7 100644
--- a/include/qemu/plugin.h
+++ b/include/qemu/plugin.h
@@ -67,6 +67,7 @@ union qemu_plugin_cb_sig {
 
 enum plugin_dyn_cb_type {
 PLUGIN_CB_REGULAR,
+PLUGIN_CB_COND,
 PLUGIN_CB_MEM_REGULAR,
 PLUGIN_CB_INLINE_ADD_U64,
 PLUGIN_CB_INLINE_STORE_U64,
@@ -88,6 +89,13 @@ struct qemu_plugin_dyn_cb {
 union qemu_plugin_cb_sig f;
 TCGHelperInfo *info;
 } regular;
+struct {
+union qemu_plugin_cb_sig f;
+TCGHelperInfo *info;
+qemu_plugin_u64 entry;
+enum qemu_plugin_cond cond;
+uint64_t imm;
+} cond;
 struct {
 qemu_plugin_u64 entry;
 enum qemu_plugin_op op;
diff --git a/include/qemu/qemu-plugin.h b/include/qemu/qemu-plugin.h
index c5cac897a0b..337de25ece7 100644
--- a/include/qemu/qemu-plugin.h
+++ b/include/qemu/qemu-plugin.h
@@ -262,6 +262,29 @@ enum qemu_plugin_mem_rw {
 QEMU_PLUGIN_MEM_RW,
 };
 
+/**
+ * enum qemu_plugin_cond - condition to enable callback
+ *
+ * @QEMU_PLUGIN_COND_NEVER: false
+ * @QEMU_PLUGIN_COND_ALWAYS: true
+ * @QEMU_PLUGIN_COND_EQ: is equal?
+ * @QEMU_PLUGIN_COND_NE: is not equal?
+ * @QEMU_PLUGIN_COND_LT: is less than?
+ * @QEMU_PLUGIN_COND_LE: is less than or equal?
+ * @QEMU_PLUGIN_COND_GT: is greater than?
+ * @QEMU_PLUGIN_COND_GE: is greater than or equal?
+ */
+enum qemu_plugin_cond {
+QEMU_PLUGIN_COND_NEVER,
+QEMU_PLUGIN_COND_ALWAYS,
+QEMU_PLUGIN_COND_EQ,
+QEMU_PLUGIN_COND_NE,
+QEMU_PLUGIN_COND_LT,
+QEMU_PLUGIN_COND_LE,
+QEMU_PLUGIN_COND_GT,
+QEMU_PLUGIN_COND_GE,
+};
+
 /**
  * typedef qemu_plugin_vcpu_tb_trans_cb_t - translation callback
  * @id: unique plugin id
@@ -301,6 +324,32 @@ void qemu_plugin_register_vcpu_tb_exec_cb(struct 
qemu_plugin_tb *tb,
   enum qemu_plugin_cb_flags flags,
   void *userdata);
 
+/**
+ * qemu_plugin_register_vcpu_tb_exec_cond_cb() - register conditional callback
+ * @tb: the opaque qemu_plugin_tb handle for the translation
+ * @cb: callback function
+ * @cond: condition to enable callback
+ * @entry: first operand for condition
+ * @imm: second operand for condition
+ * @flags: does the plugin read or write the CPU's registers?
+ * @userdata: any plugin data to pass to the @cb?
+ *
+ * The @cb function is called when a translated unit executes if
+ * entry @cond imm is true.
+ * If condition is QEMU_PLUGIN_COND_ALWAYS, condition is never interpreted and
+ * this function is equivalent to qemu_plugin_register_vcpu_tb_exec_cb.
+ * If condition QEMU_PLUGIN_COND_NEVER, condition is never interpreted and
+ * callback is never installed.
+ */
+QEMU_PLUGIN_API
+void qemu_plugin_register_vcpu_tb_exec_cond_cb(struct qemu_plugin_tb *tb,
+   qemu_plugin_vcpu_udata_cb_t cb,
+   enum qemu_plugin_cb_flags flags,
+   enum qemu_plugin_cond cond,
+   qemu_plugin_u64 entry,
+   uint64_t imm,
+   void *userdata);
+
 /**
  * enum qemu_plugin_op - describes an inline op
  *
@@ -344,6 +393,33 @@ void qemu_plugin_register_vcpu_insn_exec_cb(struct 
qemu_plugin_insn *insn,
 enum qemu_plugin_cb_flags flags,
 void *userdata);
 
+/**
+ * qemu_plugin_register_vcpu_insn_exec_cond_cb() - conditional insn execution 
cb
+ * @insn: the opaque qemu_plugin_insn handle for an instruction
+ * @cb: callback function
+ * @flags: does the plugin read or write the CPU's registers?
+ * @cond: condition to enable callback
+ * @entry: first operand for condition
+ * @imm: second operand for condition
+ * @userdata: any plugin data to pass to the @cb?
+ *
+ * The @cb function is called when an instruction executes if
+ * entry @cond imm is true.
+ * If condition is QEMU_PLUGIN_COND_ALWAYS, condition is never interpreted

[PATCH v3 2/8] plugins: extract generate ptr for qemu_plugin_u64

2024-03-25 Thread Pierrick Bouvier
Signed-off-by: Pierrick Bouvier 
---
 accel/tcg/plugin-gen.c | 27 ++-
 1 file changed, 18 insertions(+), 9 deletions(-)

diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c
index 41d4d83f547..d3667203546 100644
--- a/accel/tcg/plugin-gen.c
+++ b/accel/tcg/plugin-gen.c
@@ -120,24 +120,33 @@ static void gen_udata_cb(struct qemu_plugin_dyn_cb *cb)
 tcg_temp_free_i32(cpu_index);
 }
 
-static void gen_inline_add_u64_cb(struct qemu_plugin_dyn_cb *cb)
+static TCGv_ptr gen_plugin_u64_ptr(qemu_plugin_u64 entry)
 {
-GArray *arr = cb->inline_insn.entry.score->data;
-size_t offset = cb->inline_insn.entry.offset;
-TCGv_i32 cpu_index = tcg_temp_ebb_new_i32();
-TCGv_i64 val = tcg_temp_ebb_new_i64();
 TCGv_ptr ptr = tcg_temp_ebb_new_ptr();
 
+GArray *arr = entry.score->data;
+char *base_ptr = arr->data + entry.offset;
+size_t entry_size = g_array_get_element_size(arr);
+
+TCGv_i32 cpu_index = tcg_temp_ebb_new_i32();
 tcg_gen_ld_i32(cpu_index, tcg_env,
-offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index));
-tcg_gen_muli_i32(cpu_index, cpu_index, g_array_get_element_size(arr));
+tcg_gen_muli_i32(cpu_index, cpu_index, entry_size);
 tcg_gen_ext_i32_ptr(ptr, cpu_index);
 tcg_temp_free_i32(cpu_index);
+tcg_gen_addi_ptr(ptr, ptr, (intptr_t) base_ptr);
 
-tcg_gen_addi_ptr(ptr, ptr, (intptr_t)arr->data);
-tcg_gen_ld_i64(val, ptr, offset);
+return ptr;
+}
+
+static void gen_inline_add_u64_cb(struct qemu_plugin_dyn_cb *cb)
+{
+TCGv_ptr ptr = gen_plugin_u64_ptr(cb->inline_insn.entry);
+TCGv_i64 val = tcg_temp_ebb_new_i64();
+
+tcg_gen_ld_i64(val, ptr, 0);
 tcg_gen_addi_i64(val, val, cb->inline_insn.imm);
-tcg_gen_st_i64(val, ptr, offset);
+tcg_gen_st_i64(val, ptr, 0);
 
 tcg_temp_free_i64(val);
 tcg_temp_free_ptr(ptr);
-- 
2.39.2




[PATCH v3 7/8] plugins: distinct types for callbacks

2024-03-25 Thread Pierrick Bouvier
To prevent errors when writing new types of callbacks or inline
operations, we split callbacks data to distinct types.

Signed-off-by: Pierrick Bouvier 
---
 include/qemu/plugin.h  | 46 ++---
 plugins/plugin.h   |  2 +-
 accel/tcg/plugin-gen.c | 58 +---
 plugins/core.c | 76 ++
 4 files changed, 98 insertions(+), 84 deletions(-)

diff --git a/include/qemu/plugin.h b/include/qemu/plugin.h
index bb224b8e4c7..a078229942f 100644
--- a/include/qemu/plugin.h
+++ b/include/qemu/plugin.h
@@ -73,34 +73,40 @@ enum plugin_dyn_cb_type {
 PLUGIN_CB_INLINE_STORE_U64,
 };
 
+struct qemu_plugin_regular_cb {
+union qemu_plugin_cb_sig f;
+TCGHelperInfo *info;
+void *userp;
+enum qemu_plugin_mem_rw rw;
+};
+
+struct qemu_plugin_inline_cb {
+qemu_plugin_u64 entry;
+enum qemu_plugin_op op;
+uint64_t imm;
+enum qemu_plugin_mem_rw rw;
+};
+
+struct qemu_plugin_conditional_cb {
+union qemu_plugin_cb_sig f;
+TCGHelperInfo *info;
+void *userp;
+qemu_plugin_u64 entry;
+enum qemu_plugin_cond cond;
+uint64_t imm;
+};
+
 /*
  * 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 {
-void *userp;
 enum plugin_dyn_cb_type type;
-/* @rw applies to mem callbacks only (both regular and inline) */
-enum qemu_plugin_mem_rw rw;
-/* fields specific to each dyn_cb type go here */
 union {
-struct {
-union qemu_plugin_cb_sig f;
-TCGHelperInfo *info;
-} regular;
-struct {
-union qemu_plugin_cb_sig f;
-TCGHelperInfo *info;
-qemu_plugin_u64 entry;
-enum qemu_plugin_cond cond;
-uint64_t imm;
-} cond;
-struct {
-qemu_plugin_u64 entry;
-enum qemu_plugin_op op;
-uint64_t imm;
-} inline_insn;
+struct qemu_plugin_regular_cb regular;
+struct qemu_plugin_conditional_cb cond;
+struct qemu_plugin_inline_cb inline_insn;
 };
 };
 
diff --git a/plugins/plugin.h b/plugins/plugin.h
index 7d4b4e21f7c..80d5daa9171 100644
--- a/plugins/plugin.h
+++ b/plugins/plugin.h
@@ -108,7 +108,7 @@ void plugin_register_vcpu_mem_cb(GArray **arr,
  enum qemu_plugin_mem_rw rw,
  void *udata);
 
-void exec_inline_op(struct qemu_plugin_dyn_cb *cb, int cpu_index);
+void exec_inline_op(struct qemu_plugin_inline_cb *cb, int cpu_index);
 
 int plugin_num_vcpus(void);
 
diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c
index 7ecaf670d93..16618adf1bc 100644
--- a/accel/tcg/plugin-gen.c
+++ b/accel/tcg/plugin-gen.c
@@ -108,13 +108,13 @@ static void gen_disable_mem_helper(void)
offsetof(ArchCPU, env));
 }
 
-static void gen_udata_cb(struct qemu_plugin_dyn_cb *cb)
+static void gen_udata_cb(struct qemu_plugin_regular_cb *cb)
 {
 TCGv_i32 cpu_index = tcg_temp_ebb_new_i32();
 
 tcg_gen_ld_i32(cpu_index, tcg_env,
-offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index));
-tcg_gen_call2(cb->regular.f.vcpu_udata, cb->regular.info, NULL,
+tcg_gen_call2(cb->f.vcpu_udata, cb->info, NULL,
   tcgv_i32_temp(cpu_index),
   tcgv_ptr_temp(tcg_constant_ptr(cb->userp)));
 tcg_temp_free_i32(cpu_index);
@@ -160,21 +160,21 @@ static TCGCond plugin_cond_to_tcgcond(enum 
qemu_plugin_cond cond)
 }
 }
 
-static void gen_udata_cond_cb(struct qemu_plugin_dyn_cb *cb)
+static void gen_udata_cond_cb(struct qemu_plugin_conditional_cb *cb)
 {
-TCGv_ptr ptr = gen_plugin_u64_ptr(cb->cond.entry);
+TCGv_ptr ptr = gen_plugin_u64_ptr(cb->entry);
 TCGv_i32 cpu_index = tcg_temp_ebb_new_i32();
 TCGv_i64 val = tcg_temp_ebb_new_i64();
 TCGLabel *after_cb = gen_new_label();
 
 /* Condition should be negated, as calling the cb is the "else" path */
-TCGCond cond = tcg_invert_cond(plugin_cond_to_tcgcond(cb->cond.cond));
+TCGCond cond = tcg_invert_cond(plugin_cond_to_tcgcond(cb->cond));
 
 tcg_gen_ld_i64(val, ptr, 0);
-tcg_gen_brcondi_i64(cond, val, cb->cond.imm, after_cb);
+tcg_gen_brcondi_i64(cond, val, cb->imm, after_cb);
 tcg_gen_ld_i32(cpu_index, tcg_env,
-offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index));
-tcg_gen_call2(cb->cond.f.vcpu_udata, cb->cond.info, NULL,
+tcg_gen_call2(cb->f.vcpu_udata, cb->info, NULL,
   tcgv_i32_temp(cpu_index),
   tcgv_ptr_temp(tcg_constant_ptr(cb->userp)));
 gen_set_label(after_cb);
@@ -184,39 +184,39 @@ static void gen_udata_cond_cb(struct qemu_plugin_dyn_cb 
*cb)
 tcg_temp_fre

[PATCH v3 6/8] tests/plugin/inline: add test for conditional callback

2024-03-25 Thread Pierrick Bouvier
Count number of tb and insn executed using a conditional callback. We
ensure the callback has been called expected number of time (per vcpu).

Signed-off-by: Pierrick Bouvier 
---
 tests/plugin/inline.c | 89 +--
 1 file changed, 86 insertions(+), 3 deletions(-)

diff --git a/tests/plugin/inline.c b/tests/plugin/inline.c
index 103c3a22f6e..cd63827b7d8 100644
--- a/tests/plugin/inline.c
+++ b/tests/plugin/inline.c
@@ -20,8 +20,14 @@ typedef struct {
 uint64_t count_insn_inline;
 uint64_t count_mem;
 uint64_t count_mem_inline;
+uint64_t tb_cond_num_trigger;
+uint64_t tb_cond_track_count;
+uint64_t insn_cond_num_trigger;
+uint64_t insn_cond_track_count;
 } CPUCount;
 
+static const uint64_t cond_trigger_limit = 100;
+
 typedef struct {
 uint64_t data_insn;
 uint64_t data_tb;
@@ -35,6 +41,10 @@ static qemu_plugin_u64 count_insn;
 static qemu_plugin_u64 count_insn_inline;
 static qemu_plugin_u64 count_mem;
 static qemu_plugin_u64 count_mem_inline;
+static qemu_plugin_u64 tb_cond_num_trigger;
+static qemu_plugin_u64 tb_cond_track_count;
+static qemu_plugin_u64 insn_cond_num_trigger;
+static qemu_plugin_u64 insn_cond_track_count;
 static struct qemu_plugin_scoreboard *data;
 static qemu_plugin_u64 data_insn;
 static qemu_plugin_u64 data_tb;
@@ -56,12 +66,19 @@ static void stats_insn(void)
 const uint64_t per_vcpu = qemu_plugin_u64_sum(count_insn);
 const uint64_t inl_per_vcpu =
 qemu_plugin_u64_sum(count_insn_inline);
+const uint64_t cond_num_trigger =
+qemu_plugin_u64_sum(insn_cond_num_trigger);
+const uint64_t cond_track_left = 
qemu_plugin_u64_sum(insn_cond_track_count);
+const uint64_t conditional =
+cond_num_trigger * cond_trigger_limit + cond_track_left;
 printf("insn: %" PRIu64 "\n", expected);
 printf("insn: %" PRIu64 " (per vcpu)\n", per_vcpu);
 printf("insn: %" PRIu64 " (per vcpu inline)\n", inl_per_vcpu);
+printf("insn: %" PRIu64 " (cond cb)\n", conditional);
 g_assert(expected > 0);
 g_assert(per_vcpu == expected);
 g_assert(inl_per_vcpu == expected);
+g_assert(conditional == expected);
 }
 
 static void stats_tb(void)
@@ -70,12 +87,18 @@ static void stats_tb(void)
 const uint64_t per_vcpu = qemu_plugin_u64_sum(count_tb);
 const uint64_t inl_per_vcpu =
 qemu_plugin_u64_sum(count_tb_inline);
+const uint64_t cond_num_trigger = qemu_plugin_u64_sum(tb_cond_num_trigger);
+const uint64_t cond_track_left = qemu_plugin_u64_sum(tb_cond_track_count);
+const uint64_t conditional =
+cond_num_trigger * cond_trigger_limit + cond_track_left;
 printf("tb: %" PRIu64 "\n", expected);
 printf("tb: %" PRIu64 " (per vcpu)\n", per_vcpu);
 printf("tb: %" PRIu64 " (per vcpu inline)\n", inl_per_vcpu);
+printf("tb: %" PRIu64 " (conditional cb)\n", conditional);
 g_assert(expected > 0);
 g_assert(per_vcpu == expected);
 g_assert(inl_per_vcpu == expected);
+g_assert(conditional == expected);
 }
 
 static void stats_mem(void)
@@ -104,14 +127,35 @@ static void plugin_exit(qemu_plugin_id_t id, void *udata)
 const uint64_t insn_inline = qemu_plugin_u64_get(count_insn_inline, i);
 const uint64_t mem = qemu_plugin_u64_get(count_mem, i);
 const uint64_t mem_inline = qemu_plugin_u64_get(count_mem_inline, i);
-printf("cpu %d: tb (%" PRIu64 ", %" PRIu64 ") | "
-   "insn (%" PRIu64 ", %" PRIu64 ") | "
+const uint64_t tb_cond_trigger =
+qemu_plugin_u64_get(tb_cond_num_trigger, i);
+const uint64_t tb_cond_left =
+qemu_plugin_u64_get(tb_cond_track_count, i);
+const uint64_t insn_cond_trigger =
+qemu_plugin_u64_get(insn_cond_num_trigger, i);
+const uint64_t insn_cond_left =
+qemu_plugin_u64_get(insn_cond_track_count, i);
+printf("cpu %d: tb (%" PRIu64 ", %" PRIu64
+   ", %" PRIu64 " * %" PRIu64 " + %" PRIu64
+   ") | "
+   "insn (%" PRIu64 ", %" PRIu64
+   ", %" PRIu64 " * %" PRIu64 " + %" PRIu64
+   ") | "
"mem (%" PRIu64 ", %" PRIu64 ")"
"\n",
-   i, tb, tb_inline, insn, insn_inline, mem, mem_inline);
+   i,
+   tb, tb_inline,
+   tb_cond_trigger, cond_trigger_limit, tb_cond_left,
+   insn, insn_inline,
+   insn_cond_trigger, cond_trigger_limit, insn_cond_left,
+   mem, mem_inline);
 g_assert(tb == tb_inline);
 g_assert(insn == 

[PATCH v3 4/8] tests/plugin/inline: add test for STORE_U64 inline op

2024-03-25 Thread Pierrick Bouvier
Signed-off-by: Pierrick Bouvier 
---
 tests/plugin/inline.c | 41 +
 1 file changed, 37 insertions(+), 4 deletions(-)

diff --git a/tests/plugin/inline.c b/tests/plugin/inline.c
index 0163e9b51c5..103c3a22f6e 100644
--- a/tests/plugin/inline.c
+++ b/tests/plugin/inline.c
@@ -22,6 +22,12 @@ typedef struct {
 uint64_t count_mem_inline;
 } CPUCount;
 
+typedef struct {
+uint64_t data_insn;
+uint64_t data_tb;
+uint64_t data_mem;
+} CPUData;
+
 static struct qemu_plugin_scoreboard *counts;
 static qemu_plugin_u64 count_tb;
 static qemu_plugin_u64 count_tb_inline;
@@ -29,6 +35,10 @@ static qemu_plugin_u64 count_insn;
 static qemu_plugin_u64 count_insn_inline;
 static qemu_plugin_u64 count_mem;
 static qemu_plugin_u64 count_mem_inline;
+static struct qemu_plugin_scoreboard *data;
+static qemu_plugin_u64 data_insn;
+static qemu_plugin_u64 data_tb;
+static qemu_plugin_u64 data_mem;
 
 static uint64_t global_count_tb;
 static uint64_t global_count_insn;
@@ -109,11 +119,13 @@ static void plugin_exit(qemu_plugin_id_t id, void *udata)
 stats_mem();
 
 qemu_plugin_scoreboard_free(counts);
+qemu_plugin_scoreboard_free(data);
 }
 
 static void vcpu_tb_exec(unsigned int cpu_index, void *udata)
 {
 qemu_plugin_u64_add(count_tb, cpu_index, 1);
+g_assert(qemu_plugin_u64_get(data_tb, cpu_index) == (uintptr_t) udata);
 g_mutex_lock(_lock);
 max_cpu_index = MAX(max_cpu_index, cpu_index);
 global_count_tb++;
@@ -123,6 +135,7 @@ static void vcpu_tb_exec(unsigned int cpu_index, void 
*udata)
 static void vcpu_insn_exec(unsigned int cpu_index, void *udata)
 {
 qemu_plugin_u64_add(count_insn, cpu_index, 1);
+g_assert(qemu_plugin_u64_get(data_insn, cpu_index) == (uintptr_t) udata);
 g_mutex_lock(_lock);
 global_count_insn++;
 g_mutex_unlock(_lock);
@@ -131,9 +144,10 @@ static void vcpu_insn_exec(unsigned int cpu_index, void 
*udata)
 static void vcpu_mem_access(unsigned int cpu_index,
 qemu_plugin_meminfo_t info,
 uint64_t vaddr,
-void *userdata)
+void *udata)
 {
 qemu_plugin_u64_add(count_mem, cpu_index, 1);
+g_assert(qemu_plugin_u64_get(data_mem, cpu_index) == (uintptr_t) udata);
 g_mutex_lock(_lock);
 global_count_mem++;
 g_mutex_unlock(_lock);
@@ -141,20 +155,34 @@ static void vcpu_mem_access(unsigned int cpu_index,
 
 static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb)
 {
+void *tb_store = tb;
+qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu(
+tb, QEMU_PLUGIN_INLINE_STORE_U64, data_tb, (uintptr_t) tb_store);
 qemu_plugin_register_vcpu_tb_exec_cb(
-tb, vcpu_tb_exec, QEMU_PLUGIN_CB_NO_REGS, 0);
+tb, vcpu_tb_exec, QEMU_PLUGIN_CB_NO_REGS, tb_store);
 qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu(
 tb, QEMU_PLUGIN_INLINE_ADD_U64, count_tb_inline, 1);
 
 for (int idx = 0; idx < qemu_plugin_tb_n_insns(tb); ++idx) {
 struct qemu_plugin_insn *insn = qemu_plugin_tb_get_insn(tb, idx);
+void *insn_store = insn;
+void *mem_store = (char *)insn_store + 0xff;
+
+qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu(
+insn, QEMU_PLUGIN_INLINE_STORE_U64, data_insn,
+(uintptr_t) insn_store);
 qemu_plugin_register_vcpu_insn_exec_cb(
-insn, vcpu_insn_exec, QEMU_PLUGIN_CB_NO_REGS, 0);
+insn, vcpu_insn_exec, QEMU_PLUGIN_CB_NO_REGS, insn_store);
 qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu(
 insn, QEMU_PLUGIN_INLINE_ADD_U64, count_insn_inline, 1);
+
+qemu_plugin_register_vcpu_mem_inline_per_vcpu(
+insn, QEMU_PLUGIN_MEM_RW,
+QEMU_PLUGIN_INLINE_STORE_U64,
+data_mem, (uintptr_t) mem_store);
 qemu_plugin_register_vcpu_mem_cb(insn, _mem_access,
  QEMU_PLUGIN_CB_NO_REGS,
- QEMU_PLUGIN_MEM_RW, 0);
+ QEMU_PLUGIN_MEM_RW, mem_store);
 qemu_plugin_register_vcpu_mem_inline_per_vcpu(
 insn, QEMU_PLUGIN_MEM_RW,
 QEMU_PLUGIN_INLINE_ADD_U64,
@@ -179,6 +207,11 @@ int qemu_plugin_install(qemu_plugin_id_t id, const 
qemu_info_t *info,
 counts, CPUCount, count_insn_inline);
 count_mem_inline = qemu_plugin_scoreboard_u64_in_struct(
 counts, CPUCount, count_mem_inline);
+data = qemu_plugin_scoreboard_new(sizeof(CPUData));
+data_insn = qemu_plugin_scoreboard_u64_in_struct(data, CPUData, data_insn);
+data_tb = qemu_plugin_scoreboard_u64_in_struct(data, CPUData, data_tb);
+data_mem = qemu_plugin_scoreboard_u64_in_struct(data, CPUData, data_mem);
+
 qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans);
 qemu_plugin_register_atexit_cb(id, plugin_exit, NULL);
 
-- 
2.39.2




[PATCH v3 3/8] plugins: add new inline op STORE_U64

2024-03-25 Thread Pierrick Bouvier
Signed-off-by: Pierrick Bouvier 
---
 include/qemu/plugin.h  |  1 +
 include/qemu/qemu-plugin.h |  4 ++--
 accel/tcg/plugin-gen.c | 15 +++
 plugins/core.c |  6 ++
 4 files changed, 24 insertions(+), 2 deletions(-)

diff --git a/include/qemu/plugin.h b/include/qemu/plugin.h
index 23271fbe36a..d1d9b4490df 100644
--- a/include/qemu/plugin.h
+++ b/include/qemu/plugin.h
@@ -69,6 +69,7 @@ enum plugin_dyn_cb_type {
 PLUGIN_CB_REGULAR,
 PLUGIN_CB_MEM_REGULAR,
 PLUGIN_CB_INLINE_ADD_U64,
+PLUGIN_CB_INLINE_STORE_U64,
 };
 
 /*
diff --git a/include/qemu/qemu-plugin.h b/include/qemu/qemu-plugin.h
index 4fc6c3739b2..c5cac897a0b 100644
--- a/include/qemu/qemu-plugin.h
+++ b/include/qemu/qemu-plugin.h
@@ -305,12 +305,12 @@ void qemu_plugin_register_vcpu_tb_exec_cb(struct 
qemu_plugin_tb *tb,
  * enum qemu_plugin_op - describes an inline op
  *
  * @QEMU_PLUGIN_INLINE_ADD_U64: add an immediate value uint64_t
- *
- * Note: currently only a single inline op is supported.
+ * @QEMU_PLUGIN_INLINE_STORE_U64: store an immediate value uint64_t
  */
 
 enum qemu_plugin_op {
 QEMU_PLUGIN_INLINE_ADD_U64,
+QEMU_PLUGIN_INLINE_STORE_U64,
 };
 
 /**
diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c
index d3667203546..45856a75cda 100644
--- a/accel/tcg/plugin-gen.c
+++ b/accel/tcg/plugin-gen.c
@@ -152,6 +152,18 @@ static void gen_inline_add_u64_cb(struct 
qemu_plugin_dyn_cb *cb)
 tcg_temp_free_ptr(ptr);
 }
 
+static void gen_inline_store_u64_cb(struct qemu_plugin_dyn_cb *cb)
+{
+TCGv_ptr ptr = gen_plugin_u64_ptr(cb->inline_insn.entry);
+TCGv_i64 val = tcg_temp_ebb_new_i64();
+
+tcg_gen_movi_i64(val, cb->inline_insn.imm);
+tcg_gen_st_i64(val, ptr, 0);
+
+tcg_temp_free_i64(val);
+tcg_temp_free_ptr(ptr);
+}
+
 static void gen_mem_cb(struct qemu_plugin_dyn_cb *cb,
qemu_plugin_meminfo_t meminfo, TCGv_i64 addr)
 {
@@ -177,6 +189,9 @@ static void inject_cb(struct qemu_plugin_dyn_cb *cb)
 case PLUGIN_CB_INLINE_ADD_U64:
 gen_inline_add_u64_cb(cb);
 break;
+case PLUGIN_CB_INLINE_STORE_U64:
+gen_inline_store_u64_cb(cb);
+break;
 default:
 g_assert_not_reached();
 }
diff --git a/plugins/core.c b/plugins/core.c
index a8557b54ff7..e1bf0dc3717 100644
--- a/plugins/core.c
+++ b/plugins/core.c
@@ -321,6 +321,8 @@ static enum plugin_dyn_cb_type op_to_cb_type(enum 
qemu_plugin_op op)
 switch (op) {
 case QEMU_PLUGIN_INLINE_ADD_U64:
 return PLUGIN_CB_INLINE_ADD_U64;
+case QEMU_PLUGIN_INLINE_STORE_U64:
+return PLUGIN_CB_INLINE_STORE_U64;
 default:
 g_assert_not_reached();
 }
@@ -535,6 +537,9 @@ void exec_inline_op(struct qemu_plugin_dyn_cb *cb, int 
cpu_index)
 case QEMU_PLUGIN_INLINE_ADD_U64:
 *val += cb->inline_insn.imm;
 break;
+case QEMU_PLUGIN_INLINE_STORE_U64:
+*val = cb->inline_insn.imm;
+break;
 default:
 g_assert_not_reached();
 }
@@ -562,6 +567,7 @@ void qemu_plugin_vcpu_mem_cb(CPUState *cpu, uint64_t vaddr,
vaddr, cb->userp);
 break;
 case PLUGIN_CB_INLINE_ADD_U64:
+case PLUGIN_CB_INLINE_STORE_U64:
 exec_inline_op(cb, cpu->cpu_index);
 break;
 default:
-- 
2.39.2




[PATCH v3 0/8] TCG plugins new inline operations

2024-03-25 Thread Pierrick Bouvier
This series implement two new operations for plugins:
- Store inline allows to write a specific value to a scoreboard.
- Conditional callback executes a callback only when a given condition is true.
  The condition is evaluated inline.

It's possible to mix various inline operations (add, store) with conditional
callbacks, allowing efficient "trap" based counters.

It builds on top of new scoreboard API, introduced in the previous series.

v2
--

- fixed issue with udata not being passed to conditional callback
- added specific test for this in tests/plugin/inline.c (udata was NULL before).

v3
--

- rebased on top of "plugins: Rewrite plugin code generation":
  20240316015720.3661236-1-richard.hender...@linaro.org
- single pass code generation
- small cleanups for code generation

Pierrick Bouvier (8):
  plugins: prepare introduction of new inline ops
  plugins: extract generate ptr for qemu_plugin_u64
  plugins: add new inline op STORE_U64
  tests/plugin/inline: add test for STORE_U64 inline op
  plugins: conditional callbacks
  tests/plugin/inline: add test for conditional callback
  plugins: distinct types for callbacks
  plugins: extract cpu_index generate

 include/qemu/plugin.h|  43 +++
 include/qemu/qemu-plugin.h   |  80 +++-
 plugins/plugin.h |  10 ++-
 accel/tcg/plugin-gen.c   | 138 +++
 plugins/api.c|  39 ++
 plugins/core.c   | 106 ---
 tests/plugin/inline.c| 130 +++--
 plugins/qemu-plugins.symbols |   2 +
 8 files changed, 465 insertions(+), 83 deletions(-)

-- 
2.39.2




Re: [PATCH v2] contrib/plugins/execlog: Fix compiler warning

2024-03-25 Thread Pierrick Bouvier

On 3/25/24 13:58, Peter Maydell wrote:

On Mon, 25 Mar 2024 at 06:41, Pierrick Bouvier
 wrote:


On 3/25/24 10:06, Yao Xingtao wrote:

diff --git a/contrib/plugins/execlog.c b/contrib/plugins/execlog.c
index a1dfd59ab7..09654910ee 100644
--- a/contrib/plugins/execlog.c
+++ b/contrib/plugins/execlog.c
@@ -327,8 +327,13 @@ static GPtrArray *registers_init(int vcpu_index)
   for (int p = 0; p < rmatches->len; p++) {
   g_autoptr(GPatternSpec) pat = 
g_pattern_spec_new(rmatches->pdata[p]);
   g_autofree gchar *rd_lower = g_utf8_strdown(rd->name, -1);
+#if GLIB_CHECK_VERSION(2, 70, 0)
+if (g_pattern_spec_match_string(pat, rd->name) ||
+g_pattern_spec_match_string(pat, rd_lower)) {
+#else
   if (g_pattern_match_string(pat, rd->name) ||
   g_pattern_match_string(pat, rd_lower)) {
+#endif
   Register *reg = init_vcpu_register(rd);
   g_ptr_array_add(registers, reg);



As suggested by Peter on previous version, you can declare a new
function `g_pattern_match_string_qemu` in include/glib-compat.h which
abstract this.


We should have an abstraction function, but it should *not*
be in glib-compat.h, but local to this plugin's .c file. This is
because the plugins are deliberately standalone binaries which do not
rely on any of QEMU's include files or build process (you'll
see they don't use osdep.h, for example).



Sorry, I misunderstood that, as it was discussed with Alex that maybe 
plugins should be able to access glib-compat.h.



thanks
-- PMM




Re: [PATCH v2] contrib/plugins/execlog: Fix compiler warning

2024-03-25 Thread Pierrick Bouvier

On 3/25/24 10:06, Yao Xingtao wrote:

1. The g_pattern_match_string() is deprecated when glib2 version >= 2.70.
Use g_pattern_spec_match_string() instead to avoid this problem.

2. The type of second parameter in g_ptr_array_add() is
'gpointer' {aka 'void *'}, but the type of reg->name is 'const char*'.
Cast the type of reg->name to 'gpointer' to avoid this problem.

compiler warning message:
/root/qemu/contrib/plugins/execlog.c:330:17: warning: ‘g_pattern_match_string’
is deprecated: Use 'g_pattern_spec_match_string'
instead [-Wdeprecated-declarations]
   330 | if (g_pattern_match_string(pat, rd->name) ||
   | ^~
In file included from /usr/include/glib-2.0/glib.h:67,
  from /root/qemu/contrib/plugins/execlog.c:9:
/usr/include/glib-2.0/glib/gpattern.h:57:15: note: declared here
57 | gboolean  g_pattern_match_string   (GPatternSpec *pspec,
   |   ^~
/root/qemu/contrib/plugins/execlog.c:331:21: warning: ‘g_pattern_match_string’
is deprecated: Use 'g_pattern_spec_match_string'
instead [-Wdeprecated-declarations]
   331 | g_pattern_match_string(pat, rd_lower)) {
   | ^~
/usr/include/glib-2.0/glib/gpattern.h:57:15: note: declared here
57 | gboolean  g_pattern_match_string   (GPatternSpec *pspec,
   |   ^~
/root/qemu/contrib/plugins/execlog.c:339:63: warning: passing argument 2 of
‘g_ptr_array_add’ discards ‘const’ qualifier from pointer target type
[-Wdiscarded-qualifiers]
   339 | g_ptr_array_add(all_reg_names, reg->name);
   |~~~^~
In file included from /usr/include/glib-2.0/glib.h:33:
/usr/include/glib-2.0/glib/garray.h:198:62: note: expected
‘gpointer’ {aka ‘void *’} but argument is of type ‘const char *’
   198 |gpointer  data);
   |~~^~~~

Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2210
Signed-off-by: Yao Xingtao 
---
  contrib/plugins/execlog.c | 7 ++-
  1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/contrib/plugins/execlog.c b/contrib/plugins/execlog.c
index a1dfd59ab7..09654910ee 100644
--- a/contrib/plugins/execlog.c
+++ b/contrib/plugins/execlog.c
@@ -327,8 +327,13 @@ static GPtrArray *registers_init(int vcpu_index)
  for (int p = 0; p < rmatches->len; p++) {
  g_autoptr(GPatternSpec) pat = 
g_pattern_spec_new(rmatches->pdata[p]);
  g_autofree gchar *rd_lower = g_utf8_strdown(rd->name, -1);
+#if GLIB_CHECK_VERSION(2, 70, 0)
+if (g_pattern_spec_match_string(pat, rd->name) ||
+g_pattern_spec_match_string(pat, rd_lower)) {
+#else
  if (g_pattern_match_string(pat, rd->name) ||
  g_pattern_match_string(pat, rd_lower)) {
+#endif
  Register *reg = init_vcpu_register(rd);
  g_ptr_array_add(registers, reg);
  


As suggested by Peter on previous version, you can declare a new 
function `g_pattern_match_string_qemu` in include/glib-compat.h which 
abstract this.
You'll need to add include/ to Makefile as well, so glib-compat.h will 
be accessible to contrib plugins too.



@@ -336,7 +341,7 @@ static GPtrArray *registers_init(int vcpu_index)
  if (disas_assist) {
  g_mutex_lock(_reg_name_lock);
  if (!g_ptr_array_find(all_reg_names, reg->name, 
NULL)) {
-g_ptr_array_add(all_reg_names, reg->name);
+g_ptr_array_add(all_reg_names, 
(gpointer)reg->name);
  }
  g_mutex_unlock(_reg_name_lock);
  }


Re: [PATCH] contrib/plugins/execlog: Fix compiler warning

2024-03-24 Thread Pierrick Bouvier

On 3/25/24 07:00, Xingtao Yao (Fujitsu) wrote:

Pete:
Thanks for your comment.

I also find a similar patch written by Pierrick: 
https://lore.kernel.org/qemu-devel/20240118032400.3762658-15-pierrick.bouv...@linaro.org/
but for some reason, the patch was not merged yet.

shall I need to continue tracking the fixes of this bug?



Hi Xingtao,

you're doing the right thing here. In my original patch, there was no 
consideration for backward compatibility, to the opposite of what you did.


Alex will be out for several weeks, so it might take some time to get 
this merged, but I'm definitely voting for this .


Pierrick


-Original Message-
From: Peter Maydell 
Sent: Friday, March 22, 2024 7:50 PM
To: Yao, Xingtao/姚 幸涛 
Cc: alex.ben...@linaro.org; erdn...@crans.org; ma.mando...@gmail.com;
pierrick.bouv...@linaro.org; qemu-devel@nongnu.org
Subject: Re: [PATCH] contrib/plugins/execlog: Fix compiler warning

On Wed, 20 Mar 2024 at 02:05, Yao Xingtao via 
wrote:


1. The g_pattern_match_string() is deprecated when glib2 version >= 2.70.
Use g_pattern_spec_match_string() instead to avoid this problem.

2. The type of second parameter in g_ptr_array_add() is
'gpointer' {aka 'void *'}, but the type of reg->name is 'const char*'.
Cast the type of reg->name to 'gpointer' to avoid this problem.

compiler warning message:
/root/qemu/contrib/plugins/execlog.c:330:17: warning:

‘g_pattern_match_string’

is deprecated: Use 'g_pattern_spec_match_string'
instead [-Wdeprecated-declarations]
   330 | if (g_pattern_match_string(pat, rd->name) ||
   | ^~
In file included from /usr/include/glib-2.0/glib.h:67,
  from /root/qemu/contrib/plugins/execlog.c:9:
/usr/include/glib-2.0/glib/gpattern.h:57:15: note: declared here
57 | gboolean  g_pattern_match_string   (GPatternSpec *pspec,
   |   ^~
/root/qemu/contrib/plugins/execlog.c:331:21: warning:

‘g_pattern_match_string’

is deprecated: Use 'g_pattern_spec_match_string'
instead [-Wdeprecated-declarations]
   331 | g_pattern_match_string(pat, rd_lower)) {
   | ^~
/usr/include/glib-2.0/glib/gpattern.h:57:15: note: declared here
57 | gboolean  g_pattern_match_string   (GPatternSpec *pspec,
   |   ^~
/root/qemu/contrib/plugins/execlog.c:339:63: warning: passing argument
2 of ‘g_ptr_array_add’ discards ‘const’ qualifier from pointer target
type [-Wdiscarded-qualifiers]
   339 | g_ptr_array_add(all_reg_names,

reg->name);

   |

~~~^~

In file included from /usr/include/glib-2.0/glib.h:33:
/usr/include/glib-2.0/glib/garray.h:198:62: note: expected ‘gpointer’
{aka ‘void *’} but argument is of type ‘const char *’
   198 |gpointer

data);

   |

~~^~~~




Hi; thanks for this patch.

This fixes a bug reported in the bug tracker so we should put

Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2210

in the commit message just above your signed-off-by tag.



Signed-off-by: Yao Xingtao 

I will if needed.


---
  contrib/plugins/execlog.c | 7 ++-
  1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/contrib/plugins/execlog.c b/contrib/plugins/execlog.c
index a1dfd59ab7..41f6774116 100644
--- a/contrib/plugins/execlog.c
+++ b/contrib/plugins/execlog.c
@@ -327,8 +327,13 @@ static GPtrArray *registers_init(int vcpu_index)
  for (int p = 0; p < rmatches->len; p++) {
  g_autoptr(GPatternSpec) pat =

g_pattern_spec_new(rmatches->pdata[p]);

  g_autofree gchar *rd_lower = g_utf8_strdown(rd->name,
-1);
+#if GLIB_VERSION_MAX_ALLOWED < G_ENCODE_VERSION(2, 70)


Elsewhere we do glib version checks with

#if GLIB_CHECK_VERSION(2, 70, 0)
 code for 2.70.0 and up;
#else
 code for older versions
#endif

so I think we should probably do that here too.


  if (g_pattern_match_string(pat, rd->name) ||
  g_pattern_match_string(pat, rd_lower)) {
+#else
+if (g_pattern_spec_match_string(pat, rd->name) ||
+g_pattern_spec_match_string(pat, rd_lower)) {
+#endif

thanks, I got it.



Rather than putting this ifdef in the middle of this function, I think it would 
be
easier to read if we abstract out a function which does the pattern matching
and whose body calls the right glib function depending on glib version. We
generally call these functions the same as the glib function but with a _qemu
suffix (compare the ones in include/glib-compat.h), so here that would be
g_pattern_spec_match_string_qemu().


  Register *reg = init_vcpu_register(rd);
  g_ptr_array_add(registers, reg);

@@ -336,7 +341,7 @@ static GPtrArray *registers_init(int vcpu_index)
  if (disas_assist) {
  

Re: [PATCH 07/22] plugins: Use emit_before_op for PLUGIN_GEN_AFTER_INSN

2024-03-19 Thread Pierrick Bouvier

On 3/19/24 23:56, Richard Henderson wrote:

On 3/19/24 03:32, Pierrick Bouvier wrote:

   static void plugin_gen_inject(struct qemu_plugin_tb *plugin_tb)
   {
-    TCGOp *op;
+    TCGOp *op, *next;
   int insn_idx = -1;
   pr_ops();
-    QTAILQ_FOREACH(op, _ctx->ops, link) {
+    /*
+ * While injecting code, we cannot afford to reuse any ebb temps
+ * that might be live within the existing opcode stream.
+ * The simplest solution is to release them all and create new.
+ */
+    memset(tcg_ctx->free_temps, 0, sizeof(tcg_ctx->free_temps));
+


Not an expert at this, but wouldn't that break an existing TB that already has 
some ops on
those temps?


No, this only affects allocation of new temps -- if free_temps is empty, a new 
temp will
be allocated from tcg_ctx->nb_temps++.

Zeroing free_temps here ensures that we *do not* reuse a temp that might 
already be live
across any plugin insertion point.  Between insertion points, we will free 
plugin temps
and only reuse those.



Thanks, by looking at tcg_temp_new_internal fn, and with your 
explaination, it makes more sense.




r~


  1   2   3   4   >