summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStuart Bennett <sb476@cam.ac.uk>2008-01-11 20:19:44 +0000
committerStuart Bennett <sb476@cam.ac.uk>2008-01-11 20:26:39 +0000
commit9837d4062872e8124cde7f32080716e8b75b028a (patch)
tree3cc8f8a8f8a47c492d0f775e9f261442c7221bbe
parent5bcf3e372e0db437238350a6e613366be51ef15e (diff)
Add some useful utils
-rw-r--r--README32
-rw-r--r--extrautils/Makefile11
-rw-r--r--extrautils/functionate.c374
-rw-r--r--extrautils/ioreplay.c63
-rw-r--r--extrautils/mmioify.c135
5 files changed, 603 insertions, 12 deletions
diff --git a/README b/README
index fa35e15..22d5cd1 100644
--- a/README
+++ b/README
@@ -14,28 +14,36 @@ Run make
Running:
./vbtracetool [-d] [-l] [-r] [-g | -p | -s MODENUMBER | -w]
--d gives trace output *to stderr*. you'll want to redirect that.
--l gives IO logging *to stderr*. you'll want to redirect that.
--r forces use of the shadow ram bios image
+ -d gives trace output *to stderr*. you'll want to redirect that.
+ -l gives IO logging *to stderr*. you'll want to redirect that.
+ -r forces use of the shadow ram bios image
--g gets the current display mode number (default)
--p posts the card. could induce several seconds of pants cacking with -d.
--s sets the given mode number
--w writes the bios image that would be executed by the other options *to stderr*
+ -g gets the current display mode number (default)
+ -p posts the card. could induce several seconds of pants cacking with -d.
+ -s sets the given mode number
+ -w writes the bios image that would be executed *to stderr*
./postandrestore.bash [-d] [-l] [-r]
-options as for vbtracetool
+ options as for vbtracetool
-this shell script will run vbtracetool in such a way to do a POST
-(which may be traced) and then restore the graphics mode (useful for fbcon)
+ this shell script will run vbtracetool in such a way to do a POST
+ (which may be traced) and then restore the graphics mode (useful for fbcon)
-
-try either of these in x and lose.
+*try either of these in x and lose.*
Bonus trace processor:
"./deloopify TRACENAME" will compact loops in your trace
+Extra utilities:
+some extra utilities are included in extrautils, for those using the traces
+they can be built by issuing "make" in that directory
+
+ functionate - splits out CALL ... RET blocks into "functions"
+ ioreplay - replays the IO of a vbtracetool IO log
+ mmioify - turns vbtracetool IO log output into something mmio-parse can
+ handle. output should not be used with mmio-replay
+
Known issues:
see KNOWN_ISSUES file
diff --git a/extrautils/Makefile b/extrautils/Makefile
new file mode 100644
index 0000000..2688945
--- /dev/null
+++ b/extrautils/Makefile
@@ -0,0 +1,11 @@
+PROGS = functionate ioreplay mmioify
+
+CFLAGS = -DDEBUG -Wall -O3 -std=gnu99
+
+all: $(PROGS)
+
+distclean: clean
+clean:
+ rm -f $(PROGS)
+
+install:
diff --git a/extrautils/functionate.c b/extrautils/functionate.c
new file mode 100644
index 0000000..d6d0063
--- /dev/null
+++ b/extrautils/functionate.c
@@ -0,0 +1,374 @@
+/*
+ * functionate, a tool to split "functions" out of vbtracetool traces
+ *
+ * Copyright 2007 Stuart Bennett <sb476@cam.ac.uk>
+ *
+ * This program is released under the terms of the GNU General Public License, version 2
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define MAX_CALLS 1000000
+#define MAX_CALLDEPTH 30
+#define MAX_FNS 1000
+
+struct cop {
+ uint16_t cs, ip;
+ uint16_t nextcs, nextip;
+ long line, retline;
+ fpos_t startpos, endpos;
+ int fn;
+ bool intr;
+};
+
+struct fn {
+ uint16_t cs, ip;
+ long len;
+ int count;
+ int instance;
+ bool hasvariance;
+ bool writeme;
+ char name[20];
+};
+
+struct fn func[MAX_FNS];
+struct cop call[MAX_CALLS];
+int calls = 0;
+int callstack[MAX_CALLDEPTH], calldepth = 0;
+FILE *tracef, *outf, *outfnsf;
+
+static bool read_line_plus_n(FILE *f, int n, char *outline)
+{
+ fpos_t current;
+ bool ok = true;
+
+ fgetpos(f, &current);
+ for (int i = 0; i < n; i++)
+ if (!fgets(outline, 180, f)) {
+ ok = false;
+ clearerr(f);
+ break;
+ }
+ fsetpos(f, &current);
+
+ return ok;
+}
+
+static void read_next_cs_ip(long lineno, uint16_t *cs, uint16_t *ip)
+{
+ char nextinstr[180];
+
+ if (!read_line_plus_n(tracef, 4, nextinstr)) {
+ printf("Trace appears to be truncated around line %ld\n", lineno);
+ exit(EXIT_FAILURE);
+ }
+ if (nextinstr[4] != ':') {
+ printf("Broken instruction spacing at line %ld\n", lineno + 4);
+ exit(EXIT_FAILURE);
+ }
+
+ *cs = strtoul(&nextinstr[0], NULL, 16);
+ *ip = strtoul(&nextinstr[5], NULL, 16);
+}
+
+static inline void add_call_common(long lineno, uint16_t lastip)
+{
+ read_next_cs_ip(lineno, &call[calls].cs, &call[calls].ip);
+
+ call[calls].line = lineno;
+ call[calls].nextip = lastip;
+ fgetpos(tracef, &call[calls].startpos);
+ callstack[calldepth] = calls;
+ if (++calldepth >= MAX_CALLDEPTH) {
+ printf("Too many nested calls\n");
+ exit(EXIT_FAILURE);
+ }
+ call[calls].retline = 0;
+ if (++calls >= MAX_CALLS) {
+ printf("Too many calls\n");
+ exit(EXIT_FAILURE);
+ }
+}
+
+static void writeoutfn(int stacklen, int fncallstack[stacklen])
+{
+ int skipcall = 0, stackptr = 0;
+ long start, finalretline;
+ char line[128];
+
+ finalretline = call[fncallstack[0]].retline;
+
+// printf("fn %s stacklen %d line %d retline %d\n", func[call[fncallstack[0]].fn].name, stacklen, call[fncallstack[0]].line, call[fncallstack[0]].retline);
+ fprintf(outfnsf, "%s (called %d times):\n\n", func[call[fncallstack[0]].fn].name, func[call[fncallstack[0]].fn].count);
+
+ do {
+ if (skipcall) {
+// printf("beep skipcall %d stacklen %d\n", skipcall, stacklen);
+ if (func[call[skipcall].fn].count > 1) {
+ fsetpos(tracef, &call[skipcall].endpos);
+ start = call[skipcall].retline;
+ } else
+ /* for hasvariance cases where we just need to change the call name */
+ start = call[skipcall].line;
+ } else {
+ fsetpos(tracef, &call[fncallstack[0]].startpos);
+ start = call[fncallstack[0]].line;
+ }
+
+ skipcall = 0;
+ for (++stackptr; stackptr < stacklen; stackptr++) {
+// printf("test sc of %d\n", stackptr);
+ if (call[fncallstack[stackptr]].line > start && call[fncallstack[stackptr]].line < finalretline) {
+ skipcall = fncallstack[stackptr];
+// printf("had to skipcall %d (%d)\n", stackptr, skipcall);
+// printf("call %d ip %04x line %d retline %d\n", skipcall, call[skipcall].ip, call[skipcall].line, call[skipcall].retline);
+ break;
+ }
+ }
+ long stop;
+ if (skipcall)
+ stop = call[skipcall].line;
+ else
+ stop = finalretline;
+ if (start == stop)
+ break;
+
+ for (int i = 0; i < stop - start - 1; i++) {
+ fgets(line, 80, tracef);
+ fwrite(line, strlen(line), 1, outfnsf);
+ }
+ fgets(line, 80, tracef);
+ if (skipcall) {
+ strcpy(&line[strlen(line) - 1], " ");
+ sprintf(&line[call[skipcall].intr ? 55 : 48], "(%s%s", func[call[skipcall].fn].name, func[call[skipcall].fn].count > 1 ? ")\n\n" : " (inline))\n");
+ }
+ fwrite(line, strlen(line), 1, outfnsf);
+ } while (skipcall);
+
+ fprintf(outfnsf, "\n\n");
+}
+
+int main(int argc, char *argv[])
+{
+ int fns = 0;
+ long lineno = 0;
+
+ char outname[1024];
+ strcpy(outname, argv[1]);
+ strcat(outname, "-fn");
+ char fnsoutname[1024];
+ strcpy(fnsoutname, argv[1]);
+ strcat(fnsoutname, "-fns");
+ if (!(tracef = fopen(argv[1], "r"))) {
+ printf("File open failed\n");
+ exit(EXIT_FAILURE);
+ }
+ if (!(outf = fopen(outname, "w"))) {
+ printf("File open failed\n");
+ exit(EXIT_FAILURE);
+ }
+ if (!(outfnsf = fopen(fnsoutname, "w"))) {
+ printf("File open failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ printf("Building tree\n");
+
+ uint16_t lastip = 0;
+ while (!feof(tracef)) {
+ char line[180];
+
+ if (!fgets(line, 180, tracef))
+ break;
+ lineno += 1;
+
+ /* sadly we can't do this by simple matching of calls and rets,
+ * as sometimes the multi-ret opcodes are used, or pushes and
+ * pops are unbalanced and a ret goes to somewhere odd.
+ * neither can we do it by tracking SP, as SP can get messed with
+ */
+ if (strstr(line, "EIP"))
+ lastip = strtoul(&line[39], NULL, 16);
+
+ if (strstr(line, "CALL")) {
+ call[calls].intr = false;
+ call[calls].nextcs = strtoul(&line[0], NULL, 16);
+ add_call_common(lineno, lastip);
+ } else if (strstr(line, "INT")) { // if you can retf from an int, it's a call :)
+ call[calls].intr = true;
+ call[calls].nextcs = strtoul(&line[0], NULL, 16);
+ add_call_common(lineno, lastip);
+ } else if (strstr(line, "RET")) {
+ uint16_t nextcs, nextip;
+
+ read_next_cs_ip(lineno, &nextcs, &nextip);
+
+ if (calldepth) {
+ int newcd;
+ bool borken = false;
+
+ calldepth--;
+
+ for (newcd = calldepth; newcd >= 0; newcd--) {
+ if (call[callstack[newcd]].nextip == nextip && call[callstack[newcd]].nextcs == nextcs)
+ break;
+ else {
+ if (!borken)
+ printf("hmm, no matching call for the ret at line %ld to %04x:%04x. let's try higher in the call stack...", lineno, nextcs, nextip);
+ borken = true;
+ }
+ }
+ if (newcd >= 0) {
+ /* for the case where newcd != calldepth, fixup the tree so it still separates
+ * by terminating the nested unreturned calls at the same time */
+ for (int i = calldepth; i >= newcd; i--) {
+ call[callstack[i]].retline = lineno;
+ fgetpos(tracef, &call[callstack[i]].endpos);
+ }
+ calldepth = newcd;
+ if (borken)
+ printf(" yay!\n");
+ } else {
+ /* parsed code is undoubtedly broken. ignore ret and hope to muddle on */
+ calldepth++;
+ printf("\nerrk, no matching call for this ret. has nvidia broken the stack again?\n");
+ }
+ } else {
+ if (nextip == 0 && nextcs == 0)
+ break;
+
+ printf("arrgl, too many RETs at line %ld\n", lineno + 1);
+ call[callstack[0]].retline = lineno;
+// exit(EXIT_FAILURE);
+ }
+ }
+ }
+
+ /* complete the tree */
+ while (calldepth-- > 0)
+ call[callstack[calldepth]].retline = lineno;
+
+ for (int i = 0; i < calls; i++) {
+ bool handled = false, hasvariance = false;
+ int newvariant = 0;
+
+ if (call[i].retline == 0)
+ continue;
+
+ for (int j = 0; j < fns; j++)
+ if (func[j].ip == call[i].ip && func[j].cs == call[i].cs) {
+ if (func[j].len == call[i].retline - call[i].line) {
+ call[i].fn = j;
+ func[j].count++;
+ func[j].writeme = true;
+ handled = true;
+ break;
+ } else {
+ func[j].hasvariance = hasvariance = true;
+ newvariant++;
+ }
+ }
+ if (!handled) {
+ call[i].fn = fns;
+ func[fns].cs = call[i].cs;
+ func[fns].ip = call[i].ip;
+ func[fns].len = call[i].retline - call[i].line;
+ func[fns].hasvariance = hasvariance;
+ func[fns].instance = i;
+ func[fns].count = 1;
+ func[fns].writeme = false;
+ snprintf(func[fns].name, 20, "%s_%04x:%04x_VAR%02d", call[i].intr ? "INT" : "FN", func[fns].cs, func[fns].ip, newvariant);
+ if (++fns >= MAX_FNS) {
+ printf("Too many functions\n");
+ exit(EXIT_FAILURE);
+ }
+ }
+ }
+
+ printf("Splitting out functions\n");
+
+ int i = 0;
+ while (i < fns) {
+// printf("i is %d\n", i);
+ int fncallstack[MAX_CALLS];
+ int stackptr = 0, stackmax;
+ if (!func[i].writeme) {
+ i++;
+ continue;
+ }
+ int j = func[i].instance;
+ while (j < calls) {
+ if (func[call[j].fn].count > 1 || func[call[j].fn].hasvariance) {
+ if (call[j].retline > call[func[i].instance].retline)
+ break;
+// printf("adding j of %d (ip %04x, line %d, retline %d) at %d\n", j, call[j].ip, call[j].line, call[j].retline, stackptr);
+ fncallstack[stackptr++] = j;
+ }
+ j++;
+ }
+ stackmax = stackptr;
+// printf("stackmax %d\n", stackmax);
+ while (stackptr--)
+ if (func[call[fncallstack[stackptr]].fn].writeme) {
+// printf("\n\npassing %d\n", stackmax - stackptr);
+ writeoutfn(stackmax - stackptr, &fncallstack[stackptr]);
+ func[call[fncallstack[stackptr]].fn].writeme = false;
+ }
+ i++;
+ }
+
+ fclose(outfnsf);
+
+ printf("Compacting trace\n");
+
+ fseek(tracef, 0, SEEK_SET);
+
+ long start = 0;
+ for (int nextcall = 0; nextcall < calls; nextcall++) {
+ char line[128]; // as funny as it is, with this missing, gcc complains about the cop line...
+
+ if (!(func[call[nextcall].fn].count > 1) && !func[call[nextcall].fn].hasvariance)
+ continue;
+
+ long stop = call[nextcall].line;
+ for (int i = 0; i < stop - start - 1; i++) {
+ fgets(line, 80, tracef);
+ fwrite(line, strlen(line), 1, outf);
+ }
+// fprintf(outf, "stop %d start %d len %d\n", stop, start, stop-start);
+ fgets(line, 80, tracef);
+ strcpy(&line[strlen(line) - 1], " ");
+ sprintf(&line[call[nextcall].intr ? 55 : 48], "(%s%s", func[call[nextcall].fn].name, func[call[nextcall].fn].count > 1 ? ")\n\n" : " (inline))\n");
+ fwrite(line, strlen(line), 1, outf);
+
+ if (func[call[nextcall].fn].count > 1) {
+ fsetpos(tracef, &call[nextcall].endpos);
+ start = call[nextcall].retline;
+ int lastcall = nextcall;
+ for (; nextcall < calls; nextcall++)
+ if (call[lastcall].retline < call[nextcall].line) {
+ nextcall--;
+ break;
+ }
+ } else
+ start = stop;
+ }
+
+ while (!feof(tracef)) {
+ char line[80];
+
+ if (!fgets(line, 80, tracef))
+ break;
+ fwrite(line, strlen(line), 1, outf);
+ }
+
+ fclose(tracef);
+ fclose(outf);
+
+ return 0;
+}
diff --git a/extrautils/ioreplay.c b/extrautils/ioreplay.c
new file mode 100644
index 0000000..d848cdd
--- /dev/null
+++ b/extrautils/ioreplay.c
@@ -0,0 +1,63 @@
+/*
+ * ioreplay, a vbtracetool trace replayer. use with care
+ *
+ * Copyright 2008 Stuart Bennett <sb476@cam.ac.uk>
+ *
+ * This program is released under the terms of the GNU General Public License, version 2
+ */
+
+#include <stdio.h>
+#include <sys/io.h>
+
+#define __BUILDIO(bwl,bw,type) \
+static inline void out##bwl##_local(unsigned long port, unsigned type value) { __asm__ __volatile__("out" #bwl " %" #bw "0, %w1" : : "a"(value), "Nd"(port)); \
+}\
+static inline unsigned type in##bwl##_local(unsigned long port) { \
+ unsigned type value; \
+ __asm__ __volatile__("in" #bwl " %w1, %" #bw "0" : "=a"(value) : "Nd"(port)); \
+ return value; \
+}\
+
+__BUILDIO(b,b,char)
+__BUILDIO(w,w,short)
+__BUILDIO(l,,int)
+
+int main(int argc, char *argv[])
+{
+ FILE *io;
+ char line[100];
+ int port, data;
+
+ ioperm(0, 1024, 1);
+ iopl(3);
+
+ if (!(io = fopen(argv[1], "r"))) {
+ printf("File open failed\n");
+ return 1;
+ }
+
+ while (!feof(io)) {
+ if (!fgets(line, 100, io))
+ return 0;
+
+ if (line[10] != 'W') {
+#if 0
+ // slow shit down
+ volatile int i;
+ for (i = 0; i < 10000; i++)
+ ;
+#endif
+ continue;
+ }
+
+ sscanf(line, "%*x:%*x W%*d port: %x data: %x", &port, &data);
+
+ /* don't handle 32 bit writes yet */
+ if (line[11] == '1')
+ outw_local(port, data);
+ else
+ outb_local(port, data);
+ }
+
+ return 0;
+}
diff --git a/extrautils/mmioify.c b/extrautils/mmioify.c
new file mode 100644
index 0000000..bd63406
--- /dev/null
+++ b/extrautils/mmioify.c
@@ -0,0 +1,135 @@
+/*
+ * mmioify turns vbtracetool nvidia io logs into mmiotrace logs:
+ *
+ * Copyright 2007 Stuart Bennett <sb476@cam.ac.uk>
+ *
+ * This program is released under the terms of the GNU General Public License, version 2
+ *
+ * output file log-mmt can be parsed with mmio-parse as normal
+ * output file *should not* be run with mmio-replay, as the mappings are made up
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define PCIO0 0x00601000
+
+void cheesy_io_parser(char rw, long lineno, int size, uint16_t port, uint32_t data, FILE *outf)
+{
+ static uint32_t address = 0;
+
+ if (address) {
+ if (size == 2) {
+ static int bottom = -1;
+ if (bottom >= 0) {
+ if (port == 0x3d2) {
+ fprintf(outf, "%c 4 %ld 1 0x%08x 0x%08x 0x0 0\n", rw, lineno, address, data << 16 | bottom);
+ address = 0;
+ }
+ bottom = -1;
+ }
+ if (port == 0x3d0)
+ bottom = data;
+ }
+ if (size == 4 && (port == 0x3d0 || port == 0xe80c)) {
+ fprintf(outf, "%c 4 %ld 1 0x%08x 0x%08x 0x0 0\n", rw, lineno, address, data);
+ address = 0;
+ }
+ }
+ if (rw == 'W' && !address) {
+ if (size == 2) {
+ static int top = -1;
+ if (top >= 0) {
+ if (port == 0x3d0)
+ address = top | data;
+ top = -1;
+ }
+ if (port == 0x3d2)
+ top = data << 16;
+ }
+ if (size == 4 && (port == 0x3d0 || port == 0xe808))
+ address = data;
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ FILE *tracef, *outf;
+ long lineno = 0;
+
+ char outname[1024];
+ strcpy(outname, argv[1]);
+ strcat(outname, "-mmt");
+ if (!(tracef = fopen(argv[1], "r"))) {
+ printf("File open failed\n");
+ exit(EXIT_FAILURE);
+ }
+ if (!(outf = fopen(outname, "w"))) {
+ printf("File open failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ fprintf(outf, "PCIDEV 0000 10de0000 0 00000000 0 0 0 0 0 0 1000000 0 0 0 0 0 0\n");
+ fprintf(outf, "MAP 0 1 0x00000000 0x00000000 0x1000000 0x0 0\n");
+
+ while (!feof(tracef)) {
+ char line[180];
+ char rw;
+ int size = 0;
+ uint16_t port;
+ uint32_t data;
+
+ if (!fgets(line, 180, tracef))
+ break;
+ lineno += 1;
+
+ if (strncmp(line, "c000", 4)) /* ignore in-built cheesy io parser's output */
+ continue;
+
+ rw = line[10];
+ switch (line[11]) {
+ case '8':
+ size = 1;
+ break;
+ case '1':
+ size = 2;
+ break;
+ case '3':
+ size = 4;
+ break;
+ }
+ port = strtoul(&line[20], NULL, 16);
+ data = strtoul(&line[31], NULL, 16);
+
+ if (port == 0x3d0 || port == 0x3d2 || port == 0xe808 || port == 0xe80c) {
+ cheesy_io_parser(rw, lineno, size, port, data, outf);
+ continue;
+ }
+
+ switch (size) {
+ case 1:
+ fprintf(outf, "%c 1 %ld 1 0x%08x 0x%02x 0x0 0\n", rw, lineno, PCIO0 + port, data);
+ break;
+ case 2:
+ fprintf(outf, "%c 1 %ld 1 0x%08x 0x%02x 0x0 0\n", rw, lineno, PCIO0 + port, data & 0xff);
+ fprintf(outf, "%c 1 %ld 1 0x%08x 0x%02x 0x0 0\n", rw, lineno, PCIO0 + port + 1, (data & 0xff00) >> 8);
+ break;
+ case 4:
+ fprintf(outf, "%c 1 %ld 1 0x%08x 0x%02x 0x0 0\n", rw, lineno, PCIO0 + port, data & 0xff);
+ fprintf(outf, "%c 1 %ld 1 0x%08x 0x%02x 0x0 0\n", rw, lineno, PCIO0 + port + 1, (data & 0xff00) >> 8);
+ fprintf(outf, "%c 1 %ld 1 0x%08x 0x%02x 0x0 0\n", rw, lineno, PCIO0 + port + 2, (data & 0xff0000) >> 16);
+ fprintf(outf, "%c 1 %ld 1 0x%08x 0x%02x 0x0 0\n", rw, lineno, PCIO0 + port + 3, (data & 0xff000000) >> 24);
+ break;
+ case 0:
+ printf("size 0!?\n");
+ return 1;
+ }
+ }
+
+ fclose(tracef);
+ fclose(outf);
+
+ return 0;
+}