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 <[email protected]>

diff --git a/.gitignore b/.gitignore
--- 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/tools/Makefile b/tools/Makefile
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -1,7 +1,7 @@
 #
 # Jailhouse, a Linux-based partitioning hypervisor
 #
-# Copyright (c) Siemens AG, 2013-2016
+# Copyright (c) Siemens AG, 2013-2017
 #
 # Authors:
 #  Jan Kiszka <[email protected]>
@@ -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
--- /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 <[email protected]>
+ *
+ * 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 + 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, errstr);
+       return 0;
+}

-- 
You received this message because you are subscribed to the Google Groups 
"Jailhouse" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/d/optout.

Reply via email to