1// poor emulation of SVR5 truss command - traces system calls 2 3include("/sys/lib/acid/syscall"); 4 5_stoprunning = 0; 6 7defn stopped(pid) { 8 local l; 9 local pc; 10 pc = *PC; 11 if notes then { 12 if (notes[0]!="sys: breakpoint") then 13 { 14 print(pid,": ",trapreason(),"\t"); 15 print(fmt(pc,97),"\t",fmt(pc,105),"\n"); 16 print("Notes pending:\n"); 17 l = notes; 18 while l do 19 { 20 print("\t",head l,"\n"); 21 l = tail l; 22 } 23 _stoprunning = 1; 24 } 25 } 26} 27 28defn _addressof(pattern) { 29 local s, l; 30 l = symbols; 31 pattern = "^\\$*"+pattern+"$"; 32 while l do 33 { 34 s = head l; 35 if regexp(pattern, s[0]) && ((s[1] == 'T') || (s[1] == 'L')) then 36 return s[2]; 37 l = tail l; 38 } 39 return 0; 40} 41 42stopPC = {}; 43readPC = {}; 44fd2pathPC = {}; 45errstrPC = {}; 46awaitPC = {}; 47_waitPC = {}; 48_errstrPC = {}; 49trusscalls = { 50 "sysr1", 51 "_errstr", 52 "bind", 53 "chdir", 54 "close", 55 "dup", 56 "alarm", 57 "exec", 58 "_exits", 59 "_fsession", 60 "fauth", 61 "_fstat", 62 "segbrk", 63 "_mount", 64 "open", 65 "_read", 66 "oseek", 67 "sleep", 68 "_stat", 69 "rfork", 70 "_write", 71 "pipe", 72 "create", 73 "fd2path", 74 "brk_", 75 "remove", 76 "_wstat", 77 "_fwstat", 78 "notify", 79 "noted", 80 "segattach", 81 "segdetach", 82 "segfree", 83 "segflush", 84 "rendezvous", 85 "unmount", 86 "_wait", 87 "seek", 88 "fversion", 89 "errstr", 90 "stat", 91 "fstat", 92 "wstat", 93 "fwstat", 94 "mount", 95 "await", 96 "pread", 97 "pwrite", 98 }; 99 100trussapecalls = { 101 "_SYSR1", 102 "__ERRSTR", 103 "_BIND", 104 "_CHDIR", 105 "_CLOSE", 106 "_DUP", 107 "_ALARM", 108 "_EXEC", 109 "_EXITS", 110 "__FSESSION", 111 "_FAUTH", 112 "__FSTAT", 113 "_SEGBRK", 114 "__MOUNT", 115 "_OPEN", 116 "__READ", 117 "_OSEEK", 118 "_SLEEP", 119 "__STAT", 120 "_RFORK", 121 "__WRITE", 122 "_PIPE", 123 "_CREATE", 124 "_FD2PATH", 125 "_BRK_", 126 "_REMOVE", 127 "__WSTAT", 128 "__FWSTAT", 129 "_NOTIFY", 130 "_NOTED", 131 "_SEGATTACH", 132 "_SEGDETACH", 133 "_SEGFREE", 134 "_SEGFLUSH", 135 "_RENDEZVOUS", 136 "_UNMOUNT", 137 "__WAIT", 138 "_SEEK", 139 "__NFVERSION", 140 "__NERRSTR", 141 "_STAT", 142 "__NFSTAT", 143 "__NWSTAT", 144 "__NFWSTAT", 145 "__NMOUNT", 146 "__NAWAIT", 147 "_PREAD", 148 "_PWRITE", 149 }; 150 151defn addressof(pattern) { 152 // translate to ape system calls if we have an ape binary 153 if _addressof("_EXITS") == 0 then 154 return _addressof(pattern); 155 return _addressof(trussapecalls[match(pattern, trusscalls)]); 156} 157 158defn setuptruss() { 159 local lst, offset, name, addr; 160 161 trussbpt = {}; 162 offset = trapoffset(); 163 lst = trusscalls; 164 while lst do 165 { 166 name = head lst; 167 lst = tail lst; 168 addr = addressof(name); 169 if addr then 170 { 171 bpset(addr+offset); 172 trussbpt = append trussbpt, (addr+offset); 173 // sometimes _exits is renamed $_exits 174 if(regexp("exits|exec", name)) then stopPC = append stopPC, (addr+offset); 175 if(regexp("read", name)) then readPC = append readPC, (addr+offset); 176 if(regexp("fd2path", name)) then fd2pathPC = append fd2pathPC, (addr+offset); 177 if(regexp("^\\$*await", name)) then awaitPC = append awaitPC, (addr+offset); 178 if(regexp("^\\$*errstr", name)) then errstrPC = append errstrPC, (addr+offset); 179 // compatibility hacks for old kernel 180 if(regexp("_wait", name)) then _waitPC = append _waitPC, (addr+offset); 181 if(regexp("_errstr", name)) then _errstrPC = append _errstrPC, (addr+offset); 182 } 183 } 184} 185 186defn trussflush() { 187 stop(pid); // already stopped, but flushes output 188} 189 190defn new() { 191 bplist = {}; 192 newproc(progargs); 193 bpset(follow(main)[0]); 194 cont(); 195 bpdel(*PC); 196 // clear the hang bit, which is left set by newproc, so programs we fork/exec don't hang 197 printto("/proc/"+itoa(pid)+"/ctl", "nohang"); 198} 199 200defn truss() { 201 local pc, lst, offset, prevpc, pcspret, ret; 202 203 offset = trapoffset(); 204 205 stop(pid); 206 _stoprunning = 0; 207 setuptruss(); 208 pcspret = UPCSPRET(); 209 210 while !_stoprunning do { 211 cont(); 212 if notes[0]!="sys: breakpoint" then { 213 cleantruss(); 214 return {}; 215 } 216 pc = *PC; 217 if match(*PC, stopPC)>=0 then { 218 print(pid,": ",trapreason(),"\t"); 219 print(fmt(pc,'a'),"\t",fmt(pc,'i'),"\n"); 220 cleantruss(); 221 return {}; 222 } 223 if match(*PC, trussbpt)>=0 then { 224 usyscall(); 225 trussflush(); 226 prevpc = *PC; 227 step(); 228 ret = eval pcspret[2]; 229 print("\treturn value: ", ret\D, "\n"); 230 if (ret>=0) && (match(prevpc, readPC)>=0) then { 231 print("\tdata: "); 232 printtextordata(*((eval pcspret[1])+4), ret); 233 print("\n"); 234 } 235 if (ret>=0) && (match(prevpc, fd2pathPC)>=0) then { 236 print("\tdata: \"", *(*((eval pcspret[1])+4)\s), "\"\n"); 237 } 238 if (ret>=0) && (match(prevpc, errstrPC)>=0) then { 239 print("\tdata: \"", *(*(eval pcspret[1])\s), "\"\n"); 240 } 241 if (ret>=0) && (match(prevpc, awaitPC)>=0) then { 242 print("\tdata: "); 243 printtextordata(*(eval pcspret[1]), ret); 244 print("\n"); 245 } 246 // compatibility hacks for old kernel: 247 if (ret>=0) && (match(prevpc, _waitPC)>=0) then { 248 print("\tdata: "); 249 printtextordata(*(eval pcspret[1]), 12+3*12+64); 250 print("\n"); 251 } 252 if (ret>=0) && (match(prevpc, _errstrPC)>=0) then { 253 print("\tdata: "); 254 printtextordata(*(eval pcspret[1]), 64); 255 print("\n"); 256 } 257 } 258 trussflush(); 259 } 260} 261 262defn cleantruss() { 263 local lst, offset, addr; 264 265 stop(pid); 266 offset = trapoffset(); 267 lst = trussbpt; 268 while lst do 269 { 270 addr = head lst; 271 lst = tail lst; 272 bpdel(addr); 273 } 274 trussbpt = {}; 275 **PC = @*PC; // repair current instruction 276} 277 278defn untruss() { 279 cleantruss(); 280 start(pid); 281} 282 283print("/sys/lib/acid/truss"); 284