Skip to content
Permalink
Browse files

tools: new tool to get gcov data out of dirty firmware

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.schild@siemens.com>
[Jan: clean .gcda files, fix usage of error(),
      fix signed/unsigned comparison]
Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
  • Loading branch information...
henning-schild authored and jan-kiszka committed Apr 12, 2017
1 parent 6bd02b7 commit 6462339477171b1d9459a18b934b0939f31b6947
Showing with 238 additions and 1 deletion.
  1. +2 −0 .gitignore
  2. +1 −0 hypervisor/Makefile
  3. +13 −1 tools/Makefile
  4. +222 −0 tools/jailhouse-gcov-extract.c
@@ -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
@@ -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

@@ -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 <jan.kiszka@siemens.com>
@@ -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)

@@ -0,0 +1,222 @@
/*
* Jailhouse, a Linux-based partitioning hypervisor
*
* Copyright (c) Siemens AG, 2017
*
* Authors:
* Henning Schild <henning.schild@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;
}

0 comments on commit 6462339

Please sign in to comment.
You can’t perform that action at this time.