]> git.lizzy.rs Git - plan9front.git/blob - sys/lib/acid/truss
Import sources from 2011-03-30 iso image - lib
[plan9front.git] / sys / lib / acid / truss
1 // poor emulation of SVR5 truss command - traces system calls
2
3 include("/sys/lib/acid/syscall");
4
5 _stoprunning = 0;
6
7 defn 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
28 defn _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
42 stopPC = {};
43 readPC = {};
44 fd2pathPC = {};
45 errstrPC = {};
46 awaitPC = {};
47 _waitPC = {};
48 _errstrPC = {};
49 trusscalls = {
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
100 trussapecalls = {
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
151 defn 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
158 defn 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
186 defn trussflush() {
187         stop(pid);              // already stopped, but flushes output
188 }
189
190 defn 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
200 defn 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
262 defn 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
278 defn untruss() {
279         cleantruss();
280         start(pid);
281 }
282
283 print("/sys/lib/acid/truss");