summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--util/genprof/Makefile12
-rw-r--r--util/genprof/README31
-rw-r--r--util/genprof/genprof.c108
-rwxr-xr-xutil/genprof/log2dress20
4 files changed, 171 insertions, 0 deletions
diff --git a/util/genprof/Makefile b/util/genprof/Makefile
new file mode 100644
index 0000000000..2ec77c918a
--- /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 0000000000..3483a2b22b
--- /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 0000000000..9fc39da982
--- /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 0000000000..429f8461d1
--- /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