[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH v3 5/6] hypertrace: Add guest-side user-level librar
From: |
Lluís Vilanova |
Subject: |
[Qemu-devel] [PATCH v3 5/6] hypertrace: Add guest-side user-level library |
Date: |
Wed, 28 Sep 2016 15:05:57 +0200 |
User-agent: |
StGit/0.17.1-dirty |
Provides guest library "libqemu-hypertrace-guest.a" to abstract access
to the hypertrace channel.
Signed-off-by: Lluís Vilanova <address@hidden>
---
Makefile | 6 +
configure | 2
hypertrace/guest/user/Makefile | 30 +++
hypertrace/guest/user/common.c | 296 +++++++++++++++++++++++++++++++
hypertrace/guest/user/qemu-hypertrace.h | 80 ++++++++
5 files changed, 413 insertions(+), 1 deletion(-)
create mode 100644 hypertrace/guest/user/Makefile
create mode 100644 hypertrace/guest/user/common.c
create mode 100644 hypertrace/guest/user/qemu-hypertrace.h
diff --git a/Makefile b/Makefile
index f103616..45c5c05 100644
--- a/Makefile
+++ b/Makefile
@@ -463,9 +463,13 @@ ifneq (,$(findstring qemu-ga,$(TOOLS)))
endif
endif
+install-hypertrace:
+ $(INSTALL_DIR) "$(DESTDIR)$(includedir)"
+ $(INSTALL_DATA) "$(SRC_PATH)/hypertrace/guest/user/qemu-hypertrace.h"
"$(DESTDIR)$(includedir)/"
+
install: all $(if $(BUILD_DOCS),install-doc) \
-install-datadir install-localstatedir
+install-datadir install-localstatedir install-hypertrace
ifneq ($(TOOLS),)
$(call install-prog,$(subst
qemu-ga,qemu-ga$(EXESUF),$(TOOLS)),$(DESTDIR)$(bindir))
endif
diff --git a/configure b/configure
index a25c33e..710bc91 100755
--- a/configure
+++ b/configure
@@ -5770,6 +5770,8 @@ if [ "$TARGET_BASE_ARCH" = "" ]; then
fi
symlink "$source_path/Makefile.target" "$target_dir/Makefile"
+mkdir -p $target_dir/hypertrace/guest/user
+symlink $source_path/hypertrace/guest/user/Makefile
$target_dir/hypertrace/guest/user/Makefile
upper() {
echo "$@"| LC_ALL=C tr '[a-z]' '[A-Z]'
diff --git a/hypertrace/guest/user/Makefile b/hypertrace/guest/user/Makefile
new file mode 100644
index 0000000..85d0839
--- /dev/null
+++ b/hypertrace/guest/user/Makefile
@@ -0,0 +1,30 @@
+include ../../../../config-host.mak
+include ../../../config-target.mak
+include $(SRC_PATH)/rules.mak
+
+vpath % $(SRC_PATH)/hypertrace/guest/user
+
+# do not use QEMU's per-host cflags when building guest code
+QEMU_CFLAGS = -Werror -Wall
+
+QEMU_CFLAGS += $(GLIB_CFLAGS)
+QEMU_CFLAGS += -I$(SRC_PATH)/include
+QEMU_CFLAGS += -I../../../../linux-headers
+QEMU_CFLAGS += -I../../../../
+QEMU_CFLAGS += -I../../../
+
+ifdef CONFIG_SOFTMMU
+QEMU_CFLAGS += -DNEED_CPU_H
+QEMU_CFLAGS += -I$(SRC_PATH)/target-$(TARGET_BASE_ARCH)
+endif
+
+QEMU_CFLAGS += -g -O2
+
+obj-y = common.o
+
+libqemu-hypertrace-guest.a: $(obj-y)
+
+all: libqemu-hypertrace-guest.a
+
+clean:
+ rm -f $(obj-y) libqemu-hypertrace-guest.a
diff --git a/hypertrace/guest/user/common.c b/hypertrace/guest/user/common.c
new file mode 100644
index 0000000..caa2bfc
--- /dev/null
+++ b/hypertrace/guest/user/common.c
@@ -0,0 +1,296 @@
+/*
+ * Guest-side management of hypertrace.
+ *
+ * Copyright (C) 2016 Lluís Vilanova <address@hidden>
+ *
+ * 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-hypertrace.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <glob.h>
+
+#include "config-host.h"
+#include "config-target.h"
+#if defined(CONFIG_SOFTMMU)
+#include "qemu/osdep.h"
+#include "hw/pci/pci.h"
+#endif
+#include "hypertrace/common.h"
+
+static pthread_mutex_t init_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static char *config_path;
+static int config_fd = -1;
+static uint64_t *config_addr;
+static struct hypertrace_config *config;
+
+
+static char *data_path;
+static int data_fd = -1;
+static uint64_t *data_addr;
+
+static char *control_path;
+static int control_fd = -1;
+#if defined(CONFIG_USER_ONLY)
+static __thread uint64_t *control_addr;
+static __thread uint64_t *control_addr_1;
+#else
+static uint64_t *control_addr;
+#endif
+
+
+static int init_channel_file(const char *base, const char *suffix, size_t size,
+ char **path, int *fd, uint64_t **addr, bool write)
+{
+ int prot;
+
+ *path = malloc(strlen(base) + strlen(suffix) + 1);
+ sprintf(*path, "%s%s", base, suffix);
+
+ prot = O_RDONLY;
+ if (write) {
+ prot = O_RDWR;
+ }
+ *fd = open(*path, prot);
+ if (*fd == -1) {
+ return -1;
+ }
+
+ prot = PROT_READ;
+ if (write) {
+ prot |= PROT_WRITE;
+ }
+ *addr = mmap(NULL, size, prot, MAP_SHARED, *fd, 0);
+ if (*addr == MAP_FAILED) {
+ return -1;
+ }
+ return 0;
+}
+
+#if !defined(CONFIG_USER_ONLY) && defined(__linux__)
+static int check_device_id(const char *base, const char *name, uint64_t value)
+{
+ char tmp[1024];
+ sprintf(tmp, "%s/%s", base, name);
+
+ int fd = open(tmp, O_RDONLY);
+ if (fd < 0) {
+ return -1;
+ }
+
+ char v[1024];
+ ssize_t s = read(fd, v, sizeof(v));
+ if (s < 0) {
+ close(fd);
+ return -1;
+ }
+ v[s] = '\0';
+
+ char *res;
+ uint64_t vv = strtoull(v, &res, 16);
+ if (*res == '\n' && vv == value) {
+ return 0;
+ } else {
+ return -1;
+ }
+}
+
+static char *find_device(void)
+{
+ static char tmp[1024];
+ char *res = NULL;
+
+ glob_t g;
+ if (glob("/sys/devices/pci*/*", GLOB_NOSORT, NULL, &g) != 0) {
+ return NULL;
+ }
+
+
+ int i;
+ for (i = 0; i < g.gl_pathc; i++) {
+ char *path = g.gl_pathv[i];
+
+ if (check_device_id(path, "vendor",
+ PCI_VENDOR_ID_REDHAT_QUMRANET) < 0) {
+ continue;
+ }
+ if (check_device_id(path, "device",
+ PCI_DEVICE_ID_HYPERTRACE) < 0) {
+ continue;
+ }
+
+ sprintf(tmp, "%s", path);
+ res = tmp;
+ break;
+ }
+
+ globfree(&g);
+
+ return res;
+}
+#endif
+
+int qemu_hypertrace_init(const char *base)
+{
+#if defined(CONFIG_USER_ONLY)
+ const char *config_suff = "-config";
+ const char *data_suff = "-data";
+ const char *control_suff = "-control";
+#elif defined(__linux__)
+ const char *config_suff = "/resource0";
+ const char *data_suff = "/resource1";
+ const char *control_suff = "/resource2";
+#else
+#error Unsupported OS
+#endif
+
+#if defined(CONFIG_USER_ONLY)
+ if (base == NULL) {
+ errno = ENOENT;
+ return -1;
+ }
+#elif defined(__linux__)
+ if (base == NULL) {
+ /* try to guess the base path */
+ base = find_device();
+ if (base == NULL) {
+ errno = ENOENT;
+ return -1;
+ }
+ }
+#endif
+
+ if (config_addr == NULL) {
+ int res;
+
+ if (pthread_mutex_lock(&init_mutex)) {
+ return -1;
+ }
+
+ res = init_channel_file(base, config_suff, getpagesize(),
+ &config_path, &config_fd, &config_addr,
+ false);
+ if (res != 0) {
+ return res;
+ }
+
+ config = (struct hypertrace_config *)config_addr;
+
+ if (pthread_mutex_unlock(&init_mutex)) {
+ return -1;
+ }
+ }
+
+ if (data_addr == NULL) {
+ int res;
+
+ if (pthread_mutex_lock(&init_mutex)) {
+ return -1;
+ }
+
+ res = init_channel_file(base, data_suff, config->data_size,
+ &data_path, &data_fd, &data_addr,
+ true);
+ if (res != 0) {
+ return res;
+ }
+
+ if (pthread_mutex_unlock(&init_mutex)) {
+ return -1;
+ }
+ }
+
+ if (control_addr == NULL) {
+ int res;
+ uint64_t control_size = config->control_size;
+
+ if (pthread_mutex_lock(&init_mutex)) {
+ return -1;
+ }
+
+ res = init_channel_file(base, control_suff, control_size,
+ &control_path, &control_fd, &control_addr,
+ true);
+ if (res != 0) {
+ return res;
+ }
+
+#if defined(CONFIG_USER_ONLY)
+ control_addr_1 = (uint64_t *)((char *)control_addr +
+ config->control_size / 2);
+#endif
+
+ if (pthread_mutex_unlock(&init_mutex)) {
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+static int fini_channel(int *fd, char **path)
+{
+ if (*fd != -1) {
+ if (close(*fd) == -1) {
+ return -1;
+ }
+ *fd = -1;
+ }
+ if (*path != NULL) {
+ free(*path);
+ *path = NULL;
+ }
+ return 0;
+}
+
+int qemu_hypertrace_fini(void)
+{
+ if (fini_channel(&data_fd, &data_path) != 0) {
+ return -1;
+ }
+ if (fini_channel(&control_fd, &control_path) != 0) {
+ return -1;
+ }
+ return 0;
+}
+
+
+uint64_t qemu_hypertrace_max_clients(void)
+{
+ return config->max_clients;
+}
+
+uint64_t qemu_hypertrace_num_args(void)
+{
+ return config->client_args;
+}
+
+uint64_t *qemu_hypertrace_data(uint64_t client)
+{
+ return &data_addr[client * CONFIG_HYPERTRACE_ARGS * sizeof(uint64_t)];
+}
+
+void qemu_hypertrace(uint64_t client, uint64_t arg1)
+{
+ uint64_t *ctrl = control_addr;
+ ctrl[client] = arg1;
+#if defined(CONFIG_USER_ONLY)
+ /* QEMU in 'user' mode uses two faulting pages to detect invocations */
+ ctrl = control_addr_1;
+ ctrl[client] = arg1;
+#endif
+}
diff --git a/hypertrace/guest/user/qemu-hypertrace.h
b/hypertrace/guest/user/qemu-hypertrace.h
new file mode 100644
index 0000000..aa78bb3
--- /dev/null
+++ b/hypertrace/guest/user/qemu-hypertrace.h
@@ -0,0 +1,80 @@
+/*
+ * Guest-side management of hypertrace.
+ *
+ * Copyright (C) 2016 Lluís Vilanova <address@hidden>
+ *
+ * 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>
+
+
+/**
+ * qemu_hypertrace_init:
+ * @base: Base path to the hypertrace channel.
+ *
+ * Initialize the hypertrace channel. The operation is idempotent, and must be
+ * called once per thread if running in QEMU's "user" mode.
+ *
+ * The base path to the hypertrace channel depends on the type of QEMU target:
+ *
+ * - User (single-application)
+ * The base path provided when starting QEMU ("-hypertrace" commandline
+ * option).
+ *
+ * - System (OS-dependant)
+ * + Linux
+ * The base path to the hypertrace channel virtual device; on a default
QEMU
+ * device setup for x86 this is "/sys/devices/pci0000:00/0000:00:04.0". If
+ * NULL is provided, the hypertrace device will be automatically detected.
+ *
+ * Returns: Zero on success.
+ */
+int qemu_hypertrace_init(const char *base);
+
+/**
+ * qemu_hypertrace_fini:
+ *
+ * Deinitialize the hypertrace channel.
+ *
+ * Returns: Zero on success.
+ */
+int qemu_hypertrace_fini(void);
+
+/**
+ * qemu_hypertrace_max_clients:
+ *
+ * Maximum number of concurrent clients accepted by other calls.
+ */
+uint64_t qemu_hypertrace_max_clients(void);
+
+/**
+ * qemu_hypertrace_num_args:
+ *
+ * Number of uint64_t values read by each call to qemu_hypertrace().
+ */
+uint64_t qemu_hypertrace_num_args(void);
+
+/**
+ * qemu_hypertrace_data:
+ * @client: Client identifier.
+ *
+ * Pointer to the start of the data channel for the given client. Clients must
+ * write their arguments there (all but the first one).
+ */
+uint64_t *qemu_hypertrace_data(uint64_t client);
+
+/**
+ * qemu_hypertrace:
+ * @client: Client identifier.
+ * @arg1: First argument of the hypertrace event.
+ *
+ * Emit a hypertrace event.
+ *
+ * Each of the clients (e.g., thread) must use a different client identifier to
+ * ensure they can work concurrently without using locks (i.e., each uses a
+ * different portion of the data channel).
+ */
+void qemu_hypertrace(uint64_t client, uint64_t arg1);
- [Qemu-devel] [PATCH v3 0/6] hypertrace: Lightweight guest-to-QEMU trace channel, Lluís Vilanova, 2016/09/28
- [Qemu-devel] [PATCH v3 1/6] hypertrace: Add documentation, Lluís Vilanova, 2016/09/28
- [Qemu-devel] [PATCH v3 2/6] hypertrace: Add tracing event "guest_hypertrace", Lluís Vilanova, 2016/09/28
- [Qemu-devel] [PATCH v3 5/6] hypertrace: Add guest-side user-level library,
Lluís Vilanova <=
- [Qemu-devel] [PATCH v3 3/6] hypertrace: [*-user] Add QEMU-side proxy to "guest_hypertrace" event, Lluís Vilanova, 2016/09/28
- [Qemu-devel] [PATCH v3 4/6] hypertrace: [softmmu] Add QEMU-side proxy to "guest_hypertrace" event, Lluís Vilanova, 2016/09/28
- [Qemu-devel] [PATCH v3 6/6] hypertrace: Add guest-side Linux module, Lluís Vilanova, 2016/09/28
- Re: [Qemu-devel] [PATCH v3 0/6] hypertrace: Lightweight guest-to-QEMU trace channel, no-reply, 2016/09/28