xref: /plan9-contrib/sys/lib/acid/truss (revision f8bc6aaf8056e137bcdfb6117a990ac3eff62cc9)
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