| // poor emulation of SVR5 truss command - traces system calls | 
 |  | 
 | include("syscall"); | 
 |  | 
 | _stoprunning = 0; | 
 |  | 
 | defn stopped(pid) { | 
 | 	local l; | 
 | 	local pc; | 
 | 	pc = *PC; | 
 | 	if notes then { | 
 | 		if (notes[0]!="sys: breakpoint") then | 
 | 		{ | 
 | 			print(pid,": ",trapreason(),"\t"); | 
 | 			print(fmt(pc,97),"\t",fmt(pc,105),"\n"); | 
 | 			print("Notes pending:\n"); | 
 | 			l = notes; | 
 | 			while l do | 
 | 			{ | 
 | 				print("\t",head l,"\n"); | 
 | 				l = tail l; | 
 | 			} | 
 | 			_stoprunning = 1; | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | defn _addressof(pattern) { | 
 | 	local s, l; | 
 | 	l = symbols; | 
 | 	pattern = "^\\$*"+pattern+"$"; | 
 | 	while l do | 
 | 	{ | 
 | 		s = head l; | 
 | 		if regexp(pattern, s[0]) && ((s[1] == 'T') || (s[1] == 'L')) then | 
 | 			return s[2]; | 
 | 		l = tail l; | 
 | 	} | 
 | 	return 0; | 
 | } | 
 |  | 
 | stopPC = {}; | 
 | readPC = {}; | 
 | fd2pathPC = {}; | 
 | errstrPC = {}; | 
 | awaitPC = {}; | 
 | _waitPC = {}; | 
 | _errstrPC = {}; | 
 | trusscalls = { | 
 | 		"sysr1", | 
 | 		"_errstr", | 
 | 		"bind", | 
 | 		"chdir", | 
 | 		"close", | 
 | 		"dup", | 
 | 		"alarm", | 
 | 		"exec", | 
 | 		"_exits", | 
 | 		"_fsession", | 
 | 		"fauth", | 
 | 		"_fstat", | 
 | 		"segbrk", | 
 | 		"_mount", | 
 | 		"open", | 
 | 		"_read", | 
 | 		"oseek", | 
 | 		"sleep", | 
 | 		"_stat", | 
 | 		"rfork", | 
 | 		"_write", | 
 | 		"pipe", | 
 | 		"create", | 
 | 		"fd2path", | 
 | 		"brk_", | 
 | 		"remove", | 
 | 		"_wstat", | 
 | 		"_fwstat", | 
 | 		"notify", | 
 | 		"noted", | 
 | 		"segattach", | 
 | 		"segdetach", | 
 | 		"segfree", | 
 | 		"segflush", | 
 | 		"rendezvous", | 
 | 		"unmount", | 
 | 		"_wait", | 
 | 		"seek", | 
 | 		"fversion", | 
 | 		"errstr", | 
 | 		"stat", | 
 | 		"fstat", | 
 | 		"wstat", | 
 | 		"fwstat", | 
 | 		"mount", | 
 | 		"await", | 
 | 		"pread", | 
 | 		"pwrite", | 
 | 	}; | 
 |  | 
 | trussapecalls = { | 
 | 		"_SYSR1", | 
 | 		"__ERRSTR", | 
 | 		"_BIND", | 
 | 		"_CHDIR", | 
 | 		"_CLOSE", | 
 | 		"_DUP", | 
 | 		"_ALARM", | 
 | 		"_EXEC", | 
 | 		"_EXITS", | 
 | 		"__FSESSION", | 
 | 		"_FAUTH", | 
 | 		"__FSTAT", | 
 | 		"_SEGBRK", | 
 | 		"__MOUNT", | 
 | 		"_OPEN", | 
 | 		"__READ", | 
 | 		"_OSEEK", | 
 | 		"_SLEEP", | 
 | 		"__STAT", | 
 | 		"_RFORK", | 
 | 		"__WRITE", | 
 | 		"_PIPE", | 
 | 		"_CREATE", | 
 | 		"_FD2PATH", | 
 | 		"_BRK_", | 
 | 		"_REMOVE", | 
 | 		"__WSTAT", | 
 | 		"__FWSTAT", | 
 | 		"_NOTIFY", | 
 | 		"_NOTED", | 
 | 		"_SEGATTACH", | 
 | 		"_SEGDETACH", | 
 | 		"_SEGFREE", | 
 | 		"_SEGFLUSH", | 
 | 		"_RENDEZVOUS", | 
 | 		"_UNMOUNT", | 
 | 		"__WAIT", | 
 | 		"_SEEK", | 
 | 		"__NFVERSION", | 
 | 		"__NERRSTR", | 
 | 		"_STAT", | 
 | 		"__NFSTAT", | 
 | 		"__NWSTAT", | 
 | 		"__NFWSTAT", | 
 | 		"__NMOUNT", | 
 | 		"__NAWAIT", | 
 | 		"_PREAD", | 
 | 		"_PWRITE", | 
 | 	}; | 
 |  | 
 | defn addressof(pattern) { | 
 | 	// translate to ape system calls if we have an ape binary | 
 | 	if _addressof("_EXITS") == 0 then | 
 | 		return _addressof(pattern); | 
 | 	return _addressof(trussapecalls[match(pattern, trusscalls)]); | 
 | } | 
 |  | 
 | defn setuptruss() { | 
 | 	local lst, offset, name, addr; | 
 |  | 
 | 	trussbpt = {}; | 
 | 	offset = trapoffset(); | 
 | 	lst = trusscalls; | 
 | 	while lst do | 
 | 	{ | 
 | 		name = head lst; | 
 | 		lst = tail lst; | 
 | 		addr = addressof(name); | 
 | 		if addr then | 
 | 		{ | 
 | 			bpset(addr+offset); | 
 | 			trussbpt = append trussbpt, (addr+offset); | 
 | 			// sometimes _exits is renamed $_exits | 
 | 			if(regexp("exits|exec", name)) then stopPC = append stopPC, (addr+offset); | 
 | 			if(regexp("read", name)) then readPC = append readPC, (addr+offset); | 
 | 			if(regexp("fd2path", name)) then fd2pathPC = append fd2pathPC, (addr+offset); | 
 | 			if(regexp("^\\$*await", name)) then awaitPC = append awaitPC, (addr+offset); | 
 | 			if(regexp("^\\$*errstr", name)) then errstrPC = append errstrPC, (addr+offset); | 
 | 			// compatibility hacks for old kernel | 
 | 			if(regexp("_wait", name)) then _waitPC = append _waitPC, (addr+offset); | 
 | 			if(regexp("_errstr", name)) then _errstrPC = append _errstrPC, (addr+offset); | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | defn trussflush() { | 
 | 	stop(pid);		// already stopped, but flushes output | 
 | } | 
 |  | 
 | defn new() { | 
 | 	bplist = {}; | 
 | 	newproc(progargs); | 
 | 	bpset(follow(main)[0]); | 
 | 	cont(); | 
 | 	bpdel(*PC); | 
 | 	// clear the hang bit, which is left set by newproc, so programs we fork/exec don't hang | 
 | 	printto("/proc/"+itoa(pid)+"/ctl", "nohang"); | 
 | } | 
 |  | 
 | defn truss() { | 
 | 	local pc, lst, offset, prevpc, pcspret, ret; | 
 |  | 
 | 	offset = trapoffset(); | 
 |  | 
 | 	stop(pid); | 
 | 	_stoprunning = 0; | 
 | 	setuptruss(); | 
 | 	pcspret = UPCSPRET(); | 
 |  | 
 | 	while !_stoprunning do { | 
 | 		cont(); | 
 | 		if notes[0]!="sys: breakpoint" then { | 
 | 			cleantruss(); | 
 | 			return {}; | 
 | 		} | 
 | 		pc = *PC; | 
 | 		if match(*PC, stopPC)>=0 then { | 
 | 			print(pid,": ",trapreason(),"\t"); | 
 | 			print(fmt(pc,'a'),"\t",fmt(pc,'i'),"\n"); | 
 | 			cleantruss(); | 
 | 			return {}; | 
 | 		} | 
 | 		if match(*PC, trussbpt)>=0 then { | 
 | 			usyscall(); | 
 | 			trussflush(); | 
 | 			prevpc = *PC; | 
 | 			step(); | 
 | 			ret = eval pcspret[2]; | 
 | 			print("\treturn value: ", ret\D, "\n"); | 
 | 			if (ret>=0) && (match(prevpc, readPC)>=0) then { | 
 | 				print("\tdata: "); | 
 | 				printtextordata(*((eval pcspret[1])+4), ret); | 
 | 				print("\n"); | 
 | 			} | 
 | 			if (ret>=0) && (match(prevpc, fd2pathPC)>=0) then { | 
 | 				print("\tdata: \"", *(*((eval pcspret[1])+4)\s), "\"\n"); | 
 | 			} | 
 | 			if (ret>=0) && (match(prevpc, errstrPC)>=0) then { | 
 | 				print("\tdata: \"", *(*(eval pcspret[1])\s), "\"\n"); | 
 | 			} | 
 | 			if (ret>=0) && (match(prevpc, awaitPC)>=0) then { | 
 | 				print("\tdata: "); | 
 | 				printtextordata(*(eval pcspret[1]), ret); | 
 | 				print("\n"); | 
 | 			} | 
 | 			// compatibility hacks for old kernel: | 
 | 			if (ret>=0) && (match(prevpc, _waitPC)>=0) then { | 
 | 				print("\tdata: "); | 
 | 				printtextordata(*(eval pcspret[1]), 12+3*12+64); | 
 | 				print("\n"); | 
 | 			} | 
 | 			if (ret>=0) && (match(prevpc, _errstrPC)>=0) then { | 
 | 				print("\tdata: "); | 
 | 				printtextordata(*(eval pcspret[1]), 64); | 
 | 				print("\n"); | 
 | 			} | 
 | 		} | 
 | 		trussflush(); | 
 | 	} | 
 | } | 
 |  | 
 | defn cleantruss() { | 
 | 	local lst, offset, addr; | 
 |  | 
 | 	stop(pid); | 
 | 	offset = trapoffset(); | 
 | 	lst = trussbpt; | 
 | 	while lst do | 
 | 	{ | 
 | 		addr = head lst; | 
 | 		lst = tail lst; | 
 | 		bpdel(addr); | 
 | 	} | 
 | 	trussbpt = {}; | 
 | 	**PC = @*PC;	// repair current instruction | 
 | } | 
 |  | 
 | defn untruss() { | 
 | 	cleantruss(); | 
 | 	start(pid); | 
 | } | 
 |  | 
 | print(acidfile); |