From patchwork Mon Dec 26 20:35:05 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Llu=C3=ADs_Vilanova?= X-Patchwork-Id: 9488775 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 5DE3D60839 for ; Mon, 26 Dec 2016 20:42:48 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 4F8401FF60 for ; Mon, 26 Dec 2016 20:42:48 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 4382A1FFBD; Mon, 26 Dec 2016 20:42:48 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=2.0 tests=BAYES_00,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 6AF6E1FF60 for ; Mon, 26 Dec 2016 20:42:45 +0000 (UTC) Received: from localhost ([::1]:51646 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1cLc6W-0007sw-BU for patchwork-qemu-devel@patchwork.kernel.org; Mon, 26 Dec 2016 15:42:44 -0500 Received: from eggs.gnu.org ([2001:4830:134:3::10]:41957) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1cLbzA-0002Fg-NY for qemu-devel@nongnu.org; Mon, 26 Dec 2016 15:35:10 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1cLbz8-00033V-Ru for qemu-devel@nongnu.org; Mon, 26 Dec 2016 15:35:08 -0500 Received: from roura.ac.upc.edu ([147.83.33.10]:50877 helo=roura.ac.upc.es) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1cLbz8-00030s-BG for qemu-devel@nongnu.org; Mon, 26 Dec 2016 15:35:06 -0500 Received: from gw-2.ac.upc.es (gw-2.ac.upc.es [147.83.30.8]) by roura.ac.upc.es (8.13.8/8.13.8) with ESMTP id uBQKZ5Eo026174; Mon, 26 Dec 2016 21:35:05 +0100 Received: from localhost (unknown [84.88.51.85]) by gw-2.ac.upc.es (Postfix) with ESMTPSA id 67FD44BD; Mon, 26 Dec 2016 21:35:05 +0100 (CET) From: =?utf-8?b?TGx1w61z?= Vilanova To: qemu-devel@nongnu.org Date: Mon, 26 Dec 2016 21:35:05 +0100 Message-Id: <148278450509.8988.11863539319235922492.stgit@fimbulvetr.bsc.es> X-Mailer: git-send-email 2.11.0 In-Reply-To: <148278447806.8988.12706825771606357198.stgit@fimbulvetr.bsc.es> References: <148278447806.8988.12706825771606357198.stgit@fimbulvetr.bsc.es> User-Agent: StGit/0.17.1-dirty MIME-Version: 1.0 X-MIME-Autoconverted: from 8bit to quoted-printable by roura.ac.upc.es id uBQKZ5Eo026174 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.6.x [fuzzy] X-Received-From: 147.83.33.10 Subject: [Qemu-devel] [PATCH v4 5/6] hypertrace: Add guest-side user-level library X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Stefan Hajnoczi , Luiz Capitulino Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" X-Virus-Scanned: ClamAV using ClamSMTP Provides guest library "libqemu-hypertrace-guest.a" to abstract access to the hypertrace channel. Signed-off-by: Lluís Vilanova --- 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 474cc5e66a..aa3fd4a79b 100644 --- a/Makefile +++ b/Makefile @@ -459,9 +459,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 afc7071e77..4dfb9ff586 100755 --- a/configure +++ b/configure @@ -6021,6 +6021,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 0000000000..85d08390db --- /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 0000000000..caa2bfce20 --- /dev/null +++ b/hypertrace/guest/user/common.c @@ -0,0 +1,296 @@ +/* + * Guest-side management of hypertrace. + * + * Copyright (C) 2016 Lluís Vilanova + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 0000000000..aa78bb3355 --- /dev/null +++ b/hypertrace/guest/user/qemu-hypertrace.h @@ -0,0 +1,80 @@ +/* + * Guest-side management of hypertrace. + * + * Copyright (C) 2016 Lluís Vilanova + * + * 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 +#include + + +/** + * 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);