[Skiboot] [PATCH 07/11] Add extract-gcov utility for extracting gcda from skiboot dump

Stewart Smith stewart at linux.vnet.ibm.com
Thu May 7 17:11:46 AEST 2015


If you dump the (relocated) skiboot memory (2MB, from 0x30000000)
and point extract-gcov at it, you'll get a bunch of gcda files
extracted in pwd that you can then feed to gcov to get real usage
data.

This is a different approach than, say, the linux kernel, which when
built with gcov provides a debugfs interface to the gcda files that
you can just copy with normal userspace utilities.

For skiboot, I have no desire to add a VFS style interface and since
if you're dealing with GCOV+skiboot you should probably pretty well
know what you're doing, parsing the gcov data structures in userspace
from a dump of memory is actually not too bad.

You can grab this memory from linux, FSP, mambo or any debug mechanism that
lets you dump out a section of physical memory.

Signed-off-by: Stewart Smith <stewart at linux.vnet.ibm.com>
---
 Makefile.main  |    5 +-
 extract-gcov.c |  220 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 224 insertions(+), 1 deletion(-)
 create mode 100644 extract-gcov.c

diff --git a/Makefile.main b/Makefile.main
index c01b16b..310601d 100644
--- a/Makefile.main
+++ b/Makefile.main
@@ -119,7 +119,7 @@ include $(SRC)/libc/Makefile.inc
 include $(SRC)/ccan/Makefile.inc
 include $(SRC)/$(DEVSRC)/Makefile.inc
 
-all: $(SUBDIRS) $(TARGET).lid $(TARGET).map
+all: $(SUBDIRS) $(TARGET).lid $(TARGET).map extract-gcov
 
 OBJS := $(ASM) $(CORE) $(HW) $(PLATFORMS) $(LIBFDT) $(LIBFLASH)
 ifeq ($(PORE),1)
