@@ -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
@@ -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;
@@ -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 */
@@ -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;
@@ -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) {
@@ -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'} }
@@ -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
{
@@ -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('}')
@@ -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)',
'{',
@@ -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
@@ -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 */
new file mode 100644
@@ -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();
+}
new file mode 100644
@@ -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--;
+ }
+ }
+}
@@ -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);
+ }
+ }
+}
@@ -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"
@@ -28,7 +28,7 @@ typedef struct TraceEvent {
TraceEventCPUID cpu_id;
const char * name;
const bool sstate;
- bool dstate;
+ size_t dstate;
} TraceEvent;
@@ -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);
}
}
@@ -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.
@@ -4659,6 +4659,7 @@ int main(int argc, char **argv, char **envp)
}
}
+ trace_init_vcpu_events();
main_loop();
replay_disable_events();
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