diff mbox

[v4,8/9] trace: [tcg] Add per-vCPU tracing states for events with the 'vcpu' property

Message ID 145287591675.11400.12508957244344480632.stgit@localhost (mailing list archive)
State New, archived
Headers show

Commit Message

Lluís Vilanova Jan. 15, 2016, 4:38 p.m. UTC
Each event with the 'vcpu' property gets a per-vCPU dynamic tracing state.

The set of enabled events with the 'vcpu' and 'tcg' properties is used
to select a per-vCPU physical TB cache.  The number of events with both
properties is used to select the number of physical TB caches, and a
bitmap of the identifiers of such enabled events is used to select a
physical TB cache.

Signed-off-by: Lluís Vilanova <vilanova@ac.upc.edu>
---
 Makefile.objs                            |    1 
 bsd-user/main.c                          |    2 
 include/qom/cpu.h                        |   10 ++
 linux-user/main.c                        |    2 
 monitor.c                                |    4 -
 qapi/trace.json                          |   11 ++
 qmp-commands.hx                          |   17 +++-
 scripts/tracetool/format/h.py            |    1 
 scripts/tracetool/format/tcg_helper_c.py |   11 ++
 trace/Makefile.objs                      |    2 
 trace/control-internal.h                 |   13 ++-
 trace/control-stub.c                     |   29 ++++++
 trace/control-target.c                   |   69 +++++++++++++++
 trace/control.c                          |   17 +++-
 trace/control.h                          |   55 ++++++++++++
 trace/event-internal.h                   |    2 
 trace/qmp.c                              |  138 +++++++++++++++++++++++++-----
 translate-all.c                          |    1 
 vl.c                                     |    1 
 19 files changed, 344 insertions(+), 42 deletions(-)
 create mode 100644 trace/control-stub.c
 create mode 100644 trace/control-target.c

Comments

Paolo Bonzini Jan. 18, 2016, 10:50 a.m. UTC | #1
On 15/01/2016 17:38, Lluís Vilanova wrote:
> Each event with the 'vcpu' property gets a per-vCPU dynamic tracing state.
> 
> The set of enabled events with the 'vcpu' and 'tcg' properties is used
> to select a per-vCPU physical TB cache.  The number of events with both
> properties is used to select the number of physical TB caches, and a
> bitmap of the identifiers of such enabled events is used to select a
> physical TB cache.
> 
> Signed-off-by: Lluís Vilanova <vilanova@ac.upc.edu>

I may be wrong, but this patch and patches 3+5 seem overengineered.  Why
can't you just flush the TB cache entirely whenever the set of trace
events changes, independent of whether they use the vcpu attribute?
That's a very rare event.

Paolo
Lluís Vilanova Jan. 18, 2016, 11:55 a.m. UTC | #2
Paolo Bonzini writes:

> On 15/01/2016 17:38, Lluís Vilanova wrote:
>> Each event with the 'vcpu' property gets a per-vCPU dynamic tracing state.
>> 
>> The set of enabled events with the 'vcpu' and 'tcg' properties is used
>> to select a per-vCPU physical TB cache.  The number of events with both
>> properties is used to select the number of physical TB caches, and a
>> bitmap of the identifiers of such enabled events is used to select a
>> physical TB cache.
>> 
>> Signed-off-by: Lluís Vilanova <vilanova@ac.upc.edu>

> I may be wrong, but this patch and patches 3+5 seem overengineered.  Why
> can't you just flush the TB cache entirely whenever the set of trace
> events changes, independent of whether they use the vcpu attribute?
> That's a very rare event.

A request for this was discussed some time ago, while the current simple version
was merged. Right now, an event is enabled for al vCPUs. This series allows
enabling events on a per-vCPU basis (e.g., trace memory accesses of a specific
vCPU, or with the necessary logic, trace a specific guest process).

This series contains three different new features that are closely related:

* Identify the guest vCPU that is generating an event (when it has the 'vcpu'
  property).
* Set dynamic event state on a per-vCPU basis (when it has the 'tcg' and 'vcpu'
  properties).
* Use a different virtual TB cache for each vCPU, so that they can efficiently
  share TBs. If vCPUs have different events enabled, those with the disabled
  events won't pay for the extra tracing code generated in the TB.

