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 if objtype == "mips64" && name == "seek" then 168 name = "_seek"; 169 lst = tail lst; 170 addr = addressof(name); 171 if addr then 172 { 173 bpset(addr+offset); 174 trussbpt = append trussbpt, (addr+offset); 175 // sometimes _exits is renamed $_exits 176 if(regexp("exits|exec", name)) then stopPC = append stopPC, (addr+offset); 177 if(regexp("read", name)) then readPC = append readPC, (addr+offset); 178 if(regexp("fd2path", name)) then fd2pathPC = append fd2pathPC, (addr+offset); 179 if(regexp("^\\$*await", name)) then awaitPC = append awaitPC, (addr+offset); 180 if(regexp("^\\$*errstr", name)) then errstrPC = append errstrPC, (addr+offset); 181 // compatibility hacks for old kernel 182 if(regexp("_wait", name)) then _waitPC = append _waitPC, (addr+offset); 183 if(regexp("_errstr", name)) then _errstrPC = append _errstrPC, (addr+offset); 184 } 185 } 186} 187 188defn trussflush() { 189 stop(pid); // already stopped, but flushes output 190} 191 192defn new() { 193 bplist = {}; 194 newproc(progargs); 195 bpset(follow(main)[0]); 196 cont(); 197 bpdel(*PC); 198 // clear the hang bit, which is left set by newproc, so programs we fork/exec don't hang 199 printto("/proc/"+itoa(pid)+"/ctl", "nohang"); 200} 201 202defn truss() { 203 local pc, lst, offset, prevpc, pcspret, ret, dataoffset; 204 205 offset = trapoffset(); 206 207 stop(pid); 208 _stoprunning = 0; 209 setuptruss(); 210 pcspret = UPCSPRET(); 211 212 if objtype == "mips64" then 213 dataoffset = 8; 214 else 215 dataoffset = 4; 216 217 while !_stoprunning do { 218 cont(); 219 if notes[0]!="sys: breakpoint" then { 220 cleantruss(); 221 return {}; 222 } 223 pc = *PC; 224 if match(*PC, stopPC)>=0 then { 225 print(pid,": ",trapreason(),"\t"); 226 print(fmt(pc,'a'),"\t",fmt(pc,'i'),"\n"); 227 cleantruss(); 228 return {}; 229 } 230 if match(*PC, trussbpt)>=0 then { 231 usyscall(); 232 trussflush(); 233 prevpc = *PC; 234 step(); 235 ret = eval pcspret[2]; 236 print("\treturn value: ", ret\D, "\n"); 237 if (ret>=0) && (match(prevpc, readPC)>=0) then { 238 print("\tdata: "); 239 printtextordata(*((eval pcspret[1])+dataoffset), ret); 240 print("\n"); 241 } 242 if (ret>=0) && (match(prevpc, fd2pathPC)>=0) then { 243 print("\tdata: \"", *(*((eval pcspret[1])+dataoffset)\s), "\"\n"); 244 } 245 if (ret>=0) && (match(prevpc, errstrPC)>=0) then { 246 print("\tdata: \"", *(*(eval pcspret[1])\s), "\"\n"); 247 } 248 if (ret>=0) && (match(prevpc, awaitPC)>=0) then { 249 print("\tdata: "); 250 printtextordata(*(eval pcspret[1]), ret); 251 print("\n"); 252 } 253 // compatibility hacks for old kernel: 254 if (ret>=0) && (match(prevpc, _waitPC)>=0) then { 255 print("\tdata: "); 256 printtextordata(*(eval pcspret[1]), 12+3*12+64); 257 print("\n"); 258 } 259 if (ret>=0) && (match(prevpc, _errstrPC)>=0) then { 260 print("\tdata: "); 261 printtextordata(*(eval pcspret[1]), 64); 262 print("\n"); 263 } 264 } 265 trussflush(); 266 } 267} 268 269defn cleantruss() { 270 local lst, offset, addr; 271 272 stop(pid); 273 offset = trapoffset(); 274 lst = trussbpt; 275 while lst do 276 { 277 addr = head lst; 278 lst = tail lst; 279 bpdel(addr); 280 } 281 trussbpt = {}; 282 **PC = @*PC; // repair current instruction 283} 284 285defn untruss() { 286 cleantruss(); 287 start(pid); 288} 289 290print("/sys/lib/acid/truss"); 291