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