Would it be better to split these into three separate series? It would probably
make reviewing more manageable.

Also, I have more patches on my queue to start adding meaningful guest code
events (memory access and instruction traces), but they depend on this new
'vcpu' property.


Thanks,
  Lluis
Eric Blake Jan. 19, 2016, 10:06 p.m. UTC | #3
On 01/15/2016 09:38 AM, Lluís Vilanova wrote:
> Each event with the 'vcpu' property gets a per-vCPU dynamic tracing state.
> 
> The set of enabled events with the 'vcpu' and 'tcg' properties is used
> to select a per-vCPU physical TB cache.  The number of events with both
> properties is used to select the number of physical TB caches, and a
> bitmap of the identifiers of such enabled events is used to select a
> physical TB cache.
> 
> Signed-off-by: Lluís Vilanova <vilanova@ac.upc.edu>
> ---

> +++ b/qmp-commands.hx

> @@ -4594,6 +4599,13 @@ trace-event-set-state
>  
>  Set the state of events.
>  
> +Arguments:
> +
> +- "name": Event name pattern (json-string).
> +- "enable": Whether to enable or disable the event (json-bool).
> +- "ignore-unavailable": Whether to ignore errors for events that cannot be changed (json-bool, optional).

Long line; wrap to keep it in 80 columns.
diff mbox

Patch

diff --git a/Makefile.objs b/Makefile.objs
index dac2c02..c268acb 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -103,6 +103,7 @@  version-lobj-$(CONFIG_WIN32) += $(BUILD_DIR)/version.lo
 # tracing
 util-obj-y +=  trace/
 target-obj-y += trace/
+stub-obj-y += trace/
 
 ######################################################################
 # guest agent
diff --git a/bsd-user/main.c b/bsd-user/main.c
index 1ecaeb5..b57f9a9 100644
--- a/bsd-user/main.c
+++ b/bsd-user/main.c
@@ -31,6 +31,7 @@ 
 /* For tb_lock */
 #include "cpu.h"
 #include "tcg.h"
+#include "trace/control.h"
 #include "qemu/timer.h"
 #include "qemu/envlist.h"
 
@@ -1121,6 +1122,7 @@  int main(int argc, char **argv)
         gdbserver_start (gdbstub_port);
         gdb_handlesig(cpu, 0);
     }
+    trace_init_vcpu_events();
     cpu_loop(env);
     /* never exits */
     return 0;
diff --git a/include/qom/cpu.h b/include/qom/cpu.h
index 40962e0..d1cbd7a 100644
--- a/include/qom/cpu.h
+++ b/include/qom/cpu.h
@@ -29,6 +29,7 @@ 
 #include "qemu/queue.h"
 #include "qemu/thread.h"
 #include "qemu/typedefs.h"
+#include "trace/generated-events.h"
 
 typedef int (*WriteCoreDumpFunction)(const void *buf, size_t size,
                                      void *opaque);
@@ -319,6 +320,15 @@  struct CPUState {
     unsigned int tb_phys_idx;
     unsigned int tb_phys_idx_req;
 
+    /*
+     * Ensure 'tb_phys_idx' can encode event states as a bitmask.
+     *
+     * This limits the number of events with the 'tcg' property and *without*
+     * the 'disabled' property to 'sizeof(tb_phys_idx)*8'.
+     */
+    bool too_many_tcg_vcpu_events[
+        TRACE_CPU_EVENT_COUNT > sizeof(unsigned int)*8 ? -1 : 0];
+
     /* TODO Move common fields from CPUArchState here. */
     int cpu_index; /* used by alpha TCG */
     uint32_t halted; /* used by alpha, cris, ppc TCG */
diff --git a/linux-user/main.c b/linux-user/main.c
index ee12035..44f0536 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -30,6 +30,7 @@ 
 #include "qemu-common.h"
 #include "cpu.h"
 #include "tcg.h"
+#include "trace/control.h"
 #include "qemu/timer.h"
 #include "qemu/envlist.h"
 #include "elf.h"
@@ -4654,6 +4655,7 @@  int main(int argc, char **argv, char **envp)
         }
         gdb_handlesig(cpu, 0);
     }
