#include #include #include #include #include enum { Qbitblt = 1, Qroot = CHDIR }; typedef struct Fid Fid; struct Fid { int busy; int fid; Fid *next; long qidpath; }; int detail; char *bmdumpfile; char *outfile = "btrace.out"; Biobuf *out; Bitmap *bhead; Fid *fidhead; Rectangle rrect; int rldepth; int rid; int rtype; int srvfd, bitfd; int srvpid, rcpid; long rminy, rmaxy; Dir rootdir = { ".", "btrace", "btrace", {Qroot, 0}, CHDIR|0777 }; Dir bitbltdir = { "bitblt", "btrace", "btrace", {Qbitblt, 0}, 0666 }; Fcall rhdr, thdr; char data[MAXMSG+MAXFDATA]; char repdata[EMAXMSG]; char errbuf[ERRLEN]; int req(uchar *, int); void rep(uchar *, int); Point bgpt(uchar *); Rectangle bgrect(uchar *); int Aconv(void *, int, int, int, int); int Pconv(void *, int, int, int, int); int Rconv(void *, int, int, int, int); void dumpbmrows(uchar *, int, int, Rectangle); void printfontchars(uchar *, int); void printcmap(uchar *, int); Bitmap *bmlook(int); Fid *fidlook(int); void srv(void); char *bitwrite(void); char *bitread(void); void catcher(void *, char *); void main(int argc, char **argv) { int p[2], wpid; Waitmsg w; ARGBEGIN{ case 'd': detail++; break; case 'b': bmdumpfile = ARGF(); break; case 'o': outfile = ARGF(); break; default: usage: fprint(2, "usage: btrace [-d[d]] [-o file] [-b bitmapfile]\n"); exits("usage"); }ARGEND notify(catcher); out = Bopen(outfile, OWRITE); if(!out) { fprint(2, "%s: can't open trace file %s\n", argv0, outfile); exits("can't open trace file"); } fmtinstall('R', Rconv); fmtinstall('P', Pconv); fmtinstall('A', Aconv); /* fmtinstall('F', fcallconv); */ bitfd = open("/dev/bitblt", ORDWR); if(bitfd < 0) { fprint(2, "%s: error opening /dev/bitblt: %r\n", argv0); exits("can't open /dev/bitblt"); } if(pipe(p) < 0) { fprint(2, "%s: error making pipe: %r\n", argv0); exits("can't make pipe"); } srvfd = p[1]; switch((srvpid = rfork(RFPROC|RFNOTEG|RFFDG|RFNAMEG))) { case -1: fprint(2, "%s: error forking server: %r\n", argv0); exits("can't fork"); case 0: close(p[0]); srv(); exits(0); default: close(srvfd); close(bitfd); } if(mount(p[0], "/dev", MBEFORE, "", "") < 0) { fprint(2, "%s: error mounting on /dev: %r\n", argv0); exits("can't mount\n"); } switch((rcpid = rfork(RFPROC|RFFDG))) { case -1: fprint(2, "%s: error forking shell: %r\n", argv0); exits("can't fork"); case 0: execl("/bin/rc", "rc", 0); } wpid = wait(&w); if(wpid > 0) { if(wpid == rcpid) postnote(srvpid, "kill"); else postnote(rcpid, "kill"); } exits(0); } void srv(void) { long n, q; char *err; Fid *f, *nf; while((n = read(srvfd, data, sizeof data)) >= 0) { if(convM2S(data, &rhdr, n) == 0) break; /* fprint(2, "%s: fcall %F\n", argv0, &rhdr); */ f = fidlook(rhdr.fid); q = f->qidpath; err = 0; nf = 0; switch(rhdr.type) { case Tflush: case Tsession: case Tnop: break; case Tcreate: case Tremove: case Twstat: err = "permission denied"; break; case Tauth: err = "no authentication"; break; case Tattach: f->qidpath = Qroot; f->busy = 1; thdr.qid.path = Qroot; thdr.qid.vers = 0; break; case Tclone: nf = fidlook(rhdr.newfid); nf->busy = 1; nf->qidpath = q; thdr.qid.path = q; thdr.qid.vers = 0; break; case Tclwalk: nf = fidlook(rhdr.newfid); nf->busy = 1; f = nf; /* fall through */ case Twalk: if(strcmp(rhdr.name, "bitblt") == 0) { f->qidpath = Qbitblt; thdr.qid.path = Qbitblt; thdr.qid.vers = 0; } else { err = "file does not exist"; if(nf) nf->busy = 0; } break; case Topen: thdr.qid.path = q; thdr.qid.vers = 0; break; case Tclunk: f->busy = 0; f->qidpath = 0; break; case Tstat: convD2M((q==Qroot)? &rootdir : &bitbltdir, thdr.stat); break; case Tread: if(q == Qroot) { if(rhdr.offset%DIRLEN || rhdr.count%DIRLEN) err = "i/o error"; else { thdr.data = data+MAXMSG; convD2M(&bitbltdir, thdr.data); if(rhdr.offset > 0 || rhdr.count < DIRLEN) thdr.count = 0; else thdr.count = DIRLEN; } } else err = bitread(); break; case Twrite: if(q == Qroot) err = "write to directory"; else err = bitwrite(); break; default: fprint(2, "%s: fcall unknown message type: %d\n", argv0, rhdr.type); exits("unknown message type"); } if(err) { thdr.type = Rerror; strncpy(thdr.ename, err, ERRLEN); } else thdr.type = rhdr.type+1; thdr.tag = rhdr.tag; thdr.fid = rhdr.fid; n = convS2M(&thdr, data); if(write(srvfd, data, n) < 0) { fprint(2, "%s: error writing server reply: %r\n", argv0); exits("mount write"); } } fprint(2, "%s: mount read error: %r\n", argv0); exits("mount read"); } /* * rhdr has a write message to the bitblt file. * It may hold any number of bitblt protocol requests, * but only the last, at most, should call for a reply. * Format the requests to the out file. * Set thdr.count to amount consumed. * Return 0 if ok, else an error message */ char * bitwrite(void) { long n, m; uchar *p; n = rhdr.count; p = (uchar *)rhdr.data; if(detail) Bprint(out, "*** Request length %d\n", rhdr.count); while(n > 0) { rtype = *p; m = req(p, n); if(m == 0) exits("bad request format"); if(write(bitfd, p, m) != m) { errstr(errbuf); Bprint(out, "error writing /dev/bitblt: %s\n", errbuf); return errbuf; } n -= m; p += m; } thdr.count = rhdr.count - n; Bflush(out); return 0; } /* * rhdr has a read message to the bitblt file. * Read from the real bitblt fd, * format the reply to the out file. * Set thdr.data to point at the reply, * and thdr.count to amount returned. * Return 0 if ok, else an error message */ char * bitread(void) { long n; if((n = read(bitfd, repdata, rhdr.count)) < 0) { errstr(errbuf); Bprint(out, "error reading /dev/bitblt: %s\n", errbuf); return errbuf; } if(detail) Bprint(out, "*** Reply length %d\n", n); rep((uchar *)repdata, n); thdr.data = repdata; thdr.count = n; Bflush(out); return 0; } /* Format one request, return number of bytes consumed */ int req(uchar *p, int m) { int len; int v, i; ulong ws, l; long miny, maxy, t; Bitmap *bm; switch(*p) { default: Bprint(out, "bad bitblt request 0x%x\n", *p); len = 0; break; case 'a': len = 18; rldepth = p[1]; rrect = bgrect(p+2); Bprint(out, "[a] bitmap allocate, ldepth=%d, rect=%R\n", rldepth, rrect); break; case 'b': len = 31; Bprint(out, "[b] bitblt, dst=%d, dst pt=%P, src=%d, src rect=%R, code=%A\n", BGSHORT(p+1), bgpt(p+3), BGSHORT(p+11), bgrect(p+13), BGSHORT(p+29)); break; case 'c': if(m == 1){ len = 1; Bprint(out, "[c] cursorswitch, to default cursor\n"); break; } len = 73; Bprint(out, "[c] cursorswitch, offset=%P\n", bgpt(p+1)); if(detail) { Bprint(out, "\t clr set\n"); for(i=0; i<32; i += 2) Bprint(out, "\t%.4x%.4x %.4x%.4x\n", p[9+i], p[10+i], p[41+i], p[42+i]); } break; case 'f': len = 3; Bprint(out, "[f] bitmap free, id=%d\n", BGSHORT(p+1)); break; case 'g': len = 3; Bprint(out, "[g] subfont free, id=%d\n", BGSHORT(p+1)); break; case 'h': len = 3; Bprint(out, "[h] font free, id=%d\n", BGSHORT(p+1)); break; case 'i': len = 1; Bprint(out, "[i] init\n"); break; case 'j': len = 9; Bprint(out, "[j] subfont cache check, qid=%lux,%lux\n", BGLONG(p+1), BGLONG(p+5)); break; case 'k': v = BGSHORT(p+1); len = 15+6*(v+1); Bprint(out, "[k] subfont allocate, n=%d, height=%d, ascent=%d, bitmap=%d, qid=%lux, %lux\n", v, p[3], p[4], BGSHORT(p+5), BGLONG(p+7), BGLONG(p+11)); printfontchars(p+15, v); break; case 'l': len = 22; Bprint(out, "[l] line segment, bitmap=%d, pt1=%P, pt2=%P, value=%d, code=%A\n", BGSHORT(p+1), bgpt(p+3), bgpt(p+11), p[19], BGSHORT(p+20)); break; case 'm': len = 3; rid = BGSHORT(p+1); Bprint(out, "[m] read colormap, bitmap id=%d\n", rid); break; case 'n': len = 7; Bprint(out, "[n] font allocate, height=%d, ascent=%d, ldepth=%d, ncache=%d\n", p[1], p[2], BGSHORT(p+3), BGSHORT(p+5)); break; case 'p': len = 14; Bprint(out, "[p] point, bitmap=%d, pt=%P, value=%d, code=%A\n", BGSHORT(p+1), bgpt(p+3), p[11], BGSHORT(p+12)); break; case 'q': len = 19; Bprint(out, "[q] set clip rectangle, bitmap=%d, rect=%R\n", BGSHORT(p+1), bgrect(p+3)); break; case 'r': len = 11; rid = BGSHORT(p+1); rminy = BGLONG(p+3); rmaxy = BGLONG(p+7); Bprint(out, "[r] bitmap read, bitmap=%d, miny=%d, maxy=%d\n", rid, rminy, rmaxy); break; case 's': v = BGSHORT(p+15); len = 17 + 2*v; Bprint(out, "[s] string, bitmap=%d, pt=%P, font=%d, code=%A, n=%d\n", BGSHORT(p+1), bgpt(p+3), BGSHORT(p+11), BGSHORT(p+13), v); if(detail) { Bprint(out, "\t"); for(i = 0; ildepth); /* pixels per byte */ /* set l to number of bytes of incoming data per scan line */ if(bm->r.min.x >= 0) l = (bm->r.max.x+ws-1)/ws - bm->r.min.x/ws; else{ /* make positive before divide */ t = (-bm->r.min.x)+ws-1; t = (t/ws)*ws; l = (t+bm->r.max.x+ws-1)/ws; } len = 11 + l*(maxy-miny); Bprint(out, "[w] bitmap write, bitmap=%d, miny=%d, maxy=%d\n", v, miny, maxy); dumpbmrows(p+11, l, bm->ldepth, Rect(bm->r.min.x, miny, bm->r.max.x, maxy)); break; case 'x': len = 9; Bprint(out, "[x] cursorset, pt=%P\n", bgpt(p+1)); break; case 'y': len = 9; Bprint(out, "[y] load font char, font=%d, cache index=%d, subfont=%d, subfont index=%d\n", BGSHORT(p+1), BGSHORT(p+3), BGSHORT(p+5), BGSHORT(p+7)); break; case 'z': v = BGSHORT(p+1); bm = bmlook(v); l = 1 << (1 << bm->ldepth); len = 3 + 12*l; Bprint(out, "[z] colormap write, bitmap=%d\n", v); printcmap(p+3, l); break; } if(m < len) { Bprint(out, "\tEbadblt: request length %d, should be %d\n", m, len); len = 0; } return len; } void rep(uchar *p, int n) { int len, v; Bitmap *bm; long t; ulong l, ws; len = -n; switch(rtype) { case 'i': len = 34; if(p[0] != 'I') { Bprint(out, "Expected reply 'I', got '%c'\n", p[0]); break; } bm = bmlook(0); bm->ldepth = p[1]; bm->r = bgrect(p+2); Bprint(out, "[I] ldepth=%d, rect=%R, clip rect=%R\n", p[1], bm->r, bgrect(p+18)); if(n >= 34 + 3*12) { v = atoi((char *)(p+34)); Bprint(out, "\tdefont, n=%d, height=%d, ascent=%d\n", v, atoi((char *)(p+34+12)), atoi((char *)(p+34+24))); printfontchars(p+34+3*12, v); len = 34+3*12+6*(v+1); } break; case 'a': len = 3; if(p[0] != 'A') { Bprint(out, "Expected reply 'A', got '%c'\n", p[0]); break; } v = BGSHORT(p+1); Bprint(out, "[A] bitmap=%d\n", v); bm = bmlook(v); bm->r = rrect; bm->ldepth = rldepth; break; case 'k': len = 3; if(p[0] != 'K') { Bprint(out, "Expected reply 'K', got '%c'\n", p[0]); break; } Bprint(out, "[K] subfont=%d\n", BGSHORT(p+1)); break; case 'j': if(p[0] != 'J') { Bprint(out, "Expected reply 'J', got '%c'\n", p[0]); break; } v = atoi((char *)(p+3)); Bprint(out, "[J] subfont=%d, n=%d, height=%d, ascent=%d\n", BGSHORT(p+1), v, atoi((char *)(p+3+12)), atoi((char *)(p+3+24))); printfontchars(p+3+3*12, v); len = 3+3*12+6*(v+1); break; case 'n': len = 3; if(p[0] != 'N') { Bprint(out, "Expected reply 'N', got '%c'\n", p[0]); break; } Bprint(out, "[N] font=%d\n", BGSHORT(p+1)); break; case 'm': bm = bmlook(rid); v = 1 << (1 << bm->ldepth); len = 12*v; printcmap(p, v); break; case 'r': bm = bmlook(rid); ws = 1<<(3-bm->ldepth); /* pixels per byte */ /* set l to number of bytes of incoming data per scan line */ if(bm->r.min.x >= 0) l = (bm->r.max.x+ws-1)/ws - bm->r.min.x/ws; else{ /* make positive before divide */ t = (-bm->r.min.x)+ws-1; t = (t/ws)*ws; l = (t+bm->r.max.x+ws-1)/ws; } dumpbmrows(p, l, bm->ldepth, Rect(bm->r.min.x, rminy, bm->r.max.x, rmaxy)); len = l*(rmaxy-rminy); break; } if(n != len) Bprint(out, "\tBad reply length %d, should be %d\n", n, len); } char *fcnames[] = { [Zero] "0", [DnorS] "~(D|S)", [DandnotS] "D&~S", [notS] "~S", [notDandS] "~D&S", [notD] "~D", [DxorS] "D^S", [DnandS] "~(D&S)", [DandS] "D&S", [DxnorS] "~(D^S)", [D] "D", [DornotS] "D|~S", [S] "S", [notDorS] "~D|S", [DorS] "D|S", [F] "F", }; int Aconv(void *oa, int f1, int f2, int f3, int c) { int fc; USED(c); fc = *(int *)oa; strconv(fcnames[fc & 0xF], f1, f2, f3); return sizeof(int); } void dumpbmrows(uchar *p, int bytes2row, int ldepth, Rectangle r) { Biobuf *f; int i, j; if(detail > 1) { for(i=0; icache) if(id == b->id) return b; b = malloc(sizeof(Bitmap)); memset(b, 0, sizeof(Bitmap)); b->id = id; b->cache = bhead; bhead = b; return b; } Fid * fidlook(int fid) { Fid *f, *ff; ff = 0; for(f = fidhead; f; f = f->next) if(f->fid == fid) return f; else if(!ff && !f->busy) ff = f; if(ff){ ff->fid = fid; return ff; } f = malloc(sizeof *f); f->qidpath = 0; f->fid = fid; f->next = fidhead; fidhead = f; return f; } void catcher(void *a, char *text) { USED(a); if(strcmp(text, "interrupted") != 0) if(srvpid) postnote(srvpid, text); noted(NDFLT); }