| // poor emulation of SVR5 truss command - traces system calls |
| |
| include("syscall"); |
| |
| _stoprunning = 0; |
| |
| defn stopped(pid) { |
| local l; |
| local pc; |
| pc = *PC; |
| if notes then { |
| if (notes[0]!="sys: breakpoint") then |
| { |
| print(pid,": ",trapreason(),"\t"); |
| print(fmt(pc,97),"\t",fmt(pc,105),"\n"); |
| print("Notes pending:\n"); |
| l = notes; |
| while l do |
| { |
| print("\t",head l,"\n"); |
| l = tail l; |
| } |
| _stoprunning = 1; |
| } |
| } |
| } |
| |
| defn _addressof(pattern) { |
| local s, l; |
| l = symbols; |
| pattern = "^\\$*"+pattern+"$"; |
| while l do |
| { |
| s = head l; |
| if regexp(pattern, s[0]) && ((s[1] == 'T') || (s[1] == 'L')) then |
| return s[2]; |
| l = tail l; |
| } |
| return 0; |
| } |
| |
| stopPC = {}; |
| readPC = {}; |
| fd2pathPC = {}; |
| errstrPC = {}; |
| awaitPC = {}; |
| _waitPC = {}; |
| _errstrPC = {}; |
| trusscalls = { |
| "sysr1", |
| "_errstr", |
| "bind", |
| "chdir", |
| "close", |
| "dup", |
| "alarm", |
| "exec", |
| "_exits", |
| "_fsession", |
| "fauth", |
| "_fstat", |
| "segbrk", |
| "_mount", |
| "open", |
| "_read", |
| "oseek", |
| "sleep", |
| "_stat", |
| "rfork", |
| "_write", |
| "pipe", |
| "create", |
| "fd2path", |
| "brk_", |
| "remove", |
| "_wstat", |
| "_fwstat", |
| "notify", |
| "noted", |
| "segattach", |
| "segdetach", |
| "segfree", |
| "segflush", |
| "rendezvous", |
| "unmount", |
| "_wait", |
| "seek", |
| "fversion", |
| "errstr", |
| "stat", |
| "fstat", |
| "wstat", |
| "fwstat", |
| "mount", |
| "await", |
| "pread", |
| "pwrite", |
| }; |
| |
| trussapecalls = { |
| "_SYSR1", |
| "__ERRSTR", |
| "_BIND", |
| "_CHDIR", |
| "_CLOSE", |
| "_DUP", |
| "_ALARM", |
| "_EXEC", |
| "_EXITS", |
| "__FSESSION", |
| "_FAUTH", |
| "__FSTAT", |
| "_SEGBRK", |
| "__MOUNT", |
| "_OPEN", |
| "__READ", |
| "_OSEEK", |
| "_SLEEP", |
| "__STAT", |
| "_RFORK", |
| "__WRITE", |
| "_PIPE", |
| "_CREATE", |
| "_FD2PATH", |
| "_BRK_", |
| "_REMOVE", |
| "__WSTAT", |
| "__FWSTAT", |
| "_NOTIFY", |
| "_NOTED", |
| "_SEGATTACH", |
| "_SEGDETACH", |
| "_SEGFREE", |
| "_SEGFLUSH", |
| "_RENDEZVOUS", |
| "_UNMOUNT", |
| "__WAIT", |
| "_SEEK", |
| "__NFVERSION", |
| "__NERRSTR", |
| "_STAT", |
| "__NFSTAT", |
| "__NWSTAT", |
| "__NFWSTAT", |
| "__NMOUNT", |
| "__NAWAIT", |
| "_PREAD", |
| "_PWRITE", |
| }; |
| |
| defn addressof(pattern) { |
| // translate to ape system calls if we have an ape binary |
| if _addressof("_EXITS") == 0 then |
| return _addressof(pattern); |
| return _addressof(trussapecalls[match(pattern, trusscalls)]); |
| } |
| |
| defn setuptruss() { |
| local lst, offset, name, addr; |
| |
| trussbpt = {}; |
| offset = trapoffset(); |
| lst = trusscalls; |
| while lst do |
| { |
| name = head lst; |
| lst = tail lst; |
| addr = addressof(name); |
| if addr then |
| { |
| bpset(addr+offset); |
| trussbpt = append trussbpt, (addr+offset); |
| // sometimes _exits is renamed $_exits |
| if(regexp("exits|exec", name)) then stopPC = append stopPC, (addr+offset); |
| if(regexp("read", name)) then readPC = append readPC, (addr+offset); |
| if(regexp("fd2path", name)) then fd2pathPC = append fd2pathPC, (addr+offset); |
| if(regexp("^\\$*await", name)) then awaitPC = append awaitPC, (addr+offset); |
| if(regexp("^\\$*errstr", name)) then errstrPC = append errstrPC, (addr+offset); |
| // compatibility hacks for old kernel |
| if(regexp("_wait", name)) then _waitPC = append _waitPC, (addr+offset); |
| if(regexp("_errstr", name)) then _errstrPC = append _errstrPC, (addr+offset); |
| } |
| } |
| } |
| |
| defn trussflush() { |
| stop(pid); // already stopped, but flushes output |
| } |
| |
| defn new() { |
| bplist = {}; |
| newproc(progargs); |
| bpset(follow(main)[0]); |
| cont(); |
| bpdel(*PC); |
| // clear the hang bit, which is left set by newproc, so programs we fork/exec don't hang |
| printto("/proc/"+itoa(pid)+"/ctl", "nohang"); |
| } |
| |
| defn truss() { |
| local pc, lst, offset, prevpc, pcspret, ret; |
| |
| offset = trapoffset(); |
| |
| stop(pid); |
| _stoprunning = 0; |
| setuptruss(); |
| pcspret = UPCSPRET(); |
| |
| while !_stoprunning do { |
| cont(); |
| if notes[0]!="sys: breakpoint" then { |
| cleantruss(); |
| return {}; |
| } |
| pc = *PC; |
| if match(*PC, stopPC)>=0 then { |
| print(pid,": ",trapreason(),"\t"); |
| print(fmt(pc,'a'),"\t",fmt(pc,'i'),"\n"); |
| cleantruss(); |
| return {}; |
| } |
| if match(*PC, trussbpt)>=0 then { |
| usyscall(); |
| trussflush(); |
| prevpc = *PC; |
| step(); |
| ret = eval pcspret[2]; |
| print("\treturn value: ", ret\D, "\n"); |
| if (ret>=0) && (match(prevpc, readPC)>=0) then { |
| print("\tdata: "); |
| printtextordata(*((eval pcspret[1])+4), ret); |
| print("\n"); |
| } |
| if (ret>=0) && (match(prevpc, fd2pathPC)>=0) then { |
| print("\tdata: \"", *(*((eval pcspret[1])+4)\s), "\"\n"); |
| } |
| if (ret>=0) && (match(prevpc, errstrPC)>=0) then { |
| print("\tdata: \"", *(*(eval pcspret[1])\s), "\"\n"); |
| } |
| if (ret>=0) && (match(prevpc, awaitPC)>=0) then { |
| print("\tdata: "); |
| printtextordata(*(eval pcspret[1]), ret); |
| print("\n"); |
| } |
| // compatibility hacks for old kernel: |
| if (ret>=0) && (match(prevpc, _waitPC)>=0) then { |
| print("\tdata: "); |
| printtextordata(*(eval pcspret[1]), 12+3*12+64); |
| print("\n"); |
| } |
| if (ret>=0) && (match(prevpc, _errstrPC)>=0) then { |
| print("\tdata: "); |
| printtextordata(*(eval pcspret[1]), 64); |
| print("\n"); |
| } |
| } |
| trussflush(); |
| } |
| } |
| |
| defn cleantruss() { |
| local lst, offset, addr; |
| |
| stop(pid); |
| offset = trapoffset(); |
| lst = trussbpt; |
| while lst do |
| { |
| addr = head lst; |
| lst = tail lst; |
| bpdel(addr); |
| } |
| trussbpt = {}; |
| **PC = @*PC; // repair current instruction |
| } |
| |
| defn untruss() { |
| cleantruss(); |
| start(pid); |
| } |
| |
| print(acidfile); |