xref: /plan9-contrib/sys/lib/acid/truss (revision f8bc6aaf8056e137bcdfb6117a990ac3eff62cc9)
19a747e4fSDavid du Colombier// poor emulation of SVR5 truss command - traces system calls
29a747e4fSDavid du Colombier
39a747e4fSDavid du Colombierinclude("/sys/lib/acid/syscall");
49a747e4fSDavid du Colombier
59a747e4fSDavid du Colombier_stoprunning = 0;
69a747e4fSDavid du Colombier
79a747e4fSDavid du Colombierdefn stopped(pid) {
89a747e4fSDavid du Colombier	local l;
99a747e4fSDavid du Colombier	local pc;
109a747e4fSDavid du Colombier	pc = *PC;
119a747e4fSDavid du Colombier	if notes then {
129a747e4fSDavid du Colombier		if (notes[0]!="sys: breakpoint") then
139a747e4fSDavid du Colombier		{
149a747e4fSDavid du Colombier			print(pid,": ",trapreason(),"\t");
159a747e4fSDavid du Colombier			print(fmt(pc,97),"\t",fmt(pc,105),"\n");
169a747e4fSDavid du Colombier			print("Notes pending:\n");
179a747e4fSDavid du Colombier			l = notes;
189a747e4fSDavid du Colombier			while l do
199a747e4fSDavid du Colombier			{
209a747e4fSDavid du Colombier				print("\t",head l,"\n");
219a747e4fSDavid du Colombier				l = tail l;
229a747e4fSDavid du Colombier			}
239a747e4fSDavid du Colombier			_stoprunning = 1;
249a747e4fSDavid du Colombier		}
259a747e4fSDavid du Colombier	}
269a747e4fSDavid du Colombier}
279a747e4fSDavid du Colombier
289a747e4fSDavid du Colombierdefn _addressof(pattern) {
299a747e4fSDavid du Colombier	local s, l;
309a747e4fSDavid du Colombier	l = symbols;
319a747e4fSDavid du Colombier	pattern = "^\\$*"+pattern+"$";
329a747e4fSDavid du Colombier	while l do
339a747e4fSDavid du Colombier	{
349a747e4fSDavid du Colombier		s = head l;
359a747e4fSDavid du Colombier		if regexp(pattern, s[0]) && ((s[1] == 'T') || (s[1] == 'L')) then
369a747e4fSDavid du Colombier			return s[2];
379a747e4fSDavid du Colombier		l = tail l;
389a747e4fSDavid du Colombier	}
399a747e4fSDavid du Colombier	return 0;
409a747e4fSDavid du Colombier}
419a747e4fSDavid du Colombier
429a747e4fSDavid du ColombierstopPC = {};
439a747e4fSDavid du ColombierreadPC = {};
449a747e4fSDavid du Colombierfd2pathPC = {};
459a747e4fSDavid du ColombiererrstrPC = {};
469a747e4fSDavid du ColombierawaitPC = {};
479a747e4fSDavid du Colombier_waitPC = {};
489a747e4fSDavid du Colombier_errstrPC = {};
499a747e4fSDavid du Colombiertrusscalls = {
509a747e4fSDavid du Colombier		"sysr1",
519a747e4fSDavid du Colombier		"_errstr",
529a747e4fSDavid du Colombier		"bind",
539a747e4fSDavid du Colombier		"chdir",
549a747e4fSDavid du Colombier		"close",
559a747e4fSDavid du Colombier		"dup",
569a747e4fSDavid du Colombier		"alarm",
579a747e4fSDavid du Colombier		"exec",
589a747e4fSDavid du Colombier		"_exits",
599a747e4fSDavid du Colombier		"_fsession",
609a747e4fSDavid du Colombier		"fauth",
619a747e4fSDavid du Colombier		"_fstat",
629a747e4fSDavid du Colombier		"segbrk",
639a747e4fSDavid du Colombier		"_mount",
649a747e4fSDavid du Colombier		"open",
659a747e4fSDavid du Colombier		"_read",
669a747e4fSDavid du Colombier		"oseek",
679a747e4fSDavid du Colombier		"sleep",
689a747e4fSDavid du Colombier		"_stat",
699a747e4fSDavid du Colombier		"rfork",
709a747e4fSDavid du Colombier		"_write",
719a747e4fSDavid du Colombier		"pipe",
729a747e4fSDavid du Colombier		"create",
739a747e4fSDavid du Colombier		"fd2path",
749a747e4fSDavid du Colombier		"brk_",
759a747e4fSDavid du Colombier		"remove",
769a747e4fSDavid du Colombier		"_wstat",
779a747e4fSDavid du Colombier		"_fwstat",
789a747e4fSDavid du Colombier		"notify",
799a747e4fSDavid du Colombier		"noted",
809a747e4fSDavid du Colombier		"segattach",
819a747e4fSDavid du Colombier		"segdetach",
829a747e4fSDavid du Colombier		"segfree",
839a747e4fSDavid du Colombier		"segflush",
849a747e4fSDavid du Colombier		"rendezvous",
859a747e4fSDavid du Colombier		"unmount",
869a747e4fSDavid du Colombier		"_wait",
879a747e4fSDavid du Colombier		"seek",
88d9306527SDavid du Colombier		"fversion",
89d9306527SDavid du Colombier		"errstr",
909a747e4fSDavid du Colombier		"stat",
91d9306527SDavid du Colombier		"fstat",
92d9306527SDavid du Colombier		"wstat",
93d9306527SDavid du Colombier		"fwstat",
94d9306527SDavid du Colombier		"mount",
95d9306527SDavid du Colombier		"await",
969a747e4fSDavid du Colombier		"pread",
979a747e4fSDavid du Colombier		"pwrite",
989a747e4fSDavid du Colombier	};
999a747e4fSDavid du Colombier
1009a747e4fSDavid du Colombiertrussapecalls = {
1019a747e4fSDavid du Colombier		"_SYSR1",
1029a747e4fSDavid du Colombier		"__ERRSTR",
1039a747e4fSDavid du Colombier		"_BIND",
1049a747e4fSDavid du Colombier		"_CHDIR",
1059a747e4fSDavid du Colombier		"_CLOSE",
1069a747e4fSDavid du Colombier		"_DUP",
1079a747e4fSDavid du Colombier		"_ALARM",
1089a747e4fSDavid du Colombier		"_EXEC",
1099a747e4fSDavid du Colombier		"_EXITS",
1109a747e4fSDavid du Colombier		"__FSESSION",
1119a747e4fSDavid du Colombier		"_FAUTH",
1129a747e4fSDavid du Colombier		"__FSTAT",
1139a747e4fSDavid du Colombier		"_SEGBRK",
1149a747e4fSDavid du Colombier		"__MOUNT",
1159a747e4fSDavid du Colombier		"_OPEN",
1169a747e4fSDavid du Colombier		"__READ",
1179a747e4fSDavid du Colombier		"_OSEEK",
1189a747e4fSDavid du Colombier		"_SLEEP",
1199a747e4fSDavid du Colombier		"__STAT",
1209a747e4fSDavid du Colombier		"_RFORK",
1219a747e4fSDavid du Colombier		"__WRITE",
1229a747e4fSDavid du Colombier		"_PIPE",
1239a747e4fSDavid du Colombier		"_CREATE",
1249a747e4fSDavid du Colombier		"_FD2PATH",
1259a747e4fSDavid du Colombier		"_BRK_",
1269a747e4fSDavid du Colombier		"_REMOVE",
1279a747e4fSDavid du Colombier		"__WSTAT",
1289a747e4fSDavid du Colombier		"__FWSTAT",
1299a747e4fSDavid du Colombier		"_NOTIFY",
1309a747e4fSDavid du Colombier		"_NOTED",
1319a747e4fSDavid du Colombier		"_SEGATTACH",
1329a747e4fSDavid du Colombier		"_SEGDETACH",
1339a747e4fSDavid du Colombier		"_SEGFREE",
1349a747e4fSDavid du Colombier		"_SEGFLUSH",
1359a747e4fSDavid du Colombier		"_RENDEZVOUS",
1369a747e4fSDavid du Colombier		"_UNMOUNT",
1379a747e4fSDavid du Colombier		"__WAIT",
1389a747e4fSDavid du Colombier		"_SEEK",
1399a747e4fSDavid du Colombier		"__NFVERSION",
1409a747e4fSDavid du Colombier		"__NERRSTR",
1419a747e4fSDavid du Colombier		"_STAT",
1429a747e4fSDavid du Colombier		"__NFSTAT",
1439a747e4fSDavid du Colombier		"__NWSTAT",
1449a747e4fSDavid du Colombier		"__NFWSTAT",
1459a747e4fSDavid du Colombier		"__NMOUNT",
1469a747e4fSDavid du Colombier		"__NAWAIT",
1479a747e4fSDavid du Colombier		"_PREAD",
1489a747e4fSDavid du Colombier		"_PWRITE",
1499a747e4fSDavid du Colombier	};
1509a747e4fSDavid du Colombier
1519a747e4fSDavid du Colombierdefn addressof(pattern) {
1529a747e4fSDavid du Colombier	// translate to ape system calls if we have an ape binary
1539a747e4fSDavid du Colombier	if _addressof("_EXITS") == 0 then
1549a747e4fSDavid du Colombier		return _addressof(pattern);
1559a747e4fSDavid du Colombier	return _addressof(trussapecalls[match(pattern, trusscalls)]);
1569a747e4fSDavid du Colombier}
1579a747e4fSDavid du Colombier
1589a747e4fSDavid du Colombierdefn setuptruss() {
1599a747e4fSDavid du Colombier	local lst, offset, name, addr;
1609a747e4fSDavid du Colombier
1619a747e4fSDavid du Colombier	trussbpt = {};
1629a747e4fSDavid du Colombier	offset = trapoffset();
1639a747e4fSDavid du Colombier	lst = trusscalls;
1649a747e4fSDavid du Colombier	while lst do
1659a747e4fSDavid du Colombier	{
1669a747e4fSDavid du Colombier		name = head lst;
167*f8bc6aafSDavid du Colombier		if objtype == "mips64" && name == "seek" then
168*f8bc6aafSDavid du Colombier			name = "_seek";
1699a747e4fSDavid du Colombier		lst = tail lst;
1709a747e4fSDavid du Colombier		addr = addressof(name);
1719a747e4fSDavid du Colombier		if addr then
1729a747e4fSDavid du Colombier		{
1739a747e4fSDavid du Colombier			bpset(addr+offset);
1749a747e4fSDavid du Colombier			trussbpt = append trussbpt, (addr+offset);
1759a747e4fSDavid du Colombier			// sometimes _exits is renamed $_exits
1769a747e4fSDavid du Colombier			if(regexp("exits|exec", name)) then stopPC = append stopPC, (addr+offset);
1779a747e4fSDavid du Colombier			if(regexp("read", name)) then readPC = append readPC, (addr+offset);
1789a747e4fSDavid du Colombier			if(regexp("fd2path", name)) then fd2pathPC = append fd2pathPC, (addr+offset);
1799a747e4fSDavid du Colombier			if(regexp("^\\$*await", name)) then awaitPC = append awaitPC, (addr+offset);
1809a747e4fSDavid du Colombier			if(regexp("^\\$*errstr", name)) then errstrPC = append errstrPC, (addr+offset);
1819a747e4fSDavid du Colombier			// compatibility hacks for old kernel
1829a747e4fSDavid du Colombier			if(regexp("_wait", name)) then _waitPC = append _waitPC, (addr+offset);
1839a747e4fSDavid du Colombier			if(regexp("_errstr", name)) then _errstrPC = append _errstrPC, (addr+offset);
1849a747e4fSDavid du Colombier		}
1859a747e4fSDavid du Colombier	}
1869a747e4fSDavid du Colombier}
1879a747e4fSDavid du Colombier
1889a747e4fSDavid du Colombierdefn trussflush() {
1899a747e4fSDavid du Colombier	stop(pid);		// already stopped, but flushes output
1909a747e4fSDavid du Colombier}
1919a747e4fSDavid du Colombier
1929a747e4fSDavid du Colombierdefn new() {
1939a747e4fSDavid du Colombier	bplist = {};
1949a747e4fSDavid du Colombier	newproc(progargs);
1959a747e4fSDavid du Colombier	bpset(follow(main)[0]);
1969a747e4fSDavid du Colombier	cont();
1979a747e4fSDavid du Colombier	bpdel(*PC);
1989a747e4fSDavid du Colombier	// clear the hang bit, which is left set by newproc, so programs we fork/exec don't hang
1999a747e4fSDavid du Colombier	printto("/proc/"+itoa(pid)+"/ctl", "nohang");
2009a747e4fSDavid du Colombier}
2019a747e4fSDavid du Colombier
2029a747e4fSDavid du Colombierdefn truss() {
203*f8bc6aafSDavid du Colombier	local pc, lst, offset, prevpc, pcspret, ret, dataoffset;
2049a747e4fSDavid du Colombier
2059a747e4fSDavid du Colombier	offset = trapoffset();
2069a747e4fSDavid du Colombier
2079a747e4fSDavid du Colombier	stop(pid);
2089a747e4fSDavid du Colombier	_stoprunning = 0;
2099a747e4fSDavid du Colombier	setuptruss();
2109a747e4fSDavid du Colombier	pcspret = UPCSPRET();
2119a747e4fSDavid du Colombier
212*f8bc6aafSDavid du Colombier	if objtype == "mips64" then
213*f8bc6aafSDavid du Colombier		dataoffset = 8;
214*f8bc6aafSDavid du Colombier	else
215*f8bc6aafSDavid du Colombier		dataoffset = 4;
216*f8bc6aafSDavid du Colombier
2179a747e4fSDavid du Colombier	while !_stoprunning do {
2189a747e4fSDavid du Colombier		cont();
2199a747e4fSDavid du Colombier		if notes[0]!="sys: breakpoint" then {
2209a747e4fSDavid du Colombier			cleantruss();
2219a747e4fSDavid du Colombier			return {};
2229a747e4fSDavid du Colombier		}
2239a747e4fSDavid du Colombier		pc = *PC;
2249a747e4fSDavid du Colombier		if match(*PC, stopPC)>=0 then {
2259a747e4fSDavid du Colombier			print(pid,": ",trapreason(),"\t");
2269a747e4fSDavid du Colombier			print(fmt(pc,'a'),"\t",fmt(pc,'i'),"\n");
2279a747e4fSDavid du Colombier			cleantruss();
2289a747e4fSDavid du Colombier			return {};
2299a747e4fSDavid du Colombier		}
2309a747e4fSDavid du Colombier		if match(*PC, trussbpt)>=0 then {
2319a747e4fSDavid du Colombier			usyscall();
2329a747e4fSDavid du Colombier			trussflush();
2339a747e4fSDavid du Colombier			prevpc = *PC;
2349a747e4fSDavid du Colombier			step();
2359a747e4fSDavid du Colombier			ret = eval pcspret[2];
2369a747e4fSDavid du Colombier			print("\treturn value: ", ret\D, "\n");
2379a747e4fSDavid du Colombier			if (ret>=0) && (match(prevpc, readPC)>=0) then {
2389a747e4fSDavid du Colombier				print("\tdata: ");
239*f8bc6aafSDavid du Colombier				printtextordata(*((eval pcspret[1])+dataoffset), ret);
2409a747e4fSDavid du Colombier				print("\n");
2419a747e4fSDavid du Colombier			}
2429a747e4fSDavid du Colombier			if (ret>=0) && (match(prevpc, fd2pathPC)>=0) then {
243*f8bc6aafSDavid du Colombier				print("\tdata: \"", *(*((eval pcspret[1])+dataoffset)\s), "\"\n");
2449a747e4fSDavid du Colombier			}
2459a747e4fSDavid du Colombier			if (ret>=0) && (match(prevpc, errstrPC)>=0) then {
2469a747e4fSDavid du Colombier				print("\tdata: \"", *(*(eval pcspret[1])\s), "\"\n");
2479a747e4fSDavid du Colombier			}
2489a747e4fSDavid du Colombier			if (ret>=0) && (match(prevpc, awaitPC)>=0) then {
2499a747e4fSDavid du Colombier				print("\tdata: ");
2509a747e4fSDavid du Colombier				printtextordata(*(eval pcspret[1]), ret);
2519a747e4fSDavid du Colombier				print("\n");
2529a747e4fSDavid du Colombier			}
2539a747e4fSDavid du Colombier			// compatibility hacks for old kernel:
2549a747e4fSDavid du Colombier			if (ret>=0) && (match(prevpc, _waitPC)>=0) then {
2559a747e4fSDavid du Colombier				print("\tdata: ");
2569a747e4fSDavid du Colombier				printtextordata(*(eval pcspret[1]), 12+3*12+64);
2579a747e4fSDavid du Colombier				print("\n");
2589a747e4fSDavid du Colombier			}
2599a747e4fSDavid du Colombier			if (ret>=0) && (match(prevpc, _errstrPC)>=0) then {
2609a747e4fSDavid du Colombier				print("\tdata: ");
2619a747e4fSDavid du Colombier				printtextordata(*(eval pcspret[1]), 64);
2629a747e4fSDavid du Colombier				print("\n");
2639a747e4fSDavid du Colombier			}
2649a747e4fSDavid du Colombier		}
2659a747e4fSDavid du Colombier		trussflush();
2669a747e4fSDavid du Colombier	}
2679a747e4fSDavid du Colombier}
2689a747e4fSDavid du Colombier
2699a747e4fSDavid du Colombierdefn cleantruss() {
2709a747e4fSDavid du Colombier	local lst, offset, addr;
2719a747e4fSDavid du Colombier
2729a747e4fSDavid du Colombier	stop(pid);
2739a747e4fSDavid du Colombier	offset = trapoffset();
2749a747e4fSDavid du Colombier	lst = trussbpt;
2759a747e4fSDavid du Colombier	while lst do
2769a747e4fSDavid du Colombier	{
2779a747e4fSDavid du Colombier		addr = head lst;
2789a747e4fSDavid du Colombier		lst = tail lst;
2799a747e4fSDavid du Colombier		bpdel(addr);
2809a747e4fSDavid du Colombier	}
2819a747e4fSDavid du Colombier	trussbpt = {};
2829a747e4fSDavid du Colombier	**PC = @*PC;	// repair current instruction
2839a747e4fSDavid du Colombier}
2849a747e4fSDavid du Colombier
2859a747e4fSDavid du Colombierdefn untruss() {
2869a747e4fSDavid du Colombier	cleantruss();
2879a747e4fSDavid du Colombier	start(pid);
2889a747e4fSDavid du Colombier}
2899a747e4fSDavid du Colombier
2909a747e4fSDavid du Colombierprint("/sys/lib/acid/truss");
291