plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

truss (5244B)


      1 // poor emulation of SVR5 truss command - traces system calls
      2 
      3 include("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(acidfile);