Signed-off-by: Lluís Vilanova <vilan...@ac.upc.edu> --- Makefile.objs | 1 include/qom/cpu.h | 5 + qapi/trace.json | 31 +++++++ qmp-commands.hx | 27 ++++++ qom/cpu.c | 12 +++ scripts/tracetool/format/tcg_h.py | 6 + scripts/tracetool/format/tcg_helper_c.py | 11 ++- trace/Makefile.objs | 2 trace/control-internal.h | 17 +++- trace/control-stub.c | 29 +++++++ trace/control-target.c | 53 +++++++++++++ trace/control.h | 53 ++++++++++++- trace/qmp.c | 127 ++++++++++++++++++++++++++++-- translate-all.c | 1 ui/Makefile.objs | 2 15 files changed, 357 insertions(+), 20 deletions(-) create mode 100644 trace/control-stub.c create mode 100644 trace/control-target.c
diff --git a/Makefile.objs b/Makefile.objs index e1d7b25..99f9d2a 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -100,6 +100,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/include/qom/cpu.h b/include/qom/cpu.h index 743e13b..94a4c1d 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); @@ -316,6 +317,10 @@ struct CPUState { unsigned int tb_phys_idx; unsigned int tb_phys_idx_req; + /* Ensure 'tb_phys_idx' can encode event states as a bitmask */ + 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/qapi/trace.json b/qapi/trace.json index 94a3a3b..369f4aa 100644 --- a/qapi/trace.json +++ b/qapi/trace.json @@ -64,3 +64,34 @@ ## { 'command': 'trace-event-set-state', 'data': {'name': 'str', 'enable': 'bool', '*ignore-unavailable': 'bool'} } + +## +# @trace-event-get-cpu-state: +# +# Query the state of events in a given vCPU. +# +# @name: Event name pattern. +# @vcpu: The vCPU to check. +# +# Returns: @TraceEventInfo of the matched events +# +# Since 2.2 +## +{ 'command': 'trace-event-get-cpu-state', + 'data': {'name': 'str', 'vcpu': 'int'}, + 'returns': ['TraceEventInfo'] } + +## +# @trace-event-set-cpu-state: +# +# Set the dynamic state of events in a given vCPU. +# +# @name: Event name pattern. +# @vcpu: The vCPU to act upon. +# @enable: Whether to enable tracing. +# @ignore-unavailable: #optional Do not match unavailable events with @name. +# +# Since 2.2 +## +{ 'command': 'trace-event-set-cpu-state', + 'data': {'name': 'str', 'vcpu': 'int', 'enable': 'bool', '*ignore-unavailable': 'bool'} } diff --git a/qmp-commands.hx b/qmp-commands.hx index d2ba800..b6b55af 100644 --- a/qmp-commands.hx +++ b/qmp-commands.hx @@ -4189,6 +4189,33 @@ Move mouse pointer to absolute coordinates (20000, 400). { "type": "abs", "data" : { "axis": "X", "value" : 20000 } }, { "type": "abs", "data" : { "axis": "Y", "value" : 400 } } ] } } <- { "return": {} } +EQMP + + { + .name = "trace-event-get-cpu-state", + .args_type = "name:s,vcpu:i", + .mhandler.cmd_new = qmp_marshal_trace_event_get_cpu_state, + }, + +SQMP +trace-event-get-state +--------------------- + +Query the state of events in a given vCPU. + +EQMP + + { + .name = "trace-event-set-cpu-state", + .args_type = "name:s,vcpu:i,enable:b,ignore-unavailable:b?", + .mhandler.cmd_new = qmp_marshal_trace_event_set_cpu_state, + }, + +SQMP +trace-event-set-state +--------------------- + +Set the state of events in a given vCPU. EQMP diff --git a/qom/cpu.c b/qom/cpu.c index bb7a618..bc27381 100644 --- a/qom/cpu.c +++ b/qom/cpu.c @@ -25,6 +25,7 @@ #include "qemu/log.h" #include "qemu/error-report.h" #include "sysemu/sysemu.h" +#include "trace/control.h" bool cpu_exists(int64_t id) { @@ -227,6 +228,17 @@ void cpu_dump_statistics(CPUState *cpu, FILE *f, fprintf_function cpu_fprintf, void cpu_reset(CPUState *cpu) { CPUClass *klass = CPU_GET_CLASS(cpu); + TraceEvent *ev = NULL; + + if (!qemu_initialized) { + /* trace enabled events on all initial vCPUs */ + 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); + } + } + } if (klass->reset != NULL) { (*klass->reset)(cpu); diff --git a/scripts/tracetool/format/tcg_h.py b/scripts/tracetool/format/tcg_h.py index 222002c..7a9254f 100644 --- a/scripts/tracetool/format/tcg_h.py +++ b/scripts/tracetool/format/tcg_h.py @@ -52,7 +52,11 @@ def generate(events, backend): if "disable" not in e.properties: out(' %(name_trans)s(%(argnames_trans)s);', - ' gen_helper_%(name_exec)s(%(argnames_exec)s);', + ' if (%(cond)s) {', + ' gen_helper_%(name_exec)s(%(argnames_exec)s);', + ' }', + cond='trace_event_get_cpu_state(_cpu, TRACE_%s_TRANS)' % e.name.upper() + if "vcpu" in e.properties else "true", name_trans=e.event_trans.api(e.QEMU_TRACE), name_exec=e.event_exec.api(e.QEMU_TRACE), argnames_trans=", ".join(e.event_trans.args.names()), diff --git a/scripts/tracetool/format/tcg_helper_c.py b/scripts/tracetool/format/tcg_helper_c.py index 96655a0..0cb7f45 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 <vilan...@ac.upc.edu>" -__copyright__ = "Copyright 2012-2014, Lluís Vilanova <vilan...@ac.upc.edu>" +__copyright__ = "Copyright 2012-2015, Lluís Vilanova <vilan...@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 32f7a32..15896de 100644 --- a/trace/Makefile.objs +++ b/trace/Makefile.objs @@ -144,4 +144,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 70e55df..b4069e3 100644 --- a/trace/control-internal.h +++ b/trace/control-internal.h @@ -12,6 +12,12 @@ #include <string.h> +#include "qemu-common.h" +/* GTK headers conflict with QOM's '_' */ +#if !defined(TRACE_CPU_INCLUDE_HACK) +#include "qom/cpu.h" +#endif + extern TraceEvent trace_events[]; @@ -63,11 +69,16 @@ static inline bool trace_event_get_state_dynamic(TraceEvent *ev) return ev->dstate; } -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) { +#if !defined(TRACE_CPU_INCLUDE_HACK) + 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); +#else + abort(); +#endif } #endif /* TRACE__CONTROL_INTERNAL_H */ diff --git a/trace/control-stub.c b/trace/control-stub.c new file mode 100644 index 0000000..dd62d3b --- /dev/null +++ b/trace/control-stub.c @@ -0,0 +1,29 @@ +/* + * Interface for configuring and controlling the state of tracing events. + * + * Copyright (C) 2014-2015 Lluís Vilanova <vilan...@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..84d3243 --- /dev/null +++ b/trace/control-target.c @@ -0,0 +1,53 @@ +/* + * Interface for configuring and controlling the state of tracing events. + * + * Copyright (C) 2014-2015 Lluís Vilanova <vilan...@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)); + CPU_FOREACH(cpu) { + trace_event_set_cpu_state_dynamic(cpu, ev, state); + } + ev->dstate = state; +} + +void trace_event_set_cpu_state_dynamic(CPUState *cpu, + TraceEvent *ev, bool state) +{ + unsigned int bit; + assert(cpu != NULL); + assert(ev != NULL); + assert(trace_event_get_state_static(ev)); + assert(trace_event_get_cpu_id(ev) != TRACE_CPU_EVENT_COUNT); + if (state) { + ev->dstate = state; + } + bit = ((unsigned long)1) << ev->cpu_id; + cpu_tb_cache_set(cpu, (cpu_tb_cache_get(cpu) & ~bit) | bit); +} diff --git a/trace/control.h b/trace/control.h index c0680c7..be3864a 100644 --- a/trace/control.h +++ b/trace/control.h @@ -13,7 +13,8 @@ #include "qemu-common.h" #include "trace/generated-events.h" - +/* Forward declaration */ +struct CPUState; typedef struct CPUState CPUState; @@ -117,6 +118,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. * @@ -135,6 +152,13 @@ static bool trace_event_get_state_static(TraceEvent *ev); 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). @@ -148,13 +172,36 @@ 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. * * 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); @@ -171,6 +218,8 @@ 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); + #include "trace/control-internal.h" diff --git a/trace/qmp.c b/trace/qmp.c index a669698..9515a91 100644 --- a/trace/qmp.c +++ b/trace/qmp.c @@ -1,7 +1,7 @@ /* * QMP commands for tracing events. * - * Copyright (C) 2014 Lluís Vilanova <vilan...@ac.upc.edu> + * Copyright (C) 2014-2015 Lluís Vilanova <vilan...@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. @@ -44,34 +44,141 @@ TraceEventInfoList *qmp_trace_event_get_state(const char *name, Error **errp) 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) { + continue; + } + *found = true; + if (!trace_event_get_state_static(ev)) { + if (error_check) { + error_setg( + errp, "cannot set dynamic tracing state for \"%s\"\n", + trace_event_get_name(ev)); + *error_found = true; + return; + } + } + } +} + void qmp_trace_event_set_state(const char *name, bool enable, bool has_ignore_unavailable, bool ignore_unavailable, Error **errp) { + bool error, found; + TraceEvent *ev = NULL; + + /* Check all selected events are dynamic */ + check_events_are_dynamic(name, false, &found, + !(has_ignore_unavailable && ignore_unavailable), + &error, errp); + if (error) { + return; + } + if (!found && !trace_event_is_pattern(name)) { + error_setg(errp, "unknown event \"%s\"\n", name); + return; + } + + /* Apply changes */ + ev = NULL; + while ((ev = trace_event_pattern(name, ev)) != NULL) { + if (trace_event_get_state_static(ev)) { + trace_event_set_state_dynamic(ev, enable); + } + } +} + + +static CPUState *get_cpu_state(int index, Error **errp) +{ + CPUState *cpu = qemu_get_cpu(index); + if (cpu == NULL) { + error_setg(errp, "invalid vCPU index %u\n", index); + } + return cpu; +} + +TraceEventInfoList *qmp_trace_event_get_cpu_state(const char *name, + int64_t vcpu, Error **errp) +{ + TraceEventInfoList *events = NULL; bool found = false; TraceEvent *ev; + CPUState *cpu = get_cpu_state(vcpu, errp); + if (!cpu) { + return NULL; + } - /* 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; + TraceEventInfoList *elem; + if (trace_event_get_cpu_id(ev) == TRACE_CPU_EVENT_COUNT) { + 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 ? false : true; + 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; + } + elem->next = events; + events = elem; + found = true; } + if (!found && !trace_event_is_pattern(name)) { - error_setg(errp, "unknown event \"%s\"", name); + error_setg(errp, "unknown or non-vCPU event \"%s\"\n", name); + } + + return events; +} + +void qmp_trace_event_set_cpu_state(const char *name, int64_t vcpu, bool enable, + bool has_ignore_unavailable, + bool ignore_unavailable, Error **errp) +{ + bool error, found; + TraceEvent *ev = NULL; + CPUState *cpu = get_cpu_state(vcpu, errp); + if (!cpu) { + return; + } + + /* Check all selected events are dynamic */ + check_events_are_dynamic(name, true, &found, + !(has_ignore_unavailable && ignore_unavailable), + &error, errp); + if (error) { + return; + } + if (!found && !trace_event_is_pattern(name)) { + error_setg(errp, "unknown or non-vCPU event \"%s\"\n", name); return; } /* Apply changes */ ev = NULL; while ((ev = trace_event_pattern(name, ev)) != NULL) { + if (trace_event_get_cpu_id(ev) == TRACE_CPU_EVENT_COUNT) { + continue; + } if (trace_event_get_state_static(ev)) { - trace_event_set_state_dynamic(ev, enable); + trace_event_set_cpu_state_dynamic(cpu, ev, enable); } } } diff --git a/translate-all.c b/translate-all.c index 5ae64d6..4e4c8e2 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/ui/Makefile.objs b/ui/Makefile.objs index 0034fbb..453b482 100644 --- a/ui/Makefile.objs +++ b/ui/Makefile.objs @@ -34,7 +34,7 @@ common-obj-y += egl-helpers.o common-obj-$(CONFIG_GTK) += gtk-egl.o endif -gtk.o-cflags := $(GTK_CFLAGS) $(VTE_CFLAGS) +gtk.o-cflags := $(GTK_CFLAGS) $(VTE_CFLAGS) -DTRACE_CPU_INCLUDE_HACK gtk-egl.o-cflags := $(GTK_CFLAGS) $(VTE_CFLAGS) $(OPENGL_CFLAGS) shader.o-cflags += $(OPENGL_CFLAGS) console-gl.o-cflags += $(OPENGL_CFLAGS)