diff options
author | Rudolf Marek <r.marek@assembler.cz> | 2011-09-02 23:34:15 +0200 |
---|---|---|
committer | Stefan Reinauer <stefan.reinauer@coreboot.org> | 2011-09-07 01:27:57 +0200 |
commit | 8679e52b9632254c247db31020d09e877071366e (patch) | |
tree | e86c734a8b11aa60f0f72612b848640ddd51c204 | |
parent | 7f0e93060e720149bb59023d608a67cfc21542b1 (diff) | |
download | coreboot-8679e52b9632254c247db31020d09e877071366e.tar.gz coreboot-8679e52b9632254c247db31020d09e877071366e.tar.bz2 coreboot-8679e52b9632254c247db31020d09e877071366e.zip |
Add support utils for tracing
Following patch adds a userspace util genprof
which is able to convert the console printed
traces to gmon.out file used by gprof & friends.
The log2dress will replace the adresses in logfile
with a line numbers.
Change-Id: I9f716f3ff2522a24fbc844a1dd5e32ef49b540c5
Signed-off-by: Rudolf Marek <r.marek@assembler.cz>
Reviewed-on: http://review.coreboot.org/179
Tested-by: build bot (Jenkins)
Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
-rw-r--r-- | util/genprof/Makefile | 12 | ||||
-rw-r--r-- | util/genprof/README | 31 | ||||
-rw-r--r-- | util/genprof/genprof.c | 108 | ||||
-rwxr-xr-x | util/genprof/log2dress | 20 |
4 files changed, 171 insertions, 0 deletions
diff --git a/util/genprof/Makefile b/util/genprof/Makefile new file mode 100644 index 000000000000..2ec77c918a55 --- /dev/null +++ b/util/genprof/Makefile @@ -0,0 +1,12 @@ +CC=gcc +CFLAGS=-O2 -Wall + +all: genprof + +genprof: genprof.o + $(CC) $(CFLAGS) -o genprof $^ + +clean: + rm -f genprof *.o *~ + +distclean: clean diff --git a/util/genprof/README b/util/genprof/README new file mode 100644 index 000000000000..3483a2b22ba6 --- /dev/null +++ b/util/genprof/README @@ -0,0 +1,31 @@ +Function tracing +---------------- + +Enable CONFIG_TRACE in debug menu. Run the compiled image on target. You will get +a log with a lot of lines like: + +... +~0x001072e8(0x00100099) +~0x00108bc0(0x0010730a) +... + +First address is address of function which was just entered, the second address +is address of functions which call that. + +You can use the log2dress to dress the log again: + +... +src/arch/x86/lib/c_start.S:85 calls /home/ruik/coreboot/src/boot/selfboot.c:367 +/home/ruik/coreboot/src/boot/selfboot.c:370 calls /home/ruik/coreboot/src/devices/device.c:325 +... + +Alternatively, you can use genprof to generate a gmon.out file, which can be used +by gprof to show the call traces. You will need to install uthash library to compile +that. + +Great use is: + +make +./genprof /tmp/yourlog ; gprof ../../build/coreboot_ram | ./gprof2dot.py -e0 -n0 | dot -Tpng -o output.png + +Which generates a PNG with a call graph. diff --git a/util/genprof/genprof.c b/util/genprof/genprof.c new file mode 100644 index 000000000000..9fc39da982f8 --- /dev/null +++ b/util/genprof/genprof.c @@ -0,0 +1,108 @@ +#include <stdio.h> +#include <uthash.h> +#include <sys/gmon_out.h> +#include <stdlib.h> + +#define GMON_SEC "seconds s" +uint32_t mineip = 0xffffffff; +uint32_t maxeip = 0; + +/* a hash structure to hold the arc */ +struct arec { + uint32_t eip; + uint32_t from; + uint32_t count; + UT_hash_handle hh; +}; + +struct arec *arc = NULL; + +void note_arc(uint32_t eip, uint32_t from) +{ + struct arec *s; + + HASH_FIND_INT(arc, &eip, s); + if (s == NULL) { + s = malloc(sizeof(struct arec)); + s->eip = eip; + s->from = from; + s->count = 1; + if (eip > maxeip) + maxeip = eip; + if (eip < mineip) + maxeip = eip; + + HASH_ADD_INT(arc, eip, s); + } else { + s->count++; + } +} + +int main(int argc, char* argv[]) +{ + FILE *f, *fo; + struct arec *s; + uint32_t eip, from, tmp; + uint8_t tag; + uint16_t hit; + + if ( argc < 2 ) + { + fprintf(stderr, "Please specify the coreboot trace log as parameter\n"); + return 1; + } + + f = fopen(argv[1], "r"); + fo = fopen("gmon.out", "w+"); + + if ((f == NULL) || (fo == NULL)) { + fprintf(stderr, "Unable to manipulate with the input file\n"); + return 1; + } + + while (!feof(f)) { + if (fscanf(f, "~%x(%x)%*[^\n]\n", &eip, &from) == 2) { + note_arc(eip, from); + } else if (fscanf(f, "%*c~%x(%x)%*[^\n]\n", &eip, &from) == 2) { + note_arc(eip, from); + } else { + /* just drop a line */ + tmp = fscanf(f, "%*[^\n]\n"); + } + } + + /* write gprof header */ + fwrite(GMON_MAGIC, 1, sizeof(GMON_MAGIC) - 1, fo); + tmp = GMON_VERSION; + fwrite(&tmp, 1, sizeof(tmp), fo); + tmp = 0; + fwrite(&tmp, 1, sizeof(tmp), fo); + fwrite(&tmp, 1, sizeof(tmp), fo); + fwrite(&tmp, 1, sizeof(tmp), fo); + /* write fake histogram */ + tag = GMON_TAG_TIME_HIST; + fwrite(&tag, 1, sizeof(tag), fo); + fwrite(&mineip, 1, sizeof(mineip), fo); + fwrite(&maxeip, 1, sizeof(maxeip), fo); + /* size of histogram */ + tmp = 1; + fwrite(&tmp, 1, sizeof(tmp), fo); + /* prof rate */ + tmp = 1000; + fwrite(&tmp, 1, sizeof(tmp), fo); + fwrite(GMON_SEC, 1, sizeof(GMON_SEC) - 1, fo); + hit = 1; + fwrite(&hit, 1, sizeof(hit), fo); + + /* write call graph data */ + tag = GMON_TAG_CG_ARC; + for (s = arc; s != NULL; s = s->hh.next) { + fwrite(&tag, 1, sizeof(tag), fo); + fwrite(&s->from, 1, sizeof(s->from), fo); + fwrite(&s->eip, 1, sizeof(s->eip), fo); + fwrite(&s->count, 1, sizeof(s->count), fo); + } + + fclose(fo); + return 0; +} diff --git a/util/genprof/log2dress b/util/genprof/log2dress new file mode 100755 index 000000000000..429f8461d16f --- /dev/null +++ b/util/genprof/log2dress @@ -0,0 +1,20 @@ +#!/bin/bash +#Parse a log and get back the function names and line numbers +#Provide a log file as first argument + +#Please rewrite to something more saner ! + +cat $1 | while read line ; do +A=`echo $line | cut -c 1` + +if [ "$A" = '~' ] ; then +FROM=`echo $line | tr \~ \( | tr \) \( | awk -F\( '{print $3}'` +TO=`echo $line | tr \~ \( | tr \) \(|awk -F\( '{print $2}'` +addr2line -e ../../build/coreboot_ram.debug "$FROM" | tr -d "\n" +echo -n " calls " +addr2line -e ../../build/coreboot_ram.debug "$TO" +else +echo "$line" +fi + +done |