diff options
author | Stuart Bennett <sb476@cam.ac.uk> | 2008-01-11 20:19:44 +0000 |
---|---|---|
committer | Stuart Bennett <sb476@cam.ac.uk> | 2008-01-11 20:26:39 +0000 |
commit | 9837d4062872e8124cde7f32080716e8b75b028a (patch) | |
tree | 3cc8f8a8f8a47c492d0f775e9f261442c7221bbe | |
parent | 5bcf3e372e0db437238350a6e613366be51ef15e (diff) |
Add some useful utils
-rw-r--r-- | README | 32 | ||||
-rw-r--r-- | extrautils/Makefile | 11 | ||||
-rw-r--r-- | extrautils/functionate.c | 374 | ||||
-rw-r--r-- | extrautils/ioreplay.c | 63 | ||||
-rw-r--r-- | extrautils/mmioify.c | 135 |
5 files changed, 603 insertions, 12 deletions
@@ -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, ¤t); + for (int i = 0; i < n; i++) + if (!fgets(outline, 180, f)) { + ok = false; + clearerr(f); + break; + } + fsetpos(f, ¤t); + + 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; +} |