+    trace_init_vcpu_events();
     cpu_loop(env);
     /* never exits */
     return 0;
diff --git a/monitor.c b/monitor.c
index c53a453..7c42e78 100644
--- a/monitor.c
+++ b/monitor.c
@@ -888,7 +888,7 @@  static void hmp_trace_event(Monitor *mon, const QDict *qdict)
     bool new_state = qdict_get_bool(qdict, "option");
     Error *local_err = NULL;
 
-    qmp_trace_event_set_state(tp_name, new_state, true, true, &local_err);
+    qmp_trace_event_set_state(tp_name, new_state, true, true, false, 0, &local_err);
     if (local_err) {
         error_report_err(local_err);
     }
@@ -1047,7 +1047,7 @@  static void hmp_info_cpustats(Monitor *mon, const QDict *qdict)
 
 static void hmp_info_trace_events(Monitor *mon, const QDict *qdict)
 {
-    TraceEventInfoList *events = qmp_trace_event_get_state("*", NULL);
+    TraceEventInfoList *events = qmp_trace_event_get_state("*", false, 0, NULL);
     TraceEventInfoList *elem;
 
     for (elem = events; elem != NULL; elem = elem->next) {
diff --git a/qapi/trace.json b/qapi/trace.json
index 84c34dd..8708dba 100644
--- a/qapi/trace.json
+++ b/qapi/trace.json
@@ -42,13 +42,18 @@ 
 # Query the state of events.
 #
 # @name: Event name pattern (case-sensitive glob).
+# @vcpu: #optional The vCPU to check (any by default; since 2.6).
 #
 # Returns: a list of @TraceEventInfo for the matching events
 #
+# For any event without the "vcpu" property:
+# - If @name is a pattern and @vcpu is set, events are ignored.
+# - If @name is not a pattern and @vcpu is set, an error is raised.
+#
 # Since 2.2
 ##
 { 'command': 'trace-event-get-state',
-  'data': {'name': 'str'},
+  'data': {'name': 'str', '*vcpu': 'int'},
   'returns': ['TraceEventInfo'] }
 
 ##
@@ -59,8 +64,10 @@ 
 # @name: Event name pattern (case-sensitive glob).
 # @enable: Whether to enable tracing.
 # @ignore-unavailable: #optional Do not match unavailable events with @name.
+# @vcpu: #optional The vCPU to act upon (all by default; since 2.6).
 #
 # Since 2.2
 ##
 { 'command': 'trace-event-set-state',
-  'data': {'name': 'str', 'enable': 'bool', '*ignore-unavailable': 'bool'} }
+  'data': {'name': 'str', 'enable': 'bool', '*ignore-unavailable': 'bool',
+           '*vcpu': 'int'} }
diff --git a/qmp-commands.hx b/qmp-commands.hx
index db072a6..ce6a419 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -4566,7 +4566,7 @@  EQMP
 
     {
         .name       = "trace-event-get-state",
-        .args_type  = "name:s",
+        .args_type  = "name:s,vcpu:i?",
         .mhandler.cmd_new = qmp_marshal_trace_event_get_state,
     },
 
@@ -4576,6 +4576,11 @@  trace-event-get-state
 
 Query the state of events.
 
+Arguments:
+
+- "name": Event name pattern (json-string).
+- "vcpu": Specific vCPU to query, any vCPU by default (json-int, optional).
+
 Example:
 
 -> { "execute": "trace-event-get-state", "arguments": { "name": "qemu_memalign" } }
@@ -4584,7 +4589,7 @@  EQMP
 
     {
         .name       = "trace-event-set-state",
-        .args_type  = "name:s,enable:b,ignore-unavailable:b?",
+        .args_type  = "name:s,enable:b,ignore-unavailable:b?,vcpu:i?",
         .mhandler.cmd_new = qmp_marshal_trace_event_set_state,
     },
 
@@ -4594,6 +4599,13 @@  trace-event-set-state
 
 Set the state of events.
 
+Arguments:
+
+- "name": Event name pattern (json-string).
+- "enable": Whether to enable or disable the event (json-bool).
+- "ignore-unavailable": Whether to ignore errors for events that cannot be changed (json-bool, optional).
+- "vcpu": Specific vCPU to set, all vCPUs by default (json-int, optional).
+
 Example:
 
 -> { "execute": "trace-event-set-state", "arguments": { "name": "qemu_memalign", "enable": "true" } }
@@ -4662,7 +4674,6 @@  Move mouse pointer to absolute coordinates (20000, 400).
                { "type": "abs", "data" : { "axis": "X", "value" : 20000 } },
                { "type": "abs", "data" : { "axis": "Y", "value" : 400 } } ] } }
 <- { "return": {} }
