@@ -104,6 +104,10 @@ util-obj-y += trace/
target-obj-y += trace/
######################################################################
+# hypertrace
+target-obj-y += hypertrace/
+
+######################################################################
# guest agent
# FIXME: a few definitions from qapi-types.o/qapi-visit.o are needed
@@ -31,9 +31,12 @@
#include "tcg.h"
#include "qemu/timer.h"
#include "qemu/envlist.h"
+#include "qemu/error-report.h"
#include "exec/log.h"
#include "trace/control.h"
#include "glib-compat.h"
+#include "hypertrace/user.h"
+
int singlestep;
unsigned long mmap_min_addr;
@@ -694,6 +697,8 @@ static void usage(void)
"-strace log system calls\n"
"-trace [[enable=]<pattern>][,events=<file>][,file=<file>]\n"
" specify tracing options\n"
+ "-hypertrace [[base=]<path>][,max-clients=<uint>]\n"
+ " specify hypertrace options\n"
"\n"
"Environment variables:\n"
"QEMU_STRACE Print system calls and arguments similar to the\n"
@@ -744,6 +749,8 @@ int main(int argc, char **argv)
envlist_t *envlist = NULL;
char *trace_file = NULL;
bsd_type = target_openbsd;
+ char *hypertrace_base = NULL;
+ unsigned int hypertrace_max_clients = 0;
if (argc <= 1)
usage();
@@ -763,6 +770,7 @@ int main(int argc, char **argv)
cpu_model = NULL;
qemu_add_opts(&qemu_trace_opts);
+ qemu_add_opts(&qemu_hypertrace_opts);
optind = 1;
for (;;) {
@@ -853,6 +861,9 @@ int main(int argc, char **argv)
} else if (!strcmp(r, "trace")) {
g_free(trace_file);
trace_file = trace_opt_parse(optarg);
+ } else if (!strcmp(r, "hypertrace")) {
+ g_free(hypertrace_file);
+ hypertrace_opt_parse(optarg, &hypertrace_base, &hypertrace_max_clients);
} else {
usage();
}
@@ -987,6 +998,11 @@ int main(int argc, char **argv)
target_set_brk(info->brk);
syscall_init();
signal_init();
+ if (atexit(hypertrace_fini) != 0) {
+ fprintf(stderr, "error: atexit: %s\n", strerror(errno));
+ abort();
+ }
+ hypertrace_init(hypertrace_base, hypertrace_size);
/* Now that we've loaded the binary, GUEST_BASE is fixed. Delay
generating the prologue until now so that the prologue can take
@@ -21,6 +21,7 @@
#include "qemu.h"
#include "qemu-common.h"
#include "bsd-mman.h"
+#include "hypertrace/user.h"
//#define DEBUG_MMAP
@@ -251,10 +252,17 @@ static abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size)
return addr;
}
-/* NOTE: all the constants are the HOST ones */
abi_long target_mmap(abi_ulong start, abi_ulong len, int prot,
int flags, int fd, abi_ulong offset)
{
+ return target_mmap_cpu(start, len, prot, flags, fd, offset, NULL);
+}
+
+/* NOTE: all the constants are the HOST ones */
+abi_long target_mmap_cpu(abi_ulong start, abi_ulong len, int prot,
+ int flags, int fd, abi_ulong offset,
+ CPUState *cpu)
+{
abi_ulong ret, end, real_start, real_end, retaddr, host_offset, host_len;
unsigned long host_start;
@@ -296,6 +304,10 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int prot,
goto the_end;
real_start = start & qemu_host_page_mask;
+ if (!hypertrace_guest_mmap_check(fd, len, offset)) {
+ goto fail;
+ }
+
if (!(flags & MAP_FIXED)) {
abi_ulong mmap_start;
void *p;
@@ -407,6 +419,7 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int prot,
}
}
the_end1:
+ hypertrace_guest_mmap_apply(fd, g2h(start), cpu);
page_set_flags(start, start + len, prot | PAGE_VALID);
the_end:
#ifdef DEBUG_MMAP
@@ -26,6 +26,7 @@
#include "qemu.h"
#include "qemu-common.h"
+#include "hypertrace/user.h"
//#define DEBUG
@@ -332,6 +333,7 @@ abi_long do_freebsd_syscall(void *cpu_env, int num, abi_long arg1,
_mcleanup();
#endif
gdb_exit(cpu_env, arg1);
+ hypertrace_fini();
/* XXX: should free thread stack and CPU env */
_exit(arg1);
ret = 0; /* avoid warning */
@@ -369,10 +371,11 @@ abi_long do_freebsd_syscall(void *cpu_env, int num, abi_long arg1,
unlock_user(p, arg1, 0);
break;
case TARGET_FREEBSD_NR_mmap:
- ret = get_errno(target_mmap(arg1, arg2, arg3,
- target_to_host_bitmask(arg4, mmap_flags_tbl),
- arg5,
- arg6));
+ ret = get_errno(target_mmap_cpu(arg1, arg2, arg3,
+ target_to_host_bitmask(arg4, mmap_flags_tbl),
+ arg5,
+ arg6,
+ cpu));
break;
case TARGET_FREEBSD_NR_mprotect:
ret = get_errno(target_mprotect(arg1, arg2, arg3));
@@ -430,6 +433,7 @@ abi_long do_netbsd_syscall(void *cpu_env, int num, abi_long arg1,
_mcleanup();
#endif
gdb_exit(cpu_env, arg1);
+ hypertrace_fini();
/* XXX: should free thread stack and CPU env */
_exit(arg1);
ret = 0; /* avoid warning */
@@ -455,10 +459,11 @@ abi_long do_netbsd_syscall(void *cpu_env, int num, abi_long arg1,
unlock_user(p, arg1, 0);
break;
case TARGET_NETBSD_NR_mmap:
- ret = get_errno(target_mmap(arg1, arg2, arg3,
- target_to_host_bitmask(arg4, mmap_flags_tbl),
- arg5,
- arg6));
+ ret = get_errno(target_mmap_cpu(arg1, arg2, arg3,
+ target_to_host_bitmask(arg4, mmap_flags_tbl),
+ arg5,
+ arg6,
+ cpu));
break;
case TARGET_NETBSD_NR_mprotect:
ret = get_errno(target_mprotect(arg1, arg2, arg3));
@@ -505,6 +510,7 @@ abi_long do_openbsd_syscall(void *cpu_env, int num, abi_long arg1,
_mcleanup();
#endif
gdb_exit(cpu_env, arg1);
+ hypertrace_fini();
/* XXX: should free thread stack and CPU env */
_exit(arg1);
ret = 0; /* avoid warning */
@@ -530,10 +536,11 @@ abi_long do_openbsd_syscall(void *cpu_env, int num, abi_long arg1,
unlock_user(p, arg1, 0);
break;
case TARGET_OPENBSD_NR_mmap:
- ret = get_errno(target_mmap(arg1, arg2, arg3,
- target_to_host_bitmask(arg4, mmap_flags_tbl),
- arg5,
- arg6));
+ ret = get_errno(target_mmap_cpu(arg1, arg2, arg3,
+ target_to_host_bitmask(arg4, mmap_flags_tbl),
+ arg5,
+ arg6,
+ cpu));
break;
case TARGET_OPENBSD_NR_mprotect:
ret = get_errno(target_mprotect(arg1, arg2, arg3));
new file mode 100644
@@ -0,0 +1,18 @@
+# -*- mode: makefile -*-
+
+target-obj-$(CONFIG_USER_ONLY) += user.o
+target-obj-y += common.o
+
+$(obj)/user.o: $(obj)/emit.c
+
+$(obj)/emit.c: $(obj)/emit.c-timestamp $(BUILD_DIR)/config-host.mak
+ @cmp $< $@ >/dev/null 2>&1 || cp $< $@
+$(obj)/emit.c-timestamp: $(BUILD_DIR)/config-host.mak
+ @echo "static void hypertrace_emit(CPUState *cpu, uint64_t arg1, uint64_t *data)" >$@
+ @echo "{" >>$@
+ @echo -n " trace_guest_hypertrace(cpu, arg1" >>$@
+ @for i in `seq $$(( $(CONFIG_HYPERTRACE_ARGS) - 1 ))`; do \
+ echo -n ", data[$$i-1]" >>$@; \
+ done
+ @echo ");" >>$@
+ @echo "}" >>$@
new file mode 100644
@@ -0,0 +1,26 @@
+/*
+ * QEMU-side management of hypertrace in user-level emulation.
+ *
+ * Copyright (C) 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 "qemu-common.h"
+#include "cpu.h"
+#include "hypertrace/common.h"
+#include "qemu/osdep.h"
+
+void hypertrace_init_config(struct hypertrace_config *config,
+ unsigned int max_clients)
+{
+ config->max_clients = max_clients;
+ config->client_args = CONFIG_HYPERTRACE_ARGS;
+ config->client_data_size = config->client_args * sizeof(uint64_t);
+ config->control_size = QEMU_ALIGN_UP(
+ config->max_clients * sizeof(uint64_t), TARGET_PAGE_SIZE);
+ config->data_size = QEMU_ALIGN_UP(
+ config->max_clients * config->client_data_size, TARGET_PAGE_SIZE);
+}
new file mode 100644
@@ -0,0 +1,24 @@
+/*
+ * QEMU-side management of hypertrace in user-level emulation.
+ *
+ * Copyright (C) 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.
+ */
+
+#if !defined(__KERNEL__)
+#include <stdint.h>
+#endif
+
+struct hypertrace_config
+{
+ uint64_t max_clients;
+ uint64_t client_args;
+ uint64_t client_data_size;
+ uint64_t control_size;
+ uint64_t data_size;
+};
+
+void hypertrace_init_config(struct hypertrace_config *config,
+ unsigned int max_clients);
new file mode 100644
@@ -0,0 +1,339 @@
+/*
+ * QEMU-side management of hypertrace in user-level emulation.
+ *
+ * Copyright (C) 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.
+ */
+
+/*
+ * Implementation details
+ * ======================
+ *
+ * There are 3 channels, each a regular file in the host system, and mmap'ed by
+ * the guest application.
+ *
+ * - Configuration channel: Exposes configuration parameters. Mapped once and
+ * directly readable.
+ *
+ * - Data channel: Lets guests write argument values. Each guest thread should
+ * use a different offset to avoid concurrency problems. Mapped once and
+ * directly accessible.
+ *
+ * - Control channel: Triggers the hypertrace event on a write, providing the
+ * first argument. Offset in the control channel sets the offset in the data
+ * channel. Mapped once per thread, using two pages to reliably detect
+ * accesses and their written value through a SEGV handler.
+ */
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/mman.h>
+
+#include "qemu/osdep.h"
+#include "cpu.h"
+
+#include "hypertrace/common.h"
+#include "hypertrace/user.h"
+#include "qemu/config-file.h"
+#include "qemu/error-report.h"
+#include "trace.h"
+
+
+static struct hypertrace_config config;
+static char *config_path = NULL;
+static int config_fd = -1;
+static uint64_t *qemu_config = NULL;
+
+static char *data_path = NULL;
+static int data_fd = -1;
+static uint64_t *qemu_data = NULL;
+
+static char *control_path = NULL;
+static int control_fd = -1;
+static uint64_t *qemu_control = NULL;
+static struct stat control_fd_stat;
+
+struct sigaction segv_next;
+static void segv_handler(int signum, siginfo_t *siginfo, void *sigctxt);
+
+
+QemuOptsList qemu_hypertrace_opts = {
+ .name = "hypertrace",
+ .implied_opt_name = "path",
+ .head = QTAILQ_HEAD_INITIALIZER(qemu_hypertrace_opts.head),
+ .desc = {
+ {
+ .name = "path",
+ .type = QEMU_OPT_STRING,
+ },
+ {
+ .name = "max-clients",
+ .type = QEMU_OPT_NUMBER,
+ .def_value_str = "1",
+ },
+ { /* end of list */ }
+ },
+};
+
+void hypertrace_opt_parse(const char *optarg, char **base, unsigned int *max_clients_)
+{
+ int max_clients;
+ QemuOpts *opts = qemu_opts_parse_noisily(qemu_find_opts("hypertrace"),
+ optarg, true);
+ if (!opts) {
+ exit(1);
+ }
+ if (qemu_opt_get(opts, "path")) {
+ *base = g_strdup(qemu_opt_get(opts, "path"));
+ } else {
+ *base = NULL;
+ }
+ max_clients = qemu_opt_get_number(opts, "pages", 1);
+ if (max_clients <= 0) {
+ error_report("Parameter 'max-clients' expects a positive number");
+ exit(EXIT_FAILURE);
+ }
+ *max_clients_ = max_clients;
+}
+
+static void init_channel(const char *base, const char *suffix, size_t size,
+ char ** path, int *fd, uint64_t **addr)
+{
+ *path = g_malloc(strlen(base) + strlen(suffix) + 1);
+ sprintf(*path, "%s%s", base, suffix);
+
+ *fd = open(*path, O_CREAT | O_EXCL | O_RDWR, S_IRUSR | S_IWUSR);
+ if (*fd == -1) {
+ error_report("error: open(%s): %s", *path, strerror(errno));
+ abort();
+ }
+
+ off_t lres = lseek(*fd, size - 1, SEEK_SET);
+ if (lres == (off_t)-1) {
+ error_report("error: lseek(%s): %s", *path, strerror(errno));
+ abort();
+ }
+
+ char tmp;
+ ssize_t wres = write(*fd, &tmp, 1);
+ if (wres == -1) {
+ error_report("error: write(%s): %s", *path, strerror(errno));
+ abort();
+ }
+
+ if (addr) {
+ *addr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, *fd, 0);
+ if (*addr == MAP_FAILED) {
+ error_report("error: mmap(%s): %s", *path, strerror(errno));
+ abort();
+ }
+ }
+}
+
+static void fini_handler(int signum, siginfo_t *siginfo, void *sigctxt)
+{
+ hypertrace_fini();
+}
+
+void hypertrace_init(const char *base, unsigned int max_clients)
+{
+ struct sigaction sigint;
+ struct hypertrace_config *pconfig;
+
+ if (base == NULL) {
+ return;
+ }
+
+ memset(&sigint, 0, sizeof(sigint));
+ sigint.sa_sigaction = fini_handler;
+ sigint.sa_flags = SA_SIGINFO | SA_RESTART;
+ if (sigaction(SIGINT, &sigint, NULL) != 0) {
+ error_report("error: sigaction(SIGINT): %s", strerror(errno));
+ abort();
+ }
+ if (sigaction(SIGABRT, &sigint, NULL) != 0) {
+ error_report("error: sigaction(SIGABRT): %s", strerror(errno));
+ abort();
+ }
+
+ hypertrace_init_config(&config, max_clients);
+ /* We need twice the space for the double-fault protocol */
+ config.control_size *= 2;
+
+ init_channel(base, "-config", TARGET_PAGE_SIZE, &config_path, &config_fd, &qemu_config);
+ pconfig = (struct hypertrace_config*)qemu_config;
+ pconfig->max_clients = tswap64(config.max_clients);
+ pconfig->client_args = tswap64(config.client_args);
+ pconfig->client_data_size = tswap64(config.client_data_size);
+ pconfig->control_size = tswap64(config.control_size);
+ pconfig->data_size = tswap64(config.data_size);
+
+ init_channel(base, "-data", config.data_size, &data_path, &data_fd, &qemu_data);
+ if (fstat(data_fd, &control_fd_stat) == -1) {
+ error_report("error: fstat(hypertrace_control): %s", strerror(errno));
+ abort();
+ }
+ printf("data: dev=%ld ino=%ld ptr=%p\n",
+ control_fd_stat.st_dev, control_fd_stat.st_ino, qemu_data);
+
+ init_channel(base, "-control", config.control_size, &control_path, &control_fd, &qemu_control);
+
+ if (fstat(control_fd, &control_fd_stat) == -1) {
+ error_report("error: fstat(hypertrace_control): %s", strerror(errno));
+ abort();
+ }
+ printf("control: dev=%ld ino=%ld ptr=%p\n",
+ control_fd_stat.st_dev, control_fd_stat.st_ino, qemu_control);
+
+ struct sigaction segv;
+ memset(&segv, 0, sizeof(segv));
+ segv.sa_sigaction = segv_handler;
+ segv.sa_flags = SA_SIGINFO | SA_RESTART;
+ sigemptyset(&segv.sa_mask);
+
+ if (sigaction(SIGSEGV, &segv, &segv_next) != 0) {
+ error_report("error: sigaction(SIGSEGV): %s", strerror(errno));
+ abort();
+ }
+}
+
+
+static void fini_channel(int *fd, char **path)
+{
+ if (*fd != -1) {
+ if (close(*fd) == -1) {
+ error_report("error: close: %s", strerror(errno));
+ abort();
+ }
+ if (unlink(*path) == -1) {
+ error_report("error: unlink(%s): %s", *path, strerror(errno));
+ abort();
+ }
+ *fd = -1;
+ }
+ if (*path != NULL) {
+ g_free(*path);
+ *path = NULL;
+ }
+}
+
+void hypertrace_fini(void)
+{
+ static bool atexit_in = false;
+ if (atexit_in) {
+ return;
+ }
+ atexit_in = true;
+
+ if (sigaction(SIGSEGV, &segv_next, NULL) != 0) {
+ error_report("error: sigaction(SIGSEGV): %s", strerror(errno));
+ abort();
+ }
+ fini_channel(&config_fd, &config_path);
+ fini_channel(&data_fd, &data_path);
+ fini_channel(&control_fd, &control_path);
+}
+
+
+bool hypertrace_guest_mmap_check(int fd, unsigned long len, unsigned long offset)
+{
+ struct stat s;
+ if (fstat(fd, &s) < 0) {
+ return true;
+ }
+
+ if (s.st_dev != control_fd_stat.st_dev ||
+ s.st_ino != control_fd_stat.st_ino) {
+ return true;
+ }
+
+ return len == (config.control_size) && offset == 0;
+}
+
+void hypertrace_guest_mmap_apply(int fd, void *qemu_addr, CPUState *vcpu)
+{
+ struct stat s;
+
+ if (vcpu == NULL) {
+ return;
+ }
+
+ if (fstat(fd, &s) != 0) {
+ return;
+ }
+
+ if (s.st_dev != control_fd_stat.st_dev ||
+ s.st_ino != control_fd_stat.st_ino) {
+ return;
+ }
+
+ /* it's an mmap of the control channel; split it in two and mprotect it to
+ * detect writes (cmd is written once on each part)
+ */
+ printf("cpu->hypertrace_control=%p %lu %lu\n", qemu_addr, s.st_dev, s.st_ino);
+ vcpu->hypertrace_control = qemu_addr;
+ if (mprotect(vcpu->hypertrace_control, config.control_size / 2, PROT_READ) == -1) {
+ error_report("error: mprotect(hypertrace_control): %s", strerror(errno));
+ abort();
+ }
+}
+
+static void swap_control(void *from, void *to)
+{
+ printf("mprotect: RW %p %lu\n", from, config.control_size / 2);
+ if (mprotect(from, config.control_size / 2, PROT_READ | PROT_WRITE) == -1) {
+ error_report("error: mprotect(from): %s", strerror(errno));
+ abort();
+ }
+ printf("mprotect: R %p %lu\n", to, config.control_size / 2);
+ if (mprotect(to, config.control_size / 2, PROT_READ) == -1) {
+ error_report("error: mprotect(to): %s", strerror(errno));
+ abort();
+ }
+}
+
+#include "hypertrace/emit.c"
+
+static void segv_handler(int signum, siginfo_t *siginfo, void *sigctxt)
+{
+ CPUState *vcpu = current_cpu;
+ void *control_0 = vcpu->hypertrace_control;
+ void *control_1 = vcpu->hypertrace_control + config.control_size / 2;
+ void *control_2 = control_1 + config.control_size / 2;
+
+ if (control_0 <= siginfo->si_addr && siginfo->si_addr < control_1) {
+
+ /* 1st fault (guest will write cmd) */
+ printf("SEGV 1\n");
+ assert(((unsigned long)siginfo->si_addr % sizeof(uint64_t)) == 0);
+ swap_control(control_0, control_1);
+ printf("SEGV 1!\n");
+
+ } else if (control_1 <= siginfo->si_addr && siginfo->si_addr < control_2) {
+ size_t client = (siginfo->si_addr - control_1) / sizeof(uint64_t);
+ uint64_t vcontrol = ((uint64_t*)control_0)[client];
+ uint64_t *data_ptr = &qemu_data[client * config.client_data_size];
+
+ /* 2nd fault (invoke) */
+ printf("SEGV 2\n");
+ assert(((unsigned long)siginfo->si_addr % sizeof(uint64_t)) == 0);
+ hypertrace_emit(current_cpu, vcontrol, data_ptr);
+ swap_control(control_1, control_0);
+ printf("SEGV 2!\n");
+
+ } else {
+ /* proxy to next handler */
+ printf("SEGV ?\n");
+ if (segv_next.sa_sigaction != NULL) {
+ segv_next.sa_sigaction(signum, siginfo, sigctxt);
+ } else if (segv_next.sa_handler != NULL) {
+ segv_next.sa_handler(signum);
+ }
+ printf("SEGV ?!\n");
+ }
+}
new file mode 100644
@@ -0,0 +1,61 @@
+/*
+ * QEMU-side management of hypertrace in user-level emulation.
+ *
+ * Copyright (C) 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 <stdint.h>
+#include <sys/types.h>
+
+
+/**
+ * Definition of QEMU options describing hypertrace subsystem configuration
+ */
+extern QemuOptsList qemu_hypertrace_opts;
+
+/**
+ * hypertrace_opt_parse:
+ * @optarg: Input arguments.
+ * @base: Output base path for the hypertrace channel files.
+ * @max_clients: Output maximum number of concurrent clients.
+ *
+ * Parse the commandline arguments for hypertrace.
+ */
+void hypertrace_opt_parse(const char *optarg, char **base, unsigned int *max_clients);
+
+/**
+ * hypertrace_init:
+ * @base: Base path for the hypertrace channel files.
+ * @max_clients: Maximum number of concurrent clients.
+ *
+ * Initialize the backing files for the hypertrace channel.
+ */
+void hypertrace_init(const char *base, unsigned int max_clients);
+
+/**
+ * hypertrace_guest_mmap_check:
+ *
+ * Verify argument validity when mapping the control channel.
+ *
+ * Precondition: defined(CONFIG_USER_ONLY)
+ */
+bool hypertrace_guest_mmap_check(int fd, unsigned long len, unsigned long offset);
+
+/**
+ * hypertrace_guest_mmap_apply:
+ *
+ * Configure initial mprotect if mapping the control channel.
+ *
+ * Precondition: defined(CONFIG_USER_ONLY)
+ */
+void hypertrace_guest_mmap_apply(int fd, void *qemu_addr, CPUState *vcpu);
+
+/**
+ * hypertrace_fini:
+ *
+ * Remove the backing files for the hypertrace channel.
+ */
+void hypertrace_fini(void);
@@ -283,6 +283,7 @@ struct qemu_work_item {
* @work_mutex: Lock to prevent multiple access to queued_work_*.
* @queued_work_first: First asynchronous work pending.
* @trace_dstate: Dynamic tracing state of events for this vCPU (bitmask).
+ * @hypertrace_control: Per-vCPU address of the hypertrace control channel.
*
* State of one CPU core or thread.
*/
@@ -353,6 +354,9 @@ struct CPUState {
/* Used for events with 'vcpu' and *without* the 'disabled' properties */
DECLARE_BITMAP(trace_dstate, TRACE_VCPU_EVENT_COUNT);
+ /* Only used when defined(CONFIG_USER_ONLY) */
+ void *hypertrace_control;
+
/* TODO Move common fields from CPUArchState here. */
int cpu_index; /* used by alpha TCG */
uint32_t halted; /* used by alpha, cris, ppc TCG */
@@ -32,10 +32,12 @@
#include "tcg.h"
#include "qemu/timer.h"
#include "qemu/envlist.h"
+#include "qemu/error-report.h"
#include "elf.h"
#include "exec/log.h"
#include "trace/control.h"
#include "glib-compat.h"
+#include "hypertrace/user.h"
char *exec_path;
@@ -4011,6 +4013,14 @@ static void handle_arg_trace(const char *arg)
trace_file = trace_opt_parse(arg);
}
+static char *hypertrace_base;
+static unsigned int hypertrace_max_clients;
+static void handle_arg_hypertrace(const char *arg)
+{
+ g_free(hypertrace_base);
+ hypertrace_opt_parse(arg, &hypertrace_base, &hypertrace_max_clients);
+}
+
struct qemu_argument {
const char *argv;
const char *env;
@@ -4060,6 +4070,8 @@ static const struct qemu_argument arg_table[] = {
"", "Seed for pseudo-random number generator"},
{"trace", "QEMU_TRACE", true, handle_arg_trace,
"", "[[enable=]<pattern>][,events=<file>][,file=<file>]"},
+ {"hypertrace", "QEMU_HYPERTRACE", true, handle_arg_hypertrace,
+ "", "[[base=]<path>][,max-clients=<uint>]"},
{"version", "QEMU_VERSION", false, handle_arg_version,
"", "display version information and exit"},
{NULL, NULL, false, NULL, NULL, NULL}
@@ -4250,6 +4262,7 @@ int main(int argc, char **argv, char **envp)
srand(time(NULL));
qemu_add_opts(&qemu_trace_opts);
+ qemu_add_opts(&qemu_hypertrace_opts);
optind = parse_args(argc, argv);
@@ -4448,6 +4461,12 @@ int main(int argc, char **argv, char **envp)
syscall_init();
signal_init();
+ if (atexit(hypertrace_fini)) {
+ fprintf(stderr, "error: atexit: %s\n", strerror(errno));
+ abort();
+ }
+ hypertrace_init(hypertrace_base, hypertrace_max_clients);
+
/* Now that we've loaded the binary, GUEST_BASE is fixed. Delay
generating the prologue until now so that the prologue can take
the real value of GUEST_BASE into account. */
@@ -23,6 +23,7 @@
#include "qemu.h"
#include "qemu-common.h"
#include "translate-all.h"
+#include "hypertrace/user.h"
//#define DEBUG_MMAP
@@ -357,10 +358,18 @@ abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size)
}
}
-/* NOTE: all the constants are the HOST ones */
abi_long target_mmap(abi_ulong start, abi_ulong len, int prot,
int flags, int fd, abi_ulong offset)
{
+ return target_mmap_cpu(start, len, prot, flags, fd, offset, NULL);
+}
+
+
+/* NOTE: all the constants are the HOST ones */
+abi_long target_mmap_cpu(abi_ulong start, abi_ulong len, int prot,
+ int flags, int fd, abi_ulong offset,
+ CPUState *cpu)
+{
abi_ulong ret, end, real_start, real_end, retaddr, host_offset, host_len;
mmap_lock();
@@ -442,6 +451,10 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int prot,
}
}
+ if (!hypertrace_guest_mmap_check(fd, len, offset)) {
+ goto fail;
+ }
+
if (!(flags & MAP_FIXED)) {
unsigned long host_start;
void *p;
@@ -553,6 +566,8 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int prot,
}
}
the_end1:
+ printf("mmap "TARGET_ABI_FMT_lx" "TARGET_ABI_FMT_lu"\n", start, len);
+ hypertrace_guest_mmap_apply(fd, g2h(start), cpu);
page_set_flags(start, start + len, prot | PAGE_VALID);
the_end:
#ifdef DEBUG_MMAP
@@ -411,6 +411,9 @@ void sparc64_get_context(CPUSPARCState *env);
int target_mprotect(abi_ulong start, abi_ulong len, int prot);
abi_long target_mmap(abi_ulong start, abi_ulong len, int prot,
int flags, int fd, abi_ulong offset);
+abi_long target_mmap_cpu(abi_ulong start, abi_ulong len, int prot,
+ int flags, int fd, abi_ulong offset,
+ CPUState *cpu);
int target_munmap(abi_ulong start, abi_ulong len);
abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size,
abi_ulong new_size, unsigned long flags,
@@ -111,6 +111,7 @@ int __clone2(int (*fn)(void *), void *child_stack_base,
#include "uname.h"
#include "qemu.h"
+#include "hypertrace/user.h"
#define CLONE_NPTL_FLAGS2 (CLONE_SETTLS | \
CLONE_PARENT_SETTID | CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID)
@@ -7372,6 +7373,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
_mcleanup();
#endif
gdb_exit(cpu_env, arg1);
+ hypertrace_fini();
_exit(arg1);
ret = 0; /* avoid warning */
break;
@@ -8824,15 +8826,19 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
v5 = tswapal(v[4]);
v6 = tswapal(v[5]);
unlock_user(v, arg1, 0);
- ret = get_errno(target_mmap(v1, v2, v3,
- target_to_host_bitmask(v4, mmap_flags_tbl),
- v5, v6));
+ ret = get_errno(target_mmap_cpu(
+ v1, v2, v3,
+ target_to_host_bitmask(v4, mmap_flags_tbl),
+ v5, v6,
+ cpu));
}
#else
- ret = get_errno(target_mmap(arg1, arg2, arg3,
- target_to_host_bitmask(arg4, mmap_flags_tbl),
- arg5,
- arg6));
+ ret = get_errno(target_mmap_cpu(
+ arg1, arg2, arg3,
+ target_to_host_bitmask(arg4, mmap_flags_tbl),
+ arg5,
+ arg6,
+ cpu));
#endif
break;
#endif
@@ -8841,10 +8847,12 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
#ifndef MMAP_SHIFT
#define MMAP_SHIFT 12
#endif
- ret = get_errno(target_mmap(arg1, arg2, arg3,
- target_to_host_bitmask(arg4, mmap_flags_tbl),
- arg5,
- arg6 << MMAP_SHIFT));
+ ret = get_errno(target_mmap_cpu(
+ arg1, arg2, arg3,
+ target_to_host_bitmask(arg4, mmap_flags_tbl),
+ arg5,
+ arg6 << MMAP_SHIFT,
+ cpu));
break;
#endif
case TARGET_NR_munmap:
@@ -9377,6 +9385,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
_mcleanup();
#endif
gdb_exit(cpu_env, arg1);
+ hypertrace_fini();
ret = get_errno(exit_group(arg1));
break;
#endif
QEMU detects when the guest uses 'mmap' on hypertrace's control channel file, and then uses 'mprotect' to detect accesses to it, which are used to trigger tracing event "guest_hypertrace". Signed-off-by: Lluís Vilanova <vilanova@ac.upc.edu> --- Makefile.objs | 4 + bsd-user/main.c | 16 ++ bsd-user/mmap.c | 15 ++ bsd-user/syscall.c | 31 +++- hypertrace/Makefile.objs | 18 ++ hypertrace/common.c | 26 ++++ hypertrace/common.h | 24 +++ hypertrace/user.c | 339 ++++++++++++++++++++++++++++++++++++++++++++++ hypertrace/user.h | 61 ++++++++ include/qom/cpu.h | 4 + linux-user/main.c | 19 +++ linux-user/mmap.c | 17 ++ linux-user/qemu.h | 3 linux-user/syscall.c | 31 +++- 14 files changed, 583 insertions(+), 25 deletions(-) create mode 100644 hypertrace/Makefile.objs create mode 100644 hypertrace/common.c create mode 100644 hypertrace/common.h create mode 100644 hypertrace/user.c create mode 100644 hypertrace/user.h