@@ -171,6 +171,9 @@ version.c: $(SRC)/make_version.sh $(OBJS_NO_VER) .version
 .PHONY: coverage
 include $(shell find $(SRC)/* -name Makefile.check)
 
+extract-gcov: extract-gcov.c
+	$(call Q, HOSTCC ,$(HOSTCC) $(HOSTCFLAGS) -O0 -g -I . -o $@ $<,$<)
+
 coverage-report: skiboot.info
 	genhtml -q -o $@ $<
 
diff --git a/extract-gcov.c b/extract-gcov.c
new file mode 100644
index 0000000..dfbc961
--- /dev/null
+++ b/extract-gcov.c
@@ -0,0 +1,220 @@
+/* Copyright 2015 IBM Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * 	http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define _BSD_SOURCE
+#include <ccan/short_types/short_types.h>
+#include <endian.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/mman.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+typedef unsigned int gcov_unsigned_int;
+
+#if __GNUC__ == 4 && __GNUC_MINOR__ >= 9
+#define GCOV_COUNTERS                   9
+#else
+#define GCOV_COUNTERS                   8
+#endif
+typedef u64 gcov_type;
+
+struct gcov_info
+{
+        gcov_unsigned_int version;
+        struct gcov_info *next;
+        gcov_unsigned_int stamp;
+        const char *filename;
+        void (*merge[GCOV_COUNTERS])(gcov_type *, unsigned int);
+        unsigned int n_functions;
+        struct gcov_fn_info **functions;
+};
+
+struct gcov_ctr_info {
+        gcov_unsigned_int num;
+        gcov_type *values;
+};
+
+struct gcov_fn_info {
+        const struct gcov_info *key;
+        unsigned int ident;
+        unsigned int lineno_checksum;
+        unsigned int cfg_checksum;
+//        struct gcov_ctr_info ctrs[0];
+};
+
+
+/* We have a list of all gcov info set up at startup */
+struct gcov_info *gcov_info_list;
+
+#define SKIBOOT_OFFSET 0x30000000
+
+/* Endian of the machine producing the gcda. Which mean BE.
+ * because skiboot is BE.
+ * If skiboot is ever LE, go have fun.
+ */
+static size_t write_u32(int fd, u32 _v)
+{
+	u32 v = htobe32(_v);
+	return write(fd, &v, sizeof(v));
+}
+
+static size_t write_u64(int fd, u64 v)
+{
+	u32 b[2];
+	b[0] = htobe32(v & 0xffffffffUL);
+	b[1] = htobe32(v >> 32);
+
+	write(fd, &b[0], sizeof(u32));
+	write(fd, &b[1], sizeof(u32));
+	return sizeof(u64);
+}
+
+#define GCOV_DATA_MAGIC         ((unsigned int) 0x67636461)
+#define GCOV_TAG_FUNCTION       ((unsigned int) 0x01000000)
+#define GCOV_TAG_COUNTER_BASE   ((unsigned int) 0x01a10000)
+#define GCOV_TAG_FOR_COUNTER(count)                                     \
+        (GCOV_TAG_COUNTER_BASE + ((unsigned int) (count) << 17))
+
+// gcc 4.7/4.8 specific
+#define GCOV_TAG_FUNCTION_LENGTH        3
+
+static inline const char* SKIBOOT_ADDR(const char* addr, const void* p)
+{
+	const char* r= (addr + (be64toh((const u64)p) - SKIBOOT_OFFSET));
+	assert(r < (addr + 0x240000));
+	return r;
+}
+
+static int counter_active(struct gcov_info *info, unsigned int type)
+{
+        return info->merge[type] ? 1 : 0;
+}
+
+static void write_gcda(char *addr, struct gcov_info* gi)
+{
+	const char* filename = SKIBOOT_ADDR(addr, gi->filename);
+	int fd;
+	u32 fn;
+	struct gcov_fn_info *fn_info;
+	struct gcov_fn_info **functions;
+	struct gcov_ctr_info *ctr_info;
+	u32 ctr;
+	u32 cv;
+
+	printf("Writing %s\n", filename);
+
+	fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR);
+	write_u32(fd, GCOV_DATA_MAGIC);
+	write_u32(fd, be32toh(gi->version));
+	write_u32(fd, be32toh(gi->stamp));
+
+	printf("nfunctions: %d \n", be32toh(gi->n_functions));
+
+	for(fn = 0; fn < be32toh(gi->n_functions); fn++) {
+		functions = (struct gcov_fn_info**)
+			SKIBOOT_ADDR(addr, gi->functions);
+
+		fn_info = (struct gcov_fn_info*)
+			SKIBOOT_ADDR(addr, functions[fn]);
+
+		write_u32(fd, GCOV_TAG_FUNCTION);
+		write_u32(fd, GCOV_TAG_FUNCTION_LENGTH);
+		write_u32(fd, be32toh(fn_info->ident));
+		write_u32(fd, be32toh(fn_info->lineno_checksum));
+		write_u32(fd, be32toh(fn_info->cfg_checksum));
+
+		ctr_info = (struct gcov_ctr_info*)
+			((char*)fn_info + sizeof(struct gcov_fn_info));
+
+		for(ctr = 0; ctr < GCOV_COUNTERS; ctr++) {
+			if (!counter_active(gi, ctr))
+				continue;
+
+			write_u32(fd, (GCOV_TAG_FOR_COUNTER(ctr)));
+			write_u32(fd, be32toh(ctr_info->num)*2);
+			printf(" ctr %d gcov_ctr_info->num %u\n",
+			       ctr, be32toh(ctr_info->num));
+
+			for(cv = 0; cv < be32toh(ctr_info->num); cv++) {
+				gcov_type *ctrv = (gcov_type *)
+					SKIBOOT_ADDR(addr, ctr_info->values);
+				printf("%lx\n", be64toh(ctrv[cv]));
+				write_u64(fd, be64toh(ctrv[cv]));
+			}
+			ctr_info++;
+		}
+	}
+
+	close(fd);
+}
+
+
+int main(int argc, char *argv[])
+{
+	int r;
+	int fd;
+	struct stat sb;
+	char *addr;
+	u64 gcov_list_addr;
+
+	printf("sizes: %zu %zu %zu %zu\n",
+	       sizeof(gcov_unsigned_int),
+	       sizeof(struct gcov_ctr_info),
+	       sizeof(struct gcov_fn_info),
+	       sizeof(struct gcov_info));
+
+	if (argc < 3) {
+		fprintf(stderr, "Usage:\n"
+			"\t%s skiboot.dump gcov_offset\n\n",
+			argv[0]);
+		return -1;
+	}
+
+	/* argv[1] = skiboot.dump */
+	fd = open(argv[1], O_RDONLY);
+	assert(fd > 0);
+
+	r = fstat(fd, &sb);
+	assert(r != -1);
+
+	addr = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+	assert(addr != NULL);
+
+	printf("Skiboot memory dump %p - %p\n",
+	       (void*)SKIBOOT_OFFSET, (void*)SKIBOOT_OFFSET+sb.st_size);
+
+	gcov_list_addr = strtoll(argv[2], NULL, 0);
+
+	printf("Skiboot gcov_info_list at %p\n", (void*)gcov_list_addr);
+
+	do {
+		gcov_info_list = (struct gcov_info *)(addr + (gcov_list_addr - SKIBOOT_OFFSET));
+		write_gcda(addr, gcov_info_list);
+		gcov_list_addr = be64toh((u64)gcov_info_list->next);
+
+	} while(gcov_list_addr);
+
+	munmap(addr, sb.st_size);
+	close(fd);
+
+	return 0;
+}
-- 
1.7.10.4



More information about the Skiboot mailing list