-
 EQMP
 
     {
diff --git a/scripts/tracetool/format/h.py b/scripts/tracetool/format/h.py
index 2bd68a2..f98f26b 100644
--- a/scripts/tracetool/format/h.py
+++ b/scripts/tracetool/format/h.py
@@ -36,6 +36,7 @@  def generate(events, backend):
             args=e.args)
 
         if "disable" not in e.properties:
+            # NOTE: See note on 'trace_event_set_cpu_state()'.
             backend.generate(e)
 
         out('}')
diff --git a/scripts/tracetool/format/tcg_helper_c.py b/scripts/tracetool/format/tcg_helper_c.py
index 96655a0..e5dd3fe 100644
--- a/scripts/tracetool/format/tcg_helper_c.py
+++ b/scripts/tracetool/format/tcg_helper_c.py
@@ -6,7 +6,7 @@  Generate trace/generated-helpers.c.
 """
 
 __author__     = "Lluís Vilanova <vilanova@ac.upc.edu>"
-__copyright__  = "Copyright 2012-2014, Lluís Vilanova <vilanova@ac.upc.edu>"
+__copyright__  = "Copyright 2012-2016, Lluís Vilanova <vilanova@ac.upc.edu>"
 __license__    = "GPL version 2 or (at your option) any later version"
 
 __maintainer__ = "Stefan Hajnoczi"
@@ -36,8 +36,13 @@  def generate(events, backend):
         # tracetool.generate always transforms types to host
         e_args = e.original.args
 
-        values = ["(%s)%s" % (t, n)
-                  for t, n in e.args.transform(TCG_2_TCG_HELPER_DEF)]
+        values = []
+        for (t_old, n), (t_new, _) in zip(
+                e.args, e.args.transform(TCG_2_TCG_HELPER_DEF)):
+            if t_old == "CPUState *":
+                values.append("ENV_GET_CPU((CPUArchState*)%s)" % n)
+            else:
+                values.append("(%s)%s" % (t_new, n))
 
         out('void %(name_tcg)s(%(args)s)',
             '{',
diff --git a/trace/Makefile.objs b/trace/Makefile.objs
index 5145b34..a179d02 100644
--- a/trace/Makefile.objs
+++ b/trace/Makefile.objs
@@ -152,4 +152,6 @@  util-obj-$(CONFIG_TRACE_SIMPLE) += simple.o generated-tracers.o
 util-obj-$(CONFIG_TRACE_FTRACE) += ftrace.o
 util-obj-$(CONFIG_TRACE_UST) += generated-ust.o
 util-obj-y += control.o
+target-obj-y += control-target.o
+stub-obj-y += control-stub.o
 util-obj-y += qmp.o
diff --git a/trace/control-internal.h b/trace/control-internal.h
index ab42396..81e757f 100644
--- a/trace/control-internal.h
+++ b/trace/control-internal.h
@@ -12,6 +12,9 @@ 
 
 #include <string.h>
 
+#include "qemu-common.h"
+#include "qom/cpu.h"
+
 
 extern TraceEvent trace_events[];
 
@@ -60,14 +63,16 @@  static inline bool trace_event_get_state_static(TraceEvent *ev)
 static inline bool trace_event_get_state_dynamic(TraceEvent *ev)
 {
     assert(ev != NULL);
-    return ev->dstate;
+    /* no need to iterate over vCPUs, since the global dynamic state is always set */
+    return ev->dstate > 0;
 }
 
-static inline void trace_event_set_state_dynamic(TraceEvent *ev, bool state)
+static inline bool trace_event_get_cpu_state_dynamic(CPUState *cpu,
+                                                     TraceEvent *ev)
 {
+    assert(cpu != NULL);
     assert(ev != NULL);
-    assert(trace_event_get_state_static(ev));
-    ev->dstate = state;
+    return cpu->tb_phys_idx & (((unsigned long)1) << ev->cpu_id);
 }
 
 #endif  /* TRACE__CONTROL_INTERNAL_H */
diff --git a/trace/control-stub.c b/trace/control-stub.c
new file mode 100644
index 0000000..784bc35
--- /dev/null
+++ b/trace/control-stub.c
@@ -0,0 +1,29 @@ 
+/*
+ * Interface for configuring and controlling the state of tracing events.
+ *
+ * Copyright (C) 2014-2016 Lluís Vilanova <vilanova@ac.upc.edu>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "trace/control.h"
+
+
+void trace_init_vcpu_tb_caches(void)
+{
+}
+
+void trace_event_set_state_dynamic(TraceEvent *ev, bool state)
+{
+    assert(ev != NULL);
+    assert(trace_event_get_state_static(ev));
+    ev->dstate = state;
+}
+
+void trace_event_set_cpu_state_dynamic(CPUState *cpu,
+                                       TraceEvent *ev, bool state)
+{
+    /* should never be called on non-target binaries */
+    abort();
+}
diff --git a/trace/control-target.c b/trace/control-target.c
new file mode 100644
index 0000000..c256f54
--- /dev/null
+++ b/trace/control-target.c
@@ -0,0 +1,69 @@ 
+/*
+ * Interface for configuring and controlling the state of tracing events.
+ *
+ * Copyright (C) 2014-2016 Lluís Vilanova <vilanova@ac.upc.edu>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "trace/control.h"
+#include "cpu.h"
+#include "translate-all.h"
+
+
+void trace_init_vcpu_tb_caches(void)
+{
+    unsigned int events = 1;
+    TraceEvent *ev = NULL;
+    while ((ev = trace_event_pattern("*", ev)) != NULL) {
+        if (trace_event_get_cpu_id(ev) == TRACE_CPU_EVENT_COUNT) {
+            continue;
+        }
+        events <<= 1;
+    }
+    tb_caches_set(events);
+    tb_caches_apply();
+}
+
+void trace_event_set_state_dynamic(TraceEvent *ev, bool state)
+{
+    CPUState *cpu;
+    assert(ev != NULL);
+    assert(trace_event_get_state_static(ev));
+    if (trace_event_get_cpu_id(ev) != TRACE_CPU_EVENT_COUNT) {
+        CPU_FOREACH(cpu) {
+            trace_event_set_cpu_state_dynamic(cpu, ev, state);
+        }
+    } else {
+        ev->dstate = state;
+    }
+}
+
+void trace_event_set_cpu_state_dynamic(CPUState *cpu,
+                                       TraceEvent *ev, bool state)
+{
+    /*
+     * NOTE: Does not immediately apply changes on all affected vCPUs, so
+     *       "tcg-exec" events might be generated after being disabled (until
+     *       the vCPU finishes a BBL and checks for event state changes).
+     */
+    unsigned int bit;
+    bool state_pre;
+    assert(cpu != NULL);
+    assert(ev != NULL);
+    assert(trace_event_get_state_static(ev));
+    assert(trace_event_get_cpu_id(ev) != TRACE_CPU_EVENT_COUNT);
+    bit = ((unsigned long)1) << ev->cpu_id;
+    /* must use the requested TB index in case it's not yet synchronized */
+    state_pre = cpu->tb_phys_idx_req & bit;
+    if ((state_pre == 0) != (state == 0)) {
+        if (state) {
+            cpu_tb_cache_set(cpu, (cpu->tb_phys_idx_req & ~bit) | bit);
+            ev->dstate++;
+        } else {
+            cpu_tb_cache_set(cpu, (cpu->tb_phys_idx_req & ~bit));
+            ev->dstate--;
+        }
+    }
+}
diff --git a/trace/control.c b/trace/control.c
index 995beb3..2bd5908 100644
--- a/trace/control.c
+++ b/trace/control.c
@@ -1,7 +1,7 @@ 
 /*
  * Interface for configuring and controlling the state of tracing events.
  *
- * Copyright (C) 2011-2014 Lluís Vilanova <vilanova@ac.upc.edu>
+ * Copyright (C) 2011-2016 Lluís Vilanova <vilanova@ac.upc.edu>
  *
  * This work is licensed under the terms of the GNU GPL, version 2 or later.
  * See the COPYING file in the top-level directory.
@@ -167,3 +167,18 @@  bool trace_init_backends(const char *events, const char *file)
     trace_init_events(events);
     return true;
 }
+
+void trace_init_vcpu_events(void)
+{
+    TraceEvent *ev = NULL;
+    /*
+     * Set per-vCPU tracing state according to initial event state. The calls
+     * here affect al vCPUs in the system.
+     */
+    while ((ev = trace_event_pattern("*", ev)) != NULL) {
+        if (trace_event_get_cpu_id(ev) != TRACE_CPU_EVENT_COUNT &&
+            trace_event_get_state_dynamic(ev)) {
+            trace_event_set_state_dynamic(ev, true);
+        }
+    }
+}
diff --git a/trace/control.h b/trace/control.h
index 860beed..1103a4c 100644
--- a/trace/control.h
+++ b/trace/control.h
@@ -115,6 +115,22 @@  static const char * trace_event_get_name(TraceEvent *ev);
     ((id ##_ENABLED) && trace_event_get_state_dynamic(trace_event_id(id)))
 
 /**
+ * trace_event_get_cpu_state:
+ * @id: Event identifier.
+ *
+ * Get the tracing state of an event (both static and dynamic) for the given
+ * vCPU.
+ *
+ * If the event has the disabled property, the check will have no performance
+ * impact.
+ *
+ * As a down side, you must always use an immediate #TraceEventID value.
+ */
+#define trace_event_get_cpu_state(cpu, id)                              \
+    ((id ##_ENABLED) && trace_event_get_cpu_state_dynamic(cpu,          \
+                                                          trace_event_id(id)))
+
+/**
  * trace_event_get_state_static:
  * @id: Event identifier.
  *
@@ -129,10 +145,19 @@  static bool trace_event_get_state_static(TraceEvent *ev);
  * trace_event_get_state_dynamic:
  *
  * Get the dynamic tracing state of an event.
+ *
+ * If the event has the 'vcpu' property, gets the OR'ed state of all vCPUs.
  */
 static bool trace_event_get_state_dynamic(TraceEvent *ev);
 
 /**
+ * trace_event_get_cpu_state_dynamic:
+ *
+ * Get the dynamic tracing state of an event for the given vCPU.
+ */
+static bool trace_event_get_cpu_state_dynamic(CPUState *cpu, TraceEvent *ev);
+
+/**
  * trace_event_set_state:
  *
  * Set the tracing state of an event (only if possible).
@@ -146,13 +171,38 @@  static bool trace_event_get_state_dynamic(TraceEvent *ev);
     } while (0)
 
 /**
+ * trace_event_set_cpu_state:
+ *
+ * Set the tracing state of an event for the given vCPU (only if possible).
+ */
+#define trace_event_set_cpu_state(cpu, id, state)               \
+    do {                                                        \
+        if ((id ##_ENABLED)) {                                  \
+            TraceEvent *_e = trace_event_id(id);                \
+            trace_event_set_cpu_state_dynamic(cpu, _e, state);  \
+        }                                                       \
+    } while (0)
+
+/**
  * trace_event_set_state_dynamic:
  *
  * Set the dynamic tracing state of an event.
  *
+ * If the event has the 'vcpu' property, sets the state on all vCPUs.
+ *
  * Pre-condition: trace_event_get_state_static(ev) == true
  */
-static void trace_event_set_state_dynamic(TraceEvent *ev, bool state);
+void trace_event_set_state_dynamic(TraceEvent *ev, bool state);
+
+/**
+ * trace_event_set_cpu_state_dynamic:
+ *
+ * Set the dynamic tracing state of an event for the given vCPU.
+ *
+ * Pre-condition: trace_event_get_cpu_state_static(ev) == true
+ */
+void trace_event_set_cpu_state_dynamic(CPUState *cpu,
+                                       TraceEvent *ev, bool state);
 
 
 
@@ -169,6 +219,9 @@  static void trace_event_set_state_dynamic(TraceEvent *ev, bool state);
  */
 bool trace_init_backends(const char *events, const char *file);
 
+void trace_init_vcpu_tb_caches(void);
+void trace_init_vcpu_events(void);
+
 
 #include "trace/control-internal.h"
 
diff --git a/trace/event-internal.h b/trace/event-internal.h
index 258bcde..b785017 100644
--- a/trace/event-internal.h
+++ b/trace/event-internal.h
@@ -28,7 +28,7 @@  typedef struct TraceEvent {
     TraceEventCPUID cpu_id;
     const char * name;
     const bool sstate;
-    bool dstate;
+    size_t dstate;
 } TraceEvent;
 
 
diff --git a/trace/qmp.c b/trace/qmp.c
index be99496..8ee9b70 100644
--- a/trace/qmp.c
+++ b/trace/qmp.c
@@ -12,64 +12,152 @@ 
 #include "trace/control.h"
 
 
-TraceEventInfoList *qmp_trace_event_get_state(const char *name, Error **errp)
+static bool get_cpu_state(bool has_index, int index, CPUState **cpu, Error **errp)
+{
+    if (has_index) {
+        *cpu = qemu_get_cpu(index);
+        if (*cpu == NULL) {
+            error_setg(errp, "invalid vCPU index %u", index);
+            return false;
+        }
+    } else {
+        *cpu = NULL;
+    }
+    return true;
+}
+
+TraceEventInfoList *qmp_trace_event_get_state(const char *name,
+                                              bool has_vcpu, int64_t vcpu,
+                                              Error **errp)
 {
     TraceEventInfoList *events = NULL;
-    bool found = false;
     TraceEvent *ev;
+    bool is_pattern = trace_event_is_pattern(name);
+    CPUState *cpu;
+
+    if (!get_cpu_state(has_vcpu, vcpu, &cpu, errp)) {
+        return NULL;
+    }
 
     ev = NULL;
     while ((ev = trace_event_pattern(name, ev)) != NULL) {
-        TraceEventInfoList *elem = g_new(TraceEventInfoList, 1);
+        TraceEventInfoList *elem;
+        bool is_vcpu = trace_event_get_cpu_id(ev) != TRACE_CPU_EVENT_COUNT;
+        if (has_vcpu && !is_vcpu) {
+            if (!is_pattern) {
+                error_setg(errp, "event \"%s\" is not vCPU-specific", name);
+            }
+            /* else: ignore */
+            continue;
+        }
+
+        elem = g_new(TraceEventInfoList, 1);
         elem->value = g_new(TraceEventInfo, 1);
-        elem->value->vcpu = trace_event_get_cpu_id(ev) != TRACE_CPU_EVENT_COUNT;
+        elem->value->vcpu = is_vcpu;
         elem->value->name = g_strdup(trace_event_get_name(ev));
+
         if (!trace_event_get_state_static(ev)) {
             elem->value->state = TRACE_EVENT_STATE_UNAVAILABLE;
-        } else if (!trace_event_get_state_dynamic(ev)) {
-            elem->value->state = TRACE_EVENT_STATE_DISABLED;
         } else {
-            elem->value->state = TRACE_EVENT_STATE_ENABLED;
+            if (has_vcpu) {
+                if (is_vcpu) {
+                    if (trace_event_get_cpu_state_dynamic(cpu, ev)) {
+                        elem->value->state = TRACE_EVENT_STATE_ENABLED;
+                    } else {
+                        elem->value->state = TRACE_EVENT_STATE_DISABLED;
+                    }
+                }
+                /* else: already handled above */
+            } else {
+                if (trace_event_get_state_dynamic(ev)) {
+                    elem->value->state = TRACE_EVENT_STATE_ENABLED;
+                } else {
+                    elem->value->state = TRACE_EVENT_STATE_DISABLED;
+                }
+            }
         }
         elem->next = events;
         events = elem;
-        found = true;
     }
 
-    if (!found && !trace_event_is_pattern(name)) {
+    if (events == NULL && !is_pattern) {
         error_setg(errp, "unknown event \"%s\"", name);
     }
 
     return events;
 }
 
+
+static void check_events_are_dynamic(const char *name, bool per_cpu,
+                                     bool *found, bool error_check,
+                                     bool *error_found, Error **errp)
+{
+    TraceEvent *ev = NULL;
+    *found = false;
+    *error_found = false;
+    while ((ev = trace_event_pattern(name, ev)) != NULL) {
+        if (per_cpu && trace_event_get_cpu_id(ev) == TRACE_CPU_EVENT_COUNT) {
+            if (error_check) {
+                Error *local_err = NULL;
+                error_setg(&local_err, "event \"%s\" is not vCPU-specific",
+                           trace_event_get_name(ev));
+                error_propagate(errp, local_err);
+                *error_found = true;
+            }
+            continue;
+        }
+        if (!trace_event_get_state_static(ev)) {
+            if (error_check) {
+                Error *local_err = NULL;
+                error_setg(&local_err, "cannot set dynamic tracing state for \"%s\"",
+                           trace_event_get_name(ev));
+                error_propagate(errp, local_err);
+                *error_found = true;
+            }
+            continue;
+        }
+        *found = true;
+    }
+}
+
 void qmp_trace_event_set_state(const char *name, bool enable,
-                               bool has_ignore_unavailable,
-                               bool ignore_unavailable, Error **errp)
+                               bool has_ignore_unavailable, bool ignore_unavailable,
+                               bool has_vcpu, int64_t vcpu,
+                               Error **errp)
 {
-    bool found = false;
-    TraceEvent *ev;
+    bool error, found;
+    TraceEvent *ev = NULL;
+    CPUState *cpu;
+
+    if (!get_cpu_state(has_vcpu, vcpu, &cpu, errp)) {
+        return;
+    }
 
     /* Check all selected events are dynamic */
-    ev = NULL;
-    while ((ev = trace_event_pattern(name, ev)) != NULL) {
-        found = true;
-        if (!(has_ignore_unavailable && ignore_unavailable) &&
-            !trace_event_get_state_static(ev)) {
-            error_setg(errp, "cannot set dynamic tracing state for \"%s\"",
-                       trace_event_get_name(ev));
-            return;
-        }
+    check_events_are_dynamic(name, has_vcpu, &found,
+                             !(has_ignore_unavailable && ignore_unavailable),
+                             &error, errp);
+    if (error) {
+        return;
     }
-    if (!found && !trace_event_is_pattern(name)) {
-        error_setg(errp, "unknown event \"%s\"", name);
+    if (!found) {
+        if (!trace_event_is_pattern(name)) {
+            error_setg(errp, "unknown event \"%s\"", name);
+        }
         return;
     }
 
     /* Apply changes */
     ev = NULL;
     while ((ev = trace_event_pattern(name, ev)) != NULL) {
-        if (trace_event_get_state_static(ev)) {
+        if (!trace_event_get_state_static(ev) ||
+            (has_vcpu && trace_event_get_cpu_id(ev) == TRACE_CPU_EVENT_COUNT)) {
+            /* if it were an error, it was catched by the check above */
+            continue;
+        }
+        if (has_vcpu) {
+            trace_event_set_cpu_state_dynamic(cpu, ev, enable);
+        } else {
             trace_event_set_state_dynamic(ev, enable);
         }
     }
diff --git a/translate-all.c b/translate-all.c
index 2395977..dab44ad 100644
--- a/translate-all.c
+++ b/translate-all.c
@@ -179,6 +179,7 @@  void cpu_gen_init(void)
     tcg_ctx.tb_ctx.tb_phys_hash_size_req = 1;
     tcg_ctx.tb_ctx.tb_phys_hash = NULL;
     tb_caches_apply();
+    trace_init_vcpu_tb_caches();
 }
 
 /* Encode VAL as a signed leb128 sequence at P.
diff --git a/vl.c b/vl.c
index b7a083e..dcba749 100644
--- a/vl.c
+++ b/vl.c
@@ -4659,6 +4659,7 @@  int main(int argc, char **argv, char **envp)
         }
     }
 
+    trace_init_vcpu_events();
     main_loop();
     replay_disable_events();