/*
* 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.
#include "qemu/osdep.h"
#include "trace/control.h"
#include "qemu/help_option.h"
+#include "qemu/option.h"
#ifdef CONFIG_TRACE_SIMPLE
#include "trace/simple.h"
#endif
#ifdef CONFIG_TRACE_LOG
#include "qemu/log.h"
#endif
+#ifdef CONFIG_TRACE_SYSLOG
+#include <syslog.h>
+#endif
#include "qapi/error.h"
#include "qemu/error-report.h"
+#include "qemu/config-file.h"
#include "monitor/monitor.h"
+#include "trace/trace-root.h"
int trace_events_enabled_count;
-bool trace_events_dstate[TRACE_EVENT_COUNT];
+
+typedef struct TraceEventGroup {
+ TraceEvent **events;
+} TraceEventGroup;
+
+static TraceEventGroup *event_groups;
+static size_t nevent_groups;
+static uint32_t next_id;
+static uint32_t next_vcpu_id;
+static bool init_trace_on_startup;
+static char *trace_opts_file;
+
+QemuOptsList qemu_trace_opts = {
+ .name = "trace",
+ .implied_opt_name = "enable",
+ .head = QTAILQ_HEAD_INITIALIZER(qemu_trace_opts.head),
+ .desc = {
+ {
+ .name = "enable",
+ .type = QEMU_OPT_STRING,
+ },
+ {
+ .name = "events",
+ .type = QEMU_OPT_STRING,
+ },{
+ .name = "file",
+ .type = QEMU_OPT_STRING,
+ },
+ { /* end of list */ }
+ },
+};
+
+
+void trace_event_register_group(TraceEvent **events)
+{
+ size_t i;
+ for (i = 0; events[i] != NULL; i++) {
+ events[i]->id = next_id++;
+ }
+ event_groups = g_renew(TraceEventGroup, event_groups, nevent_groups + 1);
+ event_groups[nevent_groups].events = events;
+ nevent_groups++;
+
+#ifdef CONFIG_TRACE_SIMPLE
+ st_init_group(nevent_groups - 1);
+#endif
+}
+
TraceEvent *trace_event_name(const char *name)
{
assert(name != NULL);
- TraceEventID i;
- for (i = 0; i < trace_event_count(); i++) {
- TraceEvent *ev = trace_event_id(i);
+ TraceEventIter iter;
+ TraceEvent *ev;
+ trace_event_iter_init_all(&iter);
+ while ((ev = trace_event_iter_next(&iter)) != NULL) {
if (strcmp(trace_event_get_name(ev), name) == 0) {
return ev;
}
return NULL;
}
-static bool pattern_glob(const char *pat, const char *ev)
+void trace_event_iter_init_all(TraceEventIter *iter)
{
- while (*pat != '\0' && *ev != '\0') {
- if (*pat == *ev) {
- pat++;
- ev++;
- }
- else if (*pat == '*') {
- if (pattern_glob(pat, ev+1)) {
- return true;
- } else if (pattern_glob(pat+1, ev)) {
- return true;
- } else {
- return false;
- }
- } else {
- return false;
- }
- }
-
- while (*pat == '*') {
- pat++;
- }
-
- if (*pat == '\0' && *ev == '\0') {
- return true;
- } else {
- return false;
- }
+ iter->event = 0;
+ iter->group = 0;
+ iter->group_id = -1;
+ iter->pattern = NULL;
}
-TraceEvent *trace_event_pattern(const char *pat, TraceEvent *ev)
+void trace_event_iter_init_pattern(TraceEventIter *iter, const char *pattern)
{
- assert(pat != NULL);
-
- TraceEventID i;
+ trace_event_iter_init_all(iter);
+ iter->pattern = pattern;
+}
- if (ev == NULL) {
- i = -1;
- } else {
- i = trace_event_get_id(ev);
- }
- i++;
+void trace_event_iter_init_group(TraceEventIter *iter, size_t group_id)
+{
+ trace_event_iter_init_all(iter);
+ iter->group_id = group_id;
+}
- while (i < trace_event_count()) {
- TraceEvent *res = trace_event_id(i);
- if (pattern_glob(pat, trace_event_get_name(res))) {
- return res;
+TraceEvent *trace_event_iter_next(TraceEventIter *iter)
+{
+ while (iter->group < nevent_groups &&
+ event_groups[iter->group].events[iter->event] != NULL) {
+ TraceEvent *ev = event_groups[iter->group].events[iter->event];
+ size_t group = iter->group;
+ iter->event++;
+ if (event_groups[iter->group].events[iter->event] == NULL) {
+ iter->event = 0;
+ iter->group++;
+ }
+ if (iter->pattern &&
+ !g_pattern_match_simple(iter->pattern, trace_event_get_name(ev))) {
+ continue;
}
- i++;
+ if (iter->group_id != -1 &&
+ iter->group_id != group) {
+ continue;
+ }
+ return ev;
}
return NULL;
}
-void trace_list_events(void)
+void trace_list_events(FILE *f)
{
- int i;
- for (i = 0; i < trace_event_count(); i++) {
- TraceEvent *res = trace_event_id(i);
- fprintf(stderr, "%s\n", trace_event_get_name(res));
+ TraceEventIter iter;
+ TraceEvent *ev;
+ trace_event_iter_init_all(&iter);
+ while ((ev = trace_event_iter_next(&iter)) != NULL) {
+ fprintf(f, "%s\n", trace_event_get_name(ev));
}
+#ifdef CONFIG_TRACE_DTRACE
+ fprintf(f, "This list of names of trace points may be incomplete "
+ "when using the DTrace/SystemTap backends.\n"
+ "Run 'qemu-trace-stap list %s' to print the full list.\n",
+ g_get_prgname());
+#endif
}
static void do_trace_enable_events(const char *line_buf)
{
const bool enable = ('-' != line_buf[0]);
const char *line_ptr = enable ? line_buf : line_buf + 1;
+ TraceEventIter iter;
+ TraceEvent *ev;
+ bool is_pattern = trace_event_is_pattern(line_ptr);
- if (trace_event_is_pattern(line_ptr)) {
- TraceEvent *ev = NULL;
- while ((ev = trace_event_pattern(line_ptr, ev)) != NULL) {
- if (trace_event_get_state_static(ev)) {
- trace_event_set_state_dynamic(ev, enable);
+ trace_event_iter_init_pattern(&iter, line_ptr);
+ while ((ev = trace_event_iter_next(&iter)) != NULL) {
+ if (!trace_event_get_state_static(ev)) {
+ if (!is_pattern) {
+ warn_report("trace event '%s' is not traceable",
+ line_ptr);
+ return;
}
+ continue;
}
- } else {
- TraceEvent *ev = trace_event_name(line_ptr);
- if (ev == NULL) {
- error_report("WARNING: trace event '%s' does not exist",
- line_ptr);
- } else if (!trace_event_get_state_static(ev)) {
- error_report("WARNING: trace event '%s' is not traceable",
- line_ptr);
- } else {
- trace_event_set_state_dynamic(ev, enable);
+
+ /* start tracing */
+ trace_event_set_state_dynamic(ev, enable);
+ if (!is_pattern) {
+ return;
}
}
+
+ if (!is_pattern) {
+ warn_report("trace event '%s' does not exist",
+ line_ptr);
+ }
}
void trace_enable_events(const char *line_buf)
{
if (is_help_option(line_buf)) {
- trace_list_events();
- if (cur_mon == NULL) {
+ trace_list_events(stdout);
+ if (monitor_cur() == NULL) {
exit(0);
}
} else {
}
}
-void trace_init_events(const char *fname)
+static void trace_init_events(const char *fname)
{
Location loc;
FILE *fp;
loc_pop(&loc);
}
-void trace_init_file(const char *file)
+void trace_init_file(void)
{
#ifdef CONFIG_TRACE_SIMPLE
- st_set_trace_file(file);
+ st_set_trace_file(trace_opts_file);
+ if (init_trace_on_startup) {
+ st_set_trace_file_enabled(true);
+ }
#elif defined CONFIG_TRACE_LOG
- /* If both the simple and the log backends are enabled, "-trace file"
- * only applies to the simple backend; use "-D" for the log backend.
+ /*
+ * If both the simple and the log backends are enabled, "--trace file"
+ * only applies to the simple backend; use "-D" for the log
+ * backend. However we should only override -D if we actually have
+ * something to override it with.
*/
- if (file) {
- qemu_set_log_filename(file, &error_fatal);
+ if (trace_opts_file) {
+ qemu_set_log_filename(trace_opts_file, &error_fatal);
}
#else
- if (file) {
- fprintf(stderr, "error: -trace file=...: "
+ if (trace_opts_file) {
+ fprintf(stderr, "error: --trace file=...: "
"option not supported by the selected tracing backends\n");
exit(1);
}
}
#endif
+#ifdef CONFIG_TRACE_SYSLOG
+ openlog(NULL, LOG_PID, LOG_DAEMON);
+#endif
+
return true;
}
+
+void trace_opt_parse(const char *optstr)
+{
+ QemuOpts *opts = qemu_opts_parse_noisily(qemu_find_opts("trace"),
+ optstr, true);
+ if (!opts) {
+ exit(1);
+ }
+ if (qemu_opt_get(opts, "enable")) {
+ trace_enable_events(qemu_opt_get(opts, "enable"));
+ }
+ trace_init_events(qemu_opt_get(opts, "events"));
+ init_trace_on_startup = true;
+ g_free(trace_opts_file);
+ trace_opts_file = g_strdup(qemu_opt_get(opts, "file"));
+ qemu_opts_del(opts);
+}
+
+uint32_t trace_get_vcpu_event_count(void)
+{
+ return next_vcpu_id;
+}