summaryrefslogtreecommitdiffstats
path: root/src/trace
diff options
context:
space:
mode:
Diffstat (limited to 'src/trace')
-rw-r--r--src/trace/Makefile.objs147
-rw-r--r--src/trace/control-internal.h67
-rw-r--r--src/trace/control.c169
-rw-r--r--src/trace/control.h167
-rw-r--r--src/trace/event-internal.h33
-rw-r--r--src/trace/ftrace.c79
-rw-r--r--src/trace/ftrace.h15
-rw-r--r--src/trace/qmp.c75
-rw-r--r--src/trace/simple.c392
-rw-r--r--src/trace/simple.h58
10 files changed, 1202 insertions, 0 deletions
diff --git a/src/trace/Makefile.objs b/src/trace/Makefile.objs
new file mode 100644
index 0000000..32f7a32
--- /dev/null
+++ b/src/trace/Makefile.objs
@@ -0,0 +1,147 @@
+# -*- mode: makefile -*-
+
+######################################################################
+# Auto-generated event descriptions for LTTng ust code
+
+ifeq ($(findstring ust,$(TRACE_BACKENDS)),ust)
+$(obj)/generated-ust-provider.h: $(obj)/generated-ust-provider.h-timestamp
+$(obj)/generated-ust-provider.h-timestamp: $(SRC_PATH)/trace-events
+ $(call quiet-command,$(TRACETOOL) \
+ --format=ust-events-h \
+ --backends=$(TRACE_BACKENDS) \
+ < $< > $@," GEN $(patsubst %-timestamp,%,$@)")
+ @cmp -s $@ $(patsubst %-timestamp,%,$@) || cp $@ $(patsubst %-timestamp,%,$@)
+
+$(obj)/generated-ust.c: $(obj)/generated-ust.c-timestamp $(BUILD_DIR)/config-host.mak
+$(obj)/generated-ust.c-timestamp: $(SRC_PATH)/trace-events
+ $(call quiet-command,$(TRACETOOL) \
+ --format=ust-events-c \
+ --backends=$(TRACE_BACKENDS) \
+ < $< > $@," GEN $(patsubst %-timestamp,%,$@)")
+ @cmp -s $@ $(patsubst %-timestamp,%,$@) || cp $@ $(patsubst %-timestamp,%,$@)
+
+$(obj)/generated-events.h: $(obj)/generated-ust-provider.h
+$(obj)/generated-events.c: $(obj)/generated-ust.c
+endif
+
+######################################################################
+# Auto-generated event descriptions
+
+$(obj)/generated-events.h: $(obj)/generated-events.h-timestamp
+$(obj)/generated-events.h-timestamp: $(SRC_PATH)/trace-events
+ $(call quiet-command,$(TRACETOOL) \
+ --format=events-h \
+ --backends=$(TRACE_BACKENDS) \
+ < $< > $@," GEN $(patsubst %-timestamp,%,$@)")
+ @cmp -s $@ $(patsubst %-timestamp,%,$@) || cp $@ $(patsubst %-timestamp,%,$@)
+
+$(obj)/generated-events.c: $(obj)/generated-events.c-timestamp $(BUILD_DIR)/config-host.mak
+$(obj)/generated-events.c-timestamp: $(SRC_PATH)/trace-events
+ $(call quiet-command,$(TRACETOOL) \
+ --format=events-c \
+ --backends=$(TRACE_BACKENDS) \
+ < $< > $@," GEN $(patsubst %-timestamp,%,$@)")
+ @cmp -s $@ $(patsubst %-timestamp,%,$@) || cp $@ $(patsubst %-timestamp,%,$@)
+
+util-obj-y += generated-events.o
+
+
+######################################################################
+# Auto-generated tracing routines
+
+##################################################
+# Execution level
+
+$(obj)/generated-tracers.h: $(obj)/generated-tracers.h-timestamp
+ @cmp -s $< $@ || cp $< $@
+$(obj)/generated-tracers.h-timestamp: $(SRC_PATH)/trace-events $(BUILD_DIR)/config-host.mak
+ $(call quiet-command,$(TRACETOOL) \
+ --format=h \
+ --backends=$(TRACE_BACKENDS) \
+ < $< > $@," GEN $(patsubst %-timestamp,%,$@)")
+
+##############################
+# non-DTrace
+
+$(obj)/generated-tracers.c: $(obj)/generated-tracers.c-timestamp
+ @cmp -s $< $@ || cp $< $@
+$(obj)/generated-tracers.c-timestamp: $(SRC_PATH)/trace-events $(BUILD_DIR)/config-host.mak
+ $(call quiet-command,$(TRACETOOL) \
+ --format=c \
+ --backends=$(TRACE_BACKENDS) \
+ < $< > $@," GEN $(patsubst %-timestamp,%,$@)")
+
+$(obj)/generated-tracers.o: $(obj)/generated-tracers.c $(obj)/generated-tracers.h
+
+##############################
+# DTrace
+
+# Normal practice is to name DTrace probe file with a '.d' extension
+# but that gets picked up by QEMU's Makefile as an external dependency
+# rule file. So we use '.dtrace' instead
+ifeq ($(findstring dtrace,$(TRACE_BACKENDS)),dtrace)
+$(obj)/generated-tracers-dtrace.dtrace: $(obj)/generated-tracers-dtrace.dtrace-timestamp
+$(obj)/generated-tracers-dtrace.dtrace-timestamp: $(SRC_PATH)/trace-events $(BUILD_DIR)/config-host.mak
+ $(call quiet-command,$(TRACETOOL) \
+ --format=d \
+ --backends=$(TRACE_BACKENDS) \
+ < $< > $@," GEN $(patsubst %-timestamp,%,$@)")
+ @cmp -s $@ $(patsubst %-timestamp,%,$@) || cp $@ $(patsubst %-timestamp,%,$@)
+
+$(obj)/generated-tracers-dtrace.h: $(obj)/generated-tracers-dtrace.dtrace
+ $(call quiet-command,dtrace -o $@ -h -s $<, " GEN $@")
+
+$(obj)/generated-tracers-dtrace.o: $(obj)/generated-tracers-dtrace.dtrace
+
+util-obj-y += generated-tracers-dtrace.o
+endif
+
+##################################################
+# Translation level
+
+$(obj)/generated-helpers-wrappers.h: $(obj)/generated-helpers-wrappers.h-timestamp
+$(obj)/generated-helpers-wrappers.h-timestamp: $(SRC_PATH)/trace-events $(BUILD_DIR)/config-host.mak
+ $(call quiet-command,$(TRACETOOL) \
+ --format=tcg-helper-wrapper-h \
+ --backend=$(TRACE_BACKENDS) \
+ < $< > $@," GEN $(patsubst %-timestamp,%,$@)")
+ @cmp -s $@ $(patsubst %-timestamp,%,$@) || cp $@ $(patsubst %-timestamp,%,$@)
+
+$(obj)/generated-helpers.h: $(obj)/generated-helpers.h-timestamp
+$(obj)/generated-helpers.h-timestamp: $(SRC_PATH)/trace-events $(BUILD_DIR)/config-host.mak
+ $(call quiet-command,$(TRACETOOL) \
+ --format=tcg-helper-h \
+ --backend=$(TRACE_BACKENDS) \
+ < $< > $@," GEN $(patsubst %-timestamp,%,$@)")
+ @cmp -s $@ $(patsubst %-timestamp,%,$@) || cp $@ $(patsubst %-timestamp,%,$@)
+
+$(obj)/generated-helpers.c: $(obj)/generated-helpers.c-timestamp
+$(obj)/generated-helpers.c-timestamp: $(SRC_PATH)/trace-events $(BUILD_DIR)/config-host.mak
+ $(call quiet-command,$(TRACETOOL) \
+ --format=tcg-helper-c \
+ --backend=$(TRACE_BACKENDS) \
+ < $< > $@," GEN $(patsubst %-timestamp,%,$@)")
+ @cmp -s $@ $(patsubst %-timestamp,%,$@) || cp $@ $(patsubst %-timestamp,%,$@)
+
+$(obj)/generated-helpers.o: $(obj)/generated-helpers.c
+
+target-obj-y += generated-helpers.o
+
+
+$(obj)/generated-tcg-tracers.h: $(obj)/generated-tcg-tracers.h-timestamp
+$(obj)/generated-tcg-tracers.h-timestamp: $(SRC_PATH)/trace-events $(BUILD_DIR)/config-host.mak
+ $(call quiet-command,$(TRACETOOL) \
+ --format=tcg-h \
+ --backend=$(TRACE_BACKENDS) \
+ < $< > $@," GEN $(patsubst %-timestamp,%,$@)")
+ @cmp -s $@ $(patsubst %-timestamp,%,$@) || cp $@ $(patsubst %-timestamp,%,$@)
+
+
+######################################################################
+# Backend code
+
+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
+util-obj-y += qmp.o
diff --git a/src/trace/control-internal.h b/src/trace/control-internal.h
new file mode 100644
index 0000000..5a8df28
--- /dev/null
+++ b/src/trace/control-internal.h
@@ -0,0 +1,67 @@
+/*
+ * Interface for configuring and controlling the state of tracing events.
+ *
+ * Copyright (C) 2011-2014 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.
+ */
+
+#ifndef TRACE__CONTROL_INTERNAL_H
+#define TRACE__CONTROL_INTERNAL_H
+
+#include <string.h>
+
+
+extern TraceEvent trace_events[];
+
+
+static inline TraceEventID trace_event_count(void)
+{
+ return TRACE_EVENT_COUNT;
+}
+
+static inline TraceEvent *trace_event_id(TraceEventID id)
+{
+ assert(id < trace_event_count());
+ return &trace_events[id];
+}
+
+static inline bool trace_event_is_pattern(const char *str)
+{
+ assert(str != NULL);
+ return strchr(str, '*') != NULL;
+}
+
+static inline TraceEventID trace_event_get_id(TraceEvent *ev)
+{
+ assert(ev != NULL);
+ return ev->id;
+}
+
+static inline const char * trace_event_get_name(TraceEvent *ev)
+{
+ assert(ev != NULL);
+ return ev->name;
+}
+
+static inline bool trace_event_get_state_static(TraceEvent *ev)
+{
+ assert(ev != NULL);
+ return ev->sstate;
+}
+
+static inline bool trace_event_get_state_dynamic(TraceEvent *ev)
+{
+ assert(ev != NULL);
+ return ev->dstate;
+}
+
+static inline void trace_event_set_state_dynamic(TraceEvent *ev, bool state)
+{
+ assert(ev != NULL);
+ assert(trace_event_get_state_static(ev));
+ ev->dstate = state;
+}
+
+#endif /* TRACE__CONTROL_INTERNAL_H */
diff --git a/src/trace/control.c b/src/trace/control.c
new file mode 100644
index 0000000..995beb3
--- /dev/null
+++ b/src/trace/control.c
@@ -0,0 +1,169 @@
+/*
+ * Interface for configuring and controlling the state of tracing events.
+ *
+ * Copyright (C) 2011-2014 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"
+#ifdef CONFIG_TRACE_SIMPLE
+#include "trace/simple.h"
+#endif
+#ifdef CONFIG_TRACE_FTRACE
+#include "trace/ftrace.h"
+#endif
+#include "qemu/error-report.h"
+
+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);
+ if (strcmp(trace_event_get_name(ev), name) == 0) {
+ return ev;
+ }
+ }
+ return NULL;
+}
+
+static bool pattern_glob(const char *pat, const char *ev)
+{
+ 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;
+ }
+}
+
+TraceEvent *trace_event_pattern(const char *pat, TraceEvent *ev)
+{
+ assert(pat != NULL);
+
+ TraceEventID i;
+
+ if (ev == NULL) {
+ i = -1;
+ } else {
+ i = trace_event_get_id(ev);
+ }
+ i++;
+
+ while (i < trace_event_count()) {
+ TraceEvent *res = trace_event_id(i);
+ if (pattern_glob(pat, trace_event_get_name(res))) {
+ return res;
+ }
+ i++;
+ }
+
+ return NULL;
+}
+
+static void trace_init_events(const char *fname)
+{
+ Location loc;
+ FILE *fp;
+ char line_buf[1024];
+ size_t line_idx = 0;
+
+ if (fname == NULL) {
+ return;
+ }
+
+ loc_push_none(&loc);
+ loc_set_file(fname, 0);
+ fp = fopen(fname, "r");
+ if (!fp) {
+ error_report("%s", strerror(errno));
+ exit(1);
+ }
+ while (fgets(line_buf, sizeof(line_buf), fp)) {
+ loc_set_file(fname, ++line_idx);
+ size_t len = strlen(line_buf);
+ if (len > 1) { /* skip empty lines */
+ line_buf[len - 1] = '\0';
+ if ('#' == line_buf[0]) { /* skip commented lines */
+ continue;
+ }
+ const bool enable = ('-' != line_buf[0]);
+ char *line_ptr = enable ? line_buf : line_buf + 1;
+ 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);
+ }
+ }
+ } 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);
+ }
+ }
+ }
+ }
+ if (fclose(fp) != 0) {
+ loc_set_file(fname, 0);
+ error_report("%s", strerror(errno));
+ exit(1);
+ }
+ loc_pop(&loc);
+}
+
+bool trace_init_backends(const char *events, const char *file)
+{
+#ifdef CONFIG_TRACE_SIMPLE
+ if (!st_init(file)) {
+ fprintf(stderr, "failed to initialize simple tracing backend.\n");
+ return false;
+ }
+#else
+ if (file) {
+ fprintf(stderr, "error: -trace file=...: "
+ "option not supported by the selected tracing backends\n");
+ return false;
+ }
+#endif
+
+#ifdef CONFIG_TRACE_FTRACE
+ if (!ftrace_init()) {
+ fprintf(stderr, "failed to initialize ftrace backend.\n");
+ return false;
+ }
+#endif
+
+ trace_init_events(events);
+ return true;
+}
diff --git a/src/trace/control.h b/src/trace/control.h
new file mode 100644
index 0000000..da9bb6b
--- /dev/null
+++ b/src/trace/control.h
@@ -0,0 +1,167 @@
+/*
+ * Interface for configuring and controlling the state of tracing events.
+ *
+ * Copyright (C) 2011-2014 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.
+ */
+
+#ifndef TRACE__CONTROL_H
+#define TRACE__CONTROL_H
+
+#include "qemu-common.h"
+#include "trace/generated-events.h"
+
+
+/**
+ * TraceEventID:
+ *
+ * Unique tracing event identifier.
+ *
+ * These are named as 'TRACE_${EVENT_NAME}'.
+ *
+ * See also: "trace/generated-events.h"
+ */
+enum TraceEventID;
+
+/**
+ * trace_event_id:
+ * @id: Event identifier.
+ *
+ * Get an event by its identifier.
+ *
+ * This routine has a constant cost, as opposed to trace_event_name and
+ * trace_event_pattern.
+ *
+ * Pre-conditions: The identifier is valid.
+ *
+ * Returns: pointer to #TraceEvent.
+ *
+ */
+static TraceEvent *trace_event_id(TraceEventID id);
+
+/**
+ * trace_event_name:
+ * @id: Event name.
+ *
+ * Search an event by its name.
+ *
+ * Returns: pointer to #TraceEvent or NULL if not found.
+ */
+TraceEvent *trace_event_name(const char *name);
+
+/**
+ * trace_event_pattern:
+ * @pat: Event name pattern.
+ * @ev: Event to start searching from (not included).
+ *
+ * Get all events with a given name pattern.
+ *
+ * Returns: pointer to #TraceEvent or NULL if not found.
+ */
+TraceEvent *trace_event_pattern(const char *pat, TraceEvent *ev);
+
+/**
+ * trace_event_is_pattern:
+ *
+ * Whether the given string is an event name pattern.
+ */
+static bool trace_event_is_pattern(const char *str);
+
+/**
+ * trace_event_count:
+ *
+ * Return the number of events.
+ */
+static TraceEventID trace_event_count(void);
+
+
+
+/**
+ * trace_event_get_id:
+ *
+ * Get the identifier of an event.
+ */
+static TraceEventID trace_event_get_id(TraceEvent *ev);
+
+/**
+ * trace_event_get_name:
+ *
+ * Get the name of an event.
+ */
+static const char * trace_event_get_name(TraceEvent *ev);
+
+/**
+ * trace_event_get_state:
+ * @id: Event identifier.
+ *
+ * Get the tracing state of an event (both static and dynamic).
+ *
+ * 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_state(id) \
+ ((id ##_ENABLED) && trace_event_get_state_dynamic(trace_event_id(id)))
+
+/**
+ * trace_event_get_state_static:
+ * @id: Event identifier.
+ *
+ * Get the static tracing state of an event.
+ *
+ * Use the define 'TRACE_${EVENT_NAME}_ENABLED' for compile-time checks (it will
+ * be set to 1 or 0 according to the presence of the disabled property).
+ */
+static bool trace_event_get_state_static(TraceEvent *ev);
+
+/**
+ * trace_event_get_state_dynamic:
+ *
+ * Get the dynamic tracing state of an event.
+ */
+static bool trace_event_get_state_dynamic(TraceEvent *ev);
+
+/**
+ * trace_event_set_state:
+ *
+ * Set the tracing state of an event (only if possible).
+ */
+#define trace_event_set_state(id, state) \
+ do { \
+ if ((id ##_ENABLED)) { \
+ TraceEvent *_e = trace_event_id(id); \
+ trace_event_set_state_dynamic(_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);
+
+
+
+/**
+ * trace_init_backends:
+ * @events: Name of file with events to be enabled at startup; may be NULL.
+ * Corresponds to commandline option "-trace events=...".
+ * @file: Name of trace output file; may be NULL.
+ * Corresponds to commandline option "-trace file=...".
+ *
+ * Initialize the tracing backend.
+ *
+ * Returns: Whether the backends could be successfully initialized.
+ */
+bool trace_init_backends(const char *events, const char *file);
+
+
+#include "trace/control-internal.h"
+
+#endif /* TRACE__CONTROL_H */
diff --git a/src/trace/event-internal.h b/src/trace/event-internal.h
new file mode 100644
index 0000000..b2310d9
--- /dev/null
+++ b/src/trace/event-internal.h
@@ -0,0 +1,33 @@
+/*
+ * Interface for configuring and controlling the state of tracing events.
+ *
+ * Copyright (C) 2012 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.
+ */
+
+#ifndef TRACE__EVENT_INTERNAL_H
+#define TRACE__EVENT_INTERNAL_H
+
+#include "trace/generated-events.h"
+
+
+/**
+ * TraceEvent:
+ * @id: Unique event identifier.
+ * @name: Event name.
+ * @sstate: Static tracing state.
+ * @dstate: Dynamic tracing state.
+ *
+ * Opaque generic description of a tracing event.
+ */
+typedef struct TraceEvent {
+ TraceEventID id;
+ const char * name;
+ const bool sstate;
+ bool dstate;
+} TraceEvent;
+
+
+#endif /* TRACE__EVENT_INTERNAL_H */
diff --git a/src/trace/ftrace.c b/src/trace/ftrace.c
new file mode 100644
index 0000000..a7ae371
--- /dev/null
+++ b/src/trace/ftrace.c
@@ -0,0 +1,79 @@
+/*
+ * Ftrace trace backend
+ *
+ * Copyright (C) 2013 Hitachi, Ltd.
+ * Created by Eiichi Tsukata <eiichi.tsukata.xh@hitachi.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <limits.h>
+#include "trace.h"
+#include "trace/control.h"
+
+int trace_marker_fd;
+
+static int find_debugfs(char *debugfs)
+{
+ char type[100];
+ FILE *fp;
+
+ fp = fopen("/proc/mounts", "r");
+ if (fp == NULL) {
+ return 0;
+ }
+
+ while (fscanf(fp, "%*s %" STR(PATH_MAX) "s %99s %*s %*d %*d\n",
+ debugfs, type) == 2) {
+ if (strcmp(type, "debugfs") == 0) {
+ break;
+ }
+ }
+ fclose(fp);
+
+ if (strcmp(type, "debugfs") != 0) {
+ return 0;
+ }
+ return 1;
+}
+
+bool ftrace_init(void)
+{
+ char debugfs[PATH_MAX];
+ char path[PATH_MAX];
+ int debugfs_found;
+ int trace_fd = -1;
+
+ debugfs_found = find_debugfs(debugfs);
+ if (debugfs_found) {
+ snprintf(path, PATH_MAX, "%s/tracing/tracing_on", debugfs);
+ trace_fd = open(path, O_WRONLY);
+ if (trace_fd < 0) {
+ perror("Could not open ftrace 'tracing_on' file");
+ return false;
+ } else {
+ if (write(trace_fd, "1", 1) < 0) {
+ perror("Could not write to 'tracing_on' file");
+ close(trace_fd);
+ return false;
+ }
+ close(trace_fd);
+ }
+ snprintf(path, PATH_MAX, "%s/tracing/trace_marker", debugfs);
+ trace_marker_fd = open(path, O_WRONLY);
+ if (trace_marker_fd < 0) {
+ perror("Could not open ftrace 'trace_marker' file");
+ return false;
+ }
+ } else {
+ fprintf(stderr, "debugfs is not mounted\n");
+ return false;
+ }
+
+ return true;
+}
diff --git a/src/trace/ftrace.h b/src/trace/ftrace.h
new file mode 100644
index 0000000..863e052
--- /dev/null
+++ b/src/trace/ftrace.h
@@ -0,0 +1,15 @@
+#ifndef TRACE_FTRACE_H
+#define TRACE_FTRACE_H
+
+#include <stdbool.h>
+
+
+#define MAX_TRACE_STRLEN 512
+#define _STR(x) #x
+#define STR(x) _STR(x)
+
+extern int trace_marker_fd;
+
+bool ftrace_init(void);
+
+#endif /* ! TRACE_FTRACE_H */
diff --git a/src/trace/qmp.c b/src/trace/qmp.c
new file mode 100644
index 0000000..0b19489
--- /dev/null
+++ b/src/trace/qmp.c
@@ -0,0 +1,75 @@
+/*
+ * QMP commands for tracing events.
+ *
+ * Copyright (C) 2014 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/typedefs.h"
+#include "qmp-commands.h"
+#include "trace/control.h"
+
+
+TraceEventInfoList *qmp_trace_event_get_state(const char *name, Error **errp)
+{
+ TraceEventInfoList *events = NULL;
+ bool found = false;
+ TraceEvent *ev;
+
+ ev = NULL;
+ while ((ev = trace_event_pattern(name, ev)) != NULL) {
+ TraceEventInfoList *elem = g_new(TraceEventInfoList, 1);
+ elem->value = g_new(TraceEventInfo, 1);
+ 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);
+ }
+
+ return events;
+}
+
+void qmp_trace_event_set_state(const char *name, bool enable,
+ bool has_ignore_unavailable,
+ bool ignore_unavailable, Error **errp)
+{
+ bool found = false;
+ TraceEvent *ev;
+
+ /* 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;
+ }
+ }
+ if (!found && !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)) {
+ trace_event_set_state_dynamic(ev, enable);
+ }
+ }
+}
diff --git a/src/trace/simple.c b/src/trace/simple.c
new file mode 100644
index 0000000..56a624c
--- /dev/null
+++ b/src/trace/simple.c
@@ -0,0 +1,392 @@
+/*
+ * Simple trace backend
+ *
+ * Copyright IBM, Corp. 2010
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <time.h>
+#ifndef _WIN32
+#include <signal.h>
+#include <pthread.h>
+#endif
+#include "qemu/timer.h"
+#include "trace.h"
+#include "trace/control.h"
+#include "trace/simple.h"
+
+/** Trace file header event ID */
+#define HEADER_EVENT_ID (~(uint64_t)0) /* avoids conflicting with TraceEventIDs */
+
+/** Trace file magic number */
+#define HEADER_MAGIC 0xf2b177cb0aa429b4ULL
+
+/** Trace file version number, bump if format changes */
+#define HEADER_VERSION 3
+
+/** Records were dropped event ID */
+#define DROPPED_EVENT_ID (~(uint64_t)0 - 1)
+
+/** Trace record is valid */
+#define TRACE_RECORD_VALID ((uint64_t)1 << 63)
+
+/*
+ * Trace records are written out by a dedicated thread. The thread waits for
+ * records to become available, writes them out, and then waits again.
+ */
+static CompatGMutex trace_lock;
+static CompatGCond trace_available_cond;
+static CompatGCond trace_empty_cond;
+
+static bool trace_available;
+static bool trace_writeout_enabled;
+
+enum {
+ TRACE_BUF_LEN = 4096 * 64,
+ TRACE_BUF_FLUSH_THRESHOLD = TRACE_BUF_LEN / 4,
+};
+
+uint8_t trace_buf[TRACE_BUF_LEN];
+static volatile gint trace_idx;
+static unsigned int writeout_idx;
+static volatile gint dropped_events;
+static uint32_t trace_pid;
+static FILE *trace_fp;
+static char *trace_file_name;
+
+/* * Trace buffer entry */
+typedef struct {
+ uint64_t event; /* TraceEventID */
+ uint64_t timestamp_ns;
+ uint32_t length; /* in bytes */
+ uint32_t pid;
+ uint64_t arguments[];
+} TraceRecord;
+
+typedef struct {
+ uint64_t header_event_id; /* HEADER_EVENT_ID */
+ uint64_t header_magic; /* HEADER_MAGIC */
+ uint64_t header_version; /* HEADER_VERSION */
+} TraceLogHeader;
+
+
+static void read_from_buffer(unsigned int idx, void *dataptr, size_t size);
+static unsigned int write_to_buffer(unsigned int idx, void *dataptr, size_t size);
+
+static void clear_buffer_range(unsigned int idx, size_t len)
+{
+ uint32_t num = 0;
+ while (num < len) {
+ if (idx >= TRACE_BUF_LEN) {
+ idx = idx % TRACE_BUF_LEN;
+ }
+ trace_buf[idx++] = 0;
+ num++;
+ }
+}
+/**
+ * Read a trace record from the trace buffer
+ *
+ * @idx Trace buffer index
+ * @record Trace record to fill
+ *
+ * Returns false if the record is not valid.
+ */
+static bool get_trace_record(unsigned int idx, TraceRecord **recordptr)
+{
+ uint64_t event_flag = 0;
+ TraceRecord record;
+ /* read the event flag to see if its a valid record */
+ read_from_buffer(idx, &record, sizeof(event_flag));
+
+ if (!(record.event & TRACE_RECORD_VALID)) {
+ return false;
+ }
+
+ smp_rmb(); /* read memory barrier before accessing record */
+ /* read the record header to know record length */
+ read_from_buffer(idx, &record, sizeof(TraceRecord));
+ *recordptr = malloc(record.length); /* dont use g_malloc, can deadlock when traced */
+ /* make a copy of record to avoid being overwritten */
+ read_from_buffer(idx, *recordptr, record.length);
+ smp_rmb(); /* memory barrier before clearing valid flag */
+ (*recordptr)->event &= ~TRACE_RECORD_VALID;
+ /* clear the trace buffer range for consumed record otherwise any byte
+ * with its MSB set may be considered as a valid event id when the writer
+ * thread crosses this range of buffer again.
+ */
+ clear_buffer_range(idx, record.length);
+ return true;
+}
+
+/**
+ * Kick writeout thread
+ *
+ * @wait Whether to wait for writeout thread to complete
+ */
+static void flush_trace_file(bool wait)
+{
+ g_mutex_lock(&trace_lock);
+ trace_available = true;
+ g_cond_signal(&trace_available_cond);
+
+ if (wait) {
+ g_cond_wait(&trace_empty_cond, &trace_lock);
+ }
+
+ g_mutex_unlock(&trace_lock);
+}
+
+static void wait_for_trace_records_available(void)
+{
+ g_mutex_lock(&trace_lock);
+ while (!(trace_available && trace_writeout_enabled)) {
+ g_cond_signal(&trace_empty_cond);
+ g_cond_wait(&trace_available_cond, &trace_lock);
+ }
+ trace_available = false;
+ g_mutex_unlock(&trace_lock);
+}
+
+static gpointer writeout_thread(gpointer opaque)
+{
+ TraceRecord *recordptr;
+ union {
+ TraceRecord rec;
+ uint8_t bytes[sizeof(TraceRecord) + sizeof(uint64_t)];
+ } dropped;
+ unsigned int idx = 0;
+ int dropped_count;
+ size_t unused __attribute__ ((unused));
+
+ for (;;) {
+ wait_for_trace_records_available();
+
+ if (g_atomic_int_get(&dropped_events)) {
+ dropped.rec.event = DROPPED_EVENT_ID,
+ dropped.rec.timestamp_ns = get_clock();
+ dropped.rec.length = sizeof(TraceRecord) + sizeof(uint64_t),
+ dropped.rec.pid = trace_pid;
+ do {
+ dropped_count = g_atomic_int_get(&dropped_events);
+ } while (!g_atomic_int_compare_and_exchange(&dropped_events,
+ dropped_count, 0));
+ dropped.rec.arguments[0] = dropped_count;
+ unused = fwrite(&dropped.rec, dropped.rec.length, 1, trace_fp);
+ }
+
+ while (get_trace_record(idx, &recordptr)) {
+ unused = fwrite(recordptr, recordptr->length, 1, trace_fp);
+ writeout_idx += recordptr->length;
+ free(recordptr); /* dont use g_free, can deadlock when traced */
+ idx = writeout_idx % TRACE_BUF_LEN;
+ }
+
+ fflush(trace_fp);
+ }
+ return NULL;
+}
+
+void trace_record_write_u64(TraceBufferRecord *rec, uint64_t val)
+{
+ rec->rec_off = write_to_buffer(rec->rec_off, &val, sizeof(uint64_t));
+}
+
+void trace_record_write_str(TraceBufferRecord *rec, const char *s, uint32_t slen)
+{
+ /* Write string length first */
+ rec->rec_off = write_to_buffer(rec->rec_off, &slen, sizeof(slen));
+ /* Write actual string now */
+ rec->rec_off = write_to_buffer(rec->rec_off, (void*)s, slen);
+}
+
+int trace_record_start(TraceBufferRecord *rec, TraceEventID event, size_t datasize)
+{
+ unsigned int idx, rec_off, old_idx, new_idx;
+ uint32_t rec_len = sizeof(TraceRecord) + datasize;
+ uint64_t event_u64 = event;
+ uint64_t timestamp_ns = get_clock();
+
+ do {
+ old_idx = g_atomic_int_get(&trace_idx);
+ smp_rmb();
+ new_idx = old_idx + rec_len;
+
+ if (new_idx - writeout_idx > TRACE_BUF_LEN) {
+ /* Trace Buffer Full, Event dropped ! */
+ g_atomic_int_inc(&dropped_events);
+ return -ENOSPC;
+ }
+ } while (!g_atomic_int_compare_and_exchange(&trace_idx, old_idx, new_idx));
+
+ idx = old_idx % TRACE_BUF_LEN;
+
+ rec_off = idx;
+ rec_off = write_to_buffer(rec_off, &event_u64, sizeof(event_u64));
+ rec_off = write_to_buffer(rec_off, &timestamp_ns, sizeof(timestamp_ns));
+ rec_off = write_to_buffer(rec_off, &rec_len, sizeof(rec_len));
+ rec_off = write_to_buffer(rec_off, &trace_pid, sizeof(trace_pid));
+
+ rec->tbuf_idx = idx;
+ rec->rec_off = (idx + sizeof(TraceRecord)) % TRACE_BUF_LEN;
+ return 0;
+}
+
+static void read_from_buffer(unsigned int idx, void *dataptr, size_t size)
+{
+ uint8_t *data_ptr = dataptr;
+ uint32_t x = 0;
+ while (x < size) {
+ if (idx >= TRACE_BUF_LEN) {
+ idx = idx % TRACE_BUF_LEN;
+ }
+ data_ptr[x++] = trace_buf[idx++];
+ }
+}
+
+static unsigned int write_to_buffer(unsigned int idx, void *dataptr, size_t size)
+{
+ uint8_t *data_ptr = dataptr;
+ uint32_t x = 0;
+ while (x < size) {
+ if (idx >= TRACE_BUF_LEN) {
+ idx = idx % TRACE_BUF_LEN;
+ }
+ trace_buf[idx++] = data_ptr[x++];
+ }
+ return idx; /* most callers wants to know where to write next */
+}
+
+void trace_record_finish(TraceBufferRecord *rec)
+{
+ TraceRecord record;
+ read_from_buffer(rec->tbuf_idx, &record, sizeof(TraceRecord));
+ smp_wmb(); /* write barrier before marking as valid */
+ record.event |= TRACE_RECORD_VALID;
+ write_to_buffer(rec->tbuf_idx, &record, sizeof(TraceRecord));
+
+ if (((unsigned int)g_atomic_int_get(&trace_idx) - writeout_idx)
+ > TRACE_BUF_FLUSH_THRESHOLD) {
+ flush_trace_file(false);
+ }
+}
+
+void st_set_trace_file_enabled(bool enable)
+{
+ if (enable == !!trace_fp) {
+ return; /* no change */
+ }
+
+ /* Halt trace writeout */
+ flush_trace_file(true);
+ trace_writeout_enabled = false;
+ flush_trace_file(true);
+
+ if (enable) {
+ static const TraceLogHeader header = {
+ .header_event_id = HEADER_EVENT_ID,
+ .header_magic = HEADER_MAGIC,
+ /* Older log readers will check for version at next location */
+ .header_version = HEADER_VERSION,
+ };
+
+ trace_fp = fopen(trace_file_name, "wb");
+ if (!trace_fp) {
+ return;
+ }
+
+ if (fwrite(&header, sizeof header, 1, trace_fp) != 1) {
+ fclose(trace_fp);
+ trace_fp = NULL;
+ return;
+ }
+
+ /* Resume trace writeout */
+ trace_writeout_enabled = true;
+ flush_trace_file(false);
+ } else {
+ fclose(trace_fp);
+ trace_fp = NULL;
+ }
+}
+
+/**
+ * Set the name of a trace file
+ *
+ * @file The trace file name or NULL for the default name-<pid> set at
+ * config time
+ */
+bool st_set_trace_file(const char *file)
+{
+ st_set_trace_file_enabled(false);
+
+ g_free(trace_file_name);
+
+ if (!file) {
+ /* Type cast needed for Windows where getpid() returns an int. */
+ trace_file_name = g_strdup_printf(CONFIG_TRACE_FILE, (pid_t)getpid());
+ } else {
+ trace_file_name = g_strdup_printf("%s", file);
+ }
+
+ st_set_trace_file_enabled(true);
+ return true;
+}
+
+void st_print_trace_file_status(FILE *stream, int (*stream_printf)(FILE *stream, const char *fmt, ...))
+{
+ stream_printf(stream, "Trace file \"%s\" %s.\n",
+ trace_file_name, trace_fp ? "on" : "off");
+}
+
+void st_flush_trace_buffer(void)
+{
+ flush_trace_file(true);
+}
+
+/* Helper function to create a thread with signals blocked. Use glib's
+ * portable threads since QEMU abstractions cannot be used due to reentrancy in
+ * the tracer. Also note the signal masking on POSIX hosts so that the thread
+ * does not steal signals when the rest of the program wants them blocked.
+ */
+static GThread *trace_thread_create(GThreadFunc fn)
+{
+ GThread *thread;
+#ifndef _WIN32
+ sigset_t set, oldset;
+
+ sigfillset(&set);
+ pthread_sigmask(SIG_SETMASK, &set, &oldset);
+#endif
+
+ thread = g_thread_new("trace-thread", fn, NULL);
+
+#ifndef _WIN32
+ pthread_sigmask(SIG_SETMASK, &oldset, NULL);
+#endif
+
+ return thread;
+}
+
+bool st_init(const char *file)
+{
+ GThread *thread;
+
+ trace_pid = getpid();
+
+ thread = trace_thread_create(writeout_thread);
+ if (!thread) {
+ fprintf(stderr, "warning: unable to initialize simple trace backend\n");
+ return false;
+ }
+
+ atexit(st_flush_trace_buffer);
+ st_set_trace_file(file);
+ return true;
+}
diff --git a/src/trace/simple.h b/src/trace/simple.h
new file mode 100644
index 0000000..6997996
--- /dev/null
+++ b/src/trace/simple.h
@@ -0,0 +1,58 @@
+/*
+ * Simple trace backend
+ *
+ * Copyright IBM, Corp. 2010
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#ifndef TRACE_SIMPLE_H
+#define TRACE_SIMPLE_H
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdio.h>
+
+#include "trace/generated-events.h"
+
+
+void st_print_trace_file_status(FILE *stream, fprintf_function stream_printf);
+void st_set_trace_file_enabled(bool enable);
+bool st_set_trace_file(const char *file);
+bool st_init(const char *file);
+void st_flush_trace_buffer(void);
+
+typedef struct {
+ unsigned int tbuf_idx;
+ unsigned int rec_off;
+} TraceBufferRecord;
+
+/* Note for hackers: Make sure MAX_TRACE_LEN < sizeof(uint32_t) */
+#define MAX_TRACE_STRLEN 512
+/**
+ * Initialize a trace record and claim space for it in the buffer
+ *
+ * @arglen number of bytes required for arguments
+ */
+int trace_record_start(TraceBufferRecord *rec, TraceEventID id, size_t arglen);
+
+/**
+ * Append a 64-bit argument to a trace record
+ */
+void trace_record_write_u64(TraceBufferRecord *rec, uint64_t val);
+
+/**
+ * Append a string argument to a trace record
+ */
+void trace_record_write_str(TraceBufferRecord *rec, const char *s, uint32_t slen);
+
+/**
+ * Mark a trace record completed
+ *
+ * Don't append any more arguments to the trace record after calling this.
+ */
+void trace_record_finish(TraceBufferRecord *rec);
+
+#endif /* TRACE_SIMPLE_H */
OpenPOWER on IntegriCloud