From: Henning Schild <
henning...@siemens.com>
With the new feature to download the used firmware out of sysfs and
a way to get GCOV data into that. It is now time to extract this
information and generate .gcda files for higher level tools to work
with.
Instead of dealing with the details of that just use what the
compiler gives us when we link to its gcov lib.
Signed-off-by: Henning Schild <
henning...@siemens.com>
[Jan: clean .gcda files, fix usage of error(),
fix signed/unsigned comparison]
.gitignore | 2 +
hypervisor/Makefile | 1 +
tools/Makefile | 14 ++-
tools/jailhouse-gcov-extract.c | 222 +++++++++++++++++++++++++++++++++++++++++
4 files changed, 238 insertions(+), 1 deletion(-)
create mode 100644 tools/jailhouse-gcov-extract.c
diff --git a/.gitignore b/.gitignore
index 839219ee..727eee34 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,6 +4,7 @@
*.cmd
*.bin
*.gcno
+*.gcda
.tmp_versions
*.dtb
*.dtb.S
@@ -14,6 +15,7 @@ driver/jailhouse.ko
hypervisor/include/jailhouse/config.h
hypervisor/hypervisor.lds
tools/jailhouse
+tools/jailhouse-gcov-extract
tools/jailhouse-config-collect
configs/*.cell
Documentation/generated
diff --git a/hypervisor/Makefile b/hypervisor/Makefile
index 0f3c7bcb..279c4c4e 100644
--- a/hypervisor/Makefile
+++ b/hypervisor/Makefile
@@ -41,6 +41,7 @@ ifdef CONFIG_JAILHOUSE_GCOV
CORE_OBJECTS += gcov.o
endif
ccflags-$(CONFIG_JAILHOUSE_GCOV) += -fprofile-arcs -ftest-coverage
+clean-files += *.gcda arch/*/.*.gcda
clean-dirs := arch/$(SRCARCH)/include/generated
diff --git a/tools/Makefile b/tools/Makefile
index 23c2b3c7..0210dc18 100644
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -1,7 +1,7 @@
#
# Jailhouse, a Linux-based partitioning hypervisor
#
-# Copyright (c) Siemens AG, 2013-2016
@@ -23,6 +23,7 @@ LDFLAGS :=
GCOV_PROFILE := n
BINARIES := jailhouse
+
HELPERS := \
jailhouse-cell-linux \
jailhouse-cell-stats \
@@ -58,6 +59,17 @@ targets += jailhouse.o
$(obj)/jailhouse: $(obj)/jailhouse.o
$(call if_changed,ld)
+CFLAGS_jailhouse-gcov-extract.o := -I$(src)/../hypervisor/include \
+ -I$(src)/../hypervisor/arch/$(SRCARCH)/include
+# just change ldflags not cflags, we are not profiling the tool
+LDFLAGS_jailhouse-gcov-extract := -lgcov -fprofile-arcs
+
+targets += jailhouse-gcov-extract.o
+always += jailhouse-gcov-extract
+
+$(obj)/jailhouse-gcov-extract: $(obj)/jailhouse-gcov-extract.o
+ $(call if_changed,ld)
+
$(obj)/jailhouse-config-collect: $(src)/jailhouse-config-create $(src)/jailhouse-config-collect.tmpl
$(call if_changed,gen_collect)
diff --git a/tools/jailhouse-gcov-extract.c b/tools/jailhouse-gcov-extract.c
new file mode 100644
index 00000000..3c25d63e
--- /dev/null
+++ b/tools/jailhouse-gcov-extract.c
@@ -0,0 +1,222 @@
+/*
+ * Jailhouse, a Linux-based partitioning hypervisor
+ *
+ * Copyright (c) Siemens AG, 2017
+ *
+ * Authors:
+ * Henning Schild <
henning...@siemens.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 <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <error.h>
+#include <unistd.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <jailhouse/header.h>
+#include <asm/jailhouse_header.h>
+
+#if (__GNUC__ < 4) || (__GNUC__ == 4 && __GNUC_MINOR__ < 7)
+#error "Gcov format of gcc < 4.7 is not supported!"
+#endif
+
+#ifdef __ARM_EABI__
+#define BITS_PER_LONG 32
+#else
+#define BITS_PER_LONG 64
+#endif
+/*
+ * the following bits are heavily inspired by linux/kernel/gcov/gcc_4.7.c
+ * with some slight modification
+ */
+#if BITS_PER_LONG >= 64
+typedef long gcov_type;
+#else
+typedef long long gcov_type;
+#endif
+
+#if (__GNUC__ > 5) || (__GNUC__ == 5 && __GNUC_MINOR__ >= 1)
+#define GCOV_COUNTERS 10
+#elif __GNUC__ == 4 && __GNUC_MINOR__ >= 9
+#define GCOV_COUNTERS 9
+#else
+#define GCOV_COUNTERS 8
+#endif
+
+struct gcov_ctr_info {
+ unsigned int num;
+ gcov_type *values;
+};
+
+struct gcov_fn_info {
+ struct gcov_info *key;
+ unsigned int ident;
+ unsigned int lineno_checksum;
+ unsigned int cfg_checksum;
+ struct gcov_ctr_info ctrs[0];
+};
+
+struct gcov_info {
+ unsigned int version;
+ struct gcov_info *next;
+ unsigned int stamp;
+ char *filename;
+ void (*merge[GCOV_COUNTERS])(gcov_type *, unsigned int);
+ unsigned int n_functions;
+ struct gcov_fn_info **functions;
+};
+/*
+ * end of linux/kernel/gcov/gcc_4.7.c
+ */
+
+static void *hypervisor;
+static ssize_t hypervisor_size;
+extern void __gcov_merge_add(gcov_type *counters, unsigned int n_counters);
+extern void __gcov_init(struct gcov_info *);
+extern void __gcov_dump();
+
+static void *hypervisor2current(void *hvp)
+{
+ unsigned long hvaddr = (unsigned long)hvp;
+ void *ret;
+
+ if (hvp == NULL)
+ return NULL;
+ assert(hvaddr >= JAILHOUSE_BASE &&
+ hvaddr < JAILHOUSE_BASE + (unsigned long)hypervisor_size);
+ ret = (void *)(hvaddr - JAILHOUSE_BASE + (unsigned long)hypervisor);
+
+ return ret;
+}
+
+/*
+ * translate one gcov-"tree" from the hypervisor address space to the current
+ * addresses
+ */
+static void translate_all_pointers(struct gcov_info *info)
+{
+ struct gcov_fn_info *fn_info;
+ struct gcov_ctr_info *ctr_info;
+ unsigned int i, j, active;
+
+ info->next = hypervisor2current(info->next);
+ info->filename = hypervisor2current(info->filename);
+ active = 0;
+ for (i = 0; i < GCOV_COUNTERS; i++) {
+ if (info->merge[i]) {
+ active++;
+ info->merge[i] = &__gcov_merge_add;
+ } else
+ break;
+ }
+ info->functions = hypervisor2current(info->functions);
+ for (i = 0; i < info->n_functions; i++) {
+ info->functions[i] = hypervisor2current(info->functions[i]);
+ fn_info = info->functions[i];
+ if (fn_info) {
+ fn_info->key = hypervisor2current(fn_info->key);
+ assert(fn_info->key == info);
+ for (j = 0; j < active; j++) {
+ ctr_info = fn_info->ctrs + j;
+ ctr_info->values =
+ hypervisor2current(ctr_info->values);
+ }
+ }
+ }
+}
+
+int main(int argc, char **argv)
+{
+ struct gcov_info *gcov_info_head, *info, *next;
+ struct jailhouse_header *header;
+ struct stat sbuf;
+ char *filename;
+ char *errstr = NULL;
+ ssize_t count, ret;
+ int fd;
+
+ if (argc == 1) {
+ filename = "/sys/devices/jailhouse/core";
+ } else {
+ if (argc != 2 || (strncmp(argv[1], "-", 1) == 0)) {
+ printf("Usage: %s [-h] [FILE]\n", argv[0]);
+ if (strcmp(argv[1], "-h")) {
+ errno = EINVAL;
+ errstr = argv[1];
+ }
+ goto out;
+ }
+ filename = argv[1];
+ }
+ fd = open(filename, O_RDONLY);
+ if (fd < 1) {
+ errstr = filename;
+ goto out;
+ }
+
+ ret = fstat(fd, &sbuf);
+ if (ret) {
+ errstr = filename;
+ goto out;
+ }
+ hypervisor_size = sbuf.st_size;
+ hypervisor = malloc(hypervisor_size);
+ if (hypervisor == NULL) {
+ errstr = "malloc";
+ goto out_f;
+ }
+
+ count = 0;
+ while (count < hypervisor_size) {
+ ret = read(fd, hypervisor + count, hypervisor_size-count);
+ if (ret < 0 && errno != EINTR) {
+ errstr = "read";
+ goto out_m;
+ }
+ count += ret;
+ }
+ assert(count == hypervisor_size);
+
+ header = (struct jailhouse_header *)hypervisor;
+ if (strcmp(header->signature, JAILHOUSE_SIGNATURE)) {
+ errno = EINVAL;
+ error(0, 0, "%s does not seem to be a hypervisor image",
+ filename);
+ goto out_m;
+ }
+
+ gcov_info_head = hypervisor2current(header->gcov_info_head);
+ if (!gcov_info_head) {
+ errno = EINVAL;
+ error(0, 0, "%s does not contain gcov information.", filename);
+ goto out_m;
+ }
+ info = gcov_info_head;
+ for (info = gcov_info_head; info; info = info->next)
+ translate_all_pointers(info);
+
+ for (info = gcov_info_head; info;) {
+ /* remember next because __gcov_init changes it */
+ next = info->next;
+ __gcov_init(info);
+ info = next;
+ }
+ __gcov_dump();
+
+out_m:
+ free(hypervisor);
+out_f:
+ close(fd);
+out:
+ if (errno && errstr)
+ error(errno, errno, "%s", errstr);
+ return 0;
+}
--
2.12.0