All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Lluís Vilanova" <vilanova@ac.upc.edu>
To: qemu-devel@nongnu.org
Cc: Daniel P Berrange <berrange@redhat.com>,
	Luiz Capitulino <lcapitulino@redhat.com>,
	Eric Blake <eblake@redhat.com>,
	Stefan Hajnoczi <stefanha@redhat.com>
Subject: [Qemu-devel] [PATCH v5 5/5] hypertrace: Add guest-side user-level library
Date: Wed, 26 Jul 2017 20:08:37 +0300	[thread overview]
Message-ID: <150108891746.11502.15570982148220246385.stgit@frigg.lan> (raw)
In-Reply-To: <150108770564.11502.9555409719195740021.stgit@frigg.lan>

Provides guest library "libqemu-hypertrace-guest.a" to abstract access
to the hypertrace channel.

Signed-off-by: Lluís Vilanova <vilanova@ac.upc.edu>
---
 Makefile                           |    5 +
 configure                          |    2 
 hypertrace/guest/Makefile          |   30 ++++
 hypertrace/guest/common.c          |  301 ++++++++++++++++++++++++++++++++++++
 hypertrace/guest/qemu-hypertrace.h |   80 ++++++++++
 5 files changed, 418 insertions(+)
 create mode 100644 hypertrace/guest/Makefile
 create mode 100644 hypertrace/guest/common.c
 create mode 100644 hypertrace/guest/qemu-hypertrace.h

diff --git a/Makefile b/Makefile
index d45158cf29..f3a39b6c7c 100644
--- a/Makefile
+++ b/Makefile
@@ -601,8 +601,13 @@ ifneq (,$(findstring qemu-ga,$(TOOLS)))
 endif
 endif
 
+install-hypertrace:
+	$(INSTALL_DIR) "$(DESTDIR)$(includedir)"
+	$(INSTALL_DATA) "$(SRC_PATH)/hypertrace/guest/qemu-hypertrace.h" "$(DESTDIR)$(includedir)/"
+
 
 install: all $(if $(BUILD_DOCS),install-doc) install-datadir install-localstatedir
+install: install-hypertrace
 ifneq ($(TOOLS),)
 	$(call install-prog,$(subst qemu-ga,qemu-ga$(EXESUF),$(TOOLS)),$(DESTDIR)$(bindir))
 endif
diff --git a/configure b/configure
index e6f752c242..9c7358d0b4 100755
--- a/configure
+++ b/configure
@@ -6286,6 +6286,8 @@ if [ "$TARGET_BASE_ARCH" = "" ]; then
 fi
 
 symlink "$source_path/Makefile.target" "$target_dir/Makefile"
+mkdir -p $target_dir/hypertrace/guest
+symlink $source_path/hypertrace/guest/Makefile $target_dir/hypertrace/guest/Makefile
 
 upper() {
     echo "$@"| LC_ALL=C tr '[a-z]' '[A-Z]'
diff --git a/hypertrace/guest/Makefile b/hypertrace/guest/Makefile
new file mode 100644
index 0000000000..cbc956bde1
--- /dev/null
+++ b/hypertrace/guest/Makefile
@@ -0,0 +1,30 @@
+include ../../../config-host.mak
+include ../../config-target.mak
+include $(SRC_PATH)/rules.mak
+
+vpath % $(SRC_PATH)/hypertrace/guest
+
+# 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/common.c b/hypertrace/guest/common.c
new file mode 100644
index 0000000000..96ae4464bc
--- /dev/null
+++ b/hypertrace/guest/common.c
@@ -0,0 +1,301 @@
+/*
+ * Guest-side management of hypertrace.
+ *
+ * Copyright (C) 2016-2017 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-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 page_size;
+
+
+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;
+        }
+
+        page_size = getpagesize();
+
+        res = init_channel_file(base, config_suff, page_size,
+                                &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 offset;
+#if defined(CONFIG_USER_ONLY)
+    offset = client * page_size;
+#endif
+    *(uint64_t *)((char *)control_addr + offset) = arg1;
+#if defined(CONFIG_USER_ONLY)
+    /* QEMU in 'user' mode uses two faulting pages to detect invocations */
+    *(uint64_t *)((char *)control_addr_1 + offset) = arg1;
+#endif
+}
diff --git a/hypertrace/guest/qemu-hypertrace.h b/hypertrace/guest/qemu-hypertrace.h
new file mode 100644
index 0000000000..7c7a17c64d
--- /dev/null
+++ b/hypertrace/guest/qemu-hypertrace.h
@@ -0,0 +1,80 @@
+/*
+ * Guest-side management of hypertrace.
+ *
+ * Copyright (C) 2016-2017 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>
+
+
+/**
+ * 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);

  parent reply	other threads:[~2017-07-26 17:08 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-07-26 16:48 [Qemu-devel] [PATCH v5 0/5] hypertrace: Lightweight guest-to-QEMU trace channel Lluís Vilanova
2017-07-26 16:52 ` [Qemu-devel] [PATCH v5 1/5] hypertrace: Add documentation Lluís Vilanova
2017-07-26 16:56 ` [Qemu-devel] [PATCH v5 2/5] hypertrace: Add tracing event "guest_hypertrace" Lluís Vilanova
2017-07-26 17:00 ` [Qemu-devel] [PATCH v5 3/5] hypertrace: [*-user] Add QEMU-side proxy to "guest_hypertrace" event Lluís Vilanova
2017-07-26 17:04 ` [Qemu-devel] [PATCH v5 4/5] hypertrace: [softmmu] " Lluís Vilanova
2017-07-26 17:08 ` Lluís Vilanova [this message]
2017-07-26 17:13 ` [Qemu-devel] [PATCH v5 0/5] hypertrace: Lightweight guest-to-QEMU trace channel no-reply
2017-07-26 17:13 ` no-reply

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=150108891746.11502.15570982148220246385.stgit@frigg.lan \
    --to=vilanova@ac.upc.edu \
    --cc=berrange@redhat.com \
    --cc=eblake@redhat.com \
    --cc=lcapitulino@redhat.com \
    --cc=qemu-devel@nongnu.org \
    --cc=stefanha@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.