9fans - fans of the OS Plan 9 from Bell Labs
 help / color / mirror / Atom feed
* Re: [9fans] truss
@ 2001-10-19 15:47 rog
  2001-10-20  1:18 ` rob pike
  0 siblings, 1 reply; 3+ messages in thread
From: rog @ 2001-10-19 15:47 UTC (permalink / raw)
  To: 9fans

rob wrote:
> Here's the output from my latest acid hack watching a
> shell (pid 500441) execute "date":

that's really nice.
is there any way of extending it so it can debug multithreaded
programs?  i've been wanting to do this for ages, but haven't found a
way of getting a newly forked process to hang around until the
debugger can get at it.

naively, i tried:

	if (ret>0) && (match(prevpc, rforkPC)>=0) then {
		print("\trforked pid=", ret\D, "\n");
		oldpid = ret;
		setproc(ret);
		setproc(oldpid);
	}

but of course i just get:

	rfork(0x00002014)
	child (pid: 1745)
	acid: <stdin>:3 (fatal problem) checkqid: (qid not checked) dirstat /proc/1745/text: file does not exist
		return value: 1745
		rforked pid=1745

the program was:

	void main(void)
	{
		if (fork())
			print("parent\n");
		else
			print("child\n");
	}

even though i've gone through the manual a few times,
i'm still not very clear on exactly how acid interacts
with multiple processes.

this would be incredibly useful for debugging emu...

  rog.



^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [9fans] truss
  2001-10-19 15:47 [9fans] truss rog
@ 2001-10-20  1:18 ` rob pike
  0 siblings, 0 replies; 3+ messages in thread
From: rob pike @ 2001-10-20  1:18 UTC (permalink / raw)
  To: 9fans

RE: multiprocess:

Have a look at how the truss code in acid handles the hang bit
and I think you'll find it's not too hard to do what you want.  Truss
deliberately avoids doing what you're asking because otherwise
programs like rc, mk, etc. are a true pain to truss.

To see how acid handles multiple process, see /sys/lib/acid/thread.
It's really not hard.

-rob




^ permalink raw reply	[flat|nested] 3+ messages in thread

* [9fans] truss
@ 2001-10-17 17:00 rob pike
  0 siblings, 0 replies; 3+ messages in thread
From: rob pike @ 2001-10-17 17:00 UTC (permalink / raw)
  To: 9fans

Here's the output from my latest acid hack watching a
shell (pid 500441) execute "date":

	dinar# acid -l truss 500441
	/proc/500441/text:386 plan 9 executable
	
	/sys/lib/acid/port
	/sys/lib/acid/kernel
	/sys/lib/acid/truss
	/sys/lib/acid/386
	acid: truss()
	create("#e/status", 1, 0000666)
		return value: 6
	write(6, 0x0001b320, 1)
		return value: 1
	close(6)
		return value: 0
	rfork(0x00002014)
		return value: 500446
	wait(0x7fffeec4)
		return value: 500446
		data: 0x7fffeec4, 500446
	write(2, 0x000184d0, 7)
		return value: 7
	read(4, 0x0001b0f0, 512)

The hack is a trivial emulation of some features of Ron
Gomes's System V TRUSS command.  I wrote it to disprove
someone's assertion that his program wasn't closing file
descriptor 0, but it's been used for a zillion things since
then.  Russ Cox has a related tool (trump) for analyzing
malloc; I'll let him post that one.  Both tools are nice
examples of the power of acid.

Here's a minimanual.
	acid -l truss 1234
attaches truss to process 1234.  To start tracing, say
	truss()
When you're done tracing, hit DEL to interrupt acid and say
	untruss()
You may need to wake up the process being trussed before
you see anything happen; for example, to wake up a shell
you'll need to hit a newline.

If you want to trace a command that hasn't started yet, say mk:
	acid -l truss /bin/8c
	progargs = "acme.c"
	new()
	truss()
and proceed as before.

Have fun.


The changes are easy.  First you need to fix a bug in acid:

# diff -n lex.c /sys/src/cmd/acid/lex.c
lex.c:261 a /n/emelie/sys/src/cmd/acid/lex.c:262,263
> 			if(esc)
> 				goto Default;
lex.c:270 a /n/emelie/sys/src/cmd/acid/lex.c:273
> 		Default:
# 

Then compile and install acid, and install these two files:

# To unbundle, run this file
echo /sys/lib/acid/truss
sed 's/.//' >/sys/lib/acid/truss <<'//GO.SYSIN DD /sys/lib/acid/truss'
-// poor emulation of SVR5 truss command - traces system calls
-
-include("/sys/lib/acid/kernel");
-
-_stoprunning = 0;
-
-defn stopped(pid) {
-	local l;
-	local pc;
-	pc = *PC;
-	if notes then {
-		if (notes[0]!="sys: breakpoint") then
-		{
-			print(pid,": ",reason(*TRAP),"\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')  then
-			return s[2];
-		l = tail l;
-	}
-	return 0;
-}
-
-stopPC = {};
-readPC = {};
-waitPC = {};
-_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",
-		"write9p",
-		"read9p",
-		"seek",
-		"pwrite",
-		"pread",
-	};
-
-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",
-		"_WRITE9P",
-		"_READ9P",
-		"_SEEK",
-		"_PWRITE",
-		"_PREAD",
-	};
-
-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|fd2path", name)) then readPC = append readPC, (addr+offset);
-			if(regexp("^\\$*(wait|errstr)", name)) then waitPC = append waitPC, (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,": ",reason(*TRAP),"\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, waitPC)>=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("/sys/lib/acid/truss");
//GO.SYSIN DD /sys/lib/acid/truss
echo /sys/lib/acid/kernel
sed 's/.//' >/sys/lib/acid/kernel <<'//GO.SYSIN DD /sys/lib/acid/kernel'
-// print system calls
-defn printstring(s)
-{
-	print("\"", s, "\"");
-}
-
-defn printtextordata(addr, n)
-{
-	local a, i;
-
-	a = addr\c;
-	i = 0;
-	loop 1, n do {
-		if (a[i]>=127) then {
-			print(fmt(addr, 'X'), ", ", n\D);
-			return {};
-		}
-		i = i+1;
-	}
-
-	print("\"");
-	printstringn(addr, n);
-	print("\"");
-}
-
-defn printstringn(s, n)
-{
-	local m;
-
-	m = n;
-	if (m > 100) then m = 100;
-	loop 1,m do {
-		print(*(s\c)); s=s+1;
-	}
-	if(m != n) then print("...");
-}
-
-defn printsyscall(name, fmt, arg) {
-	local f, i, a, argp, sl;
-
-	print(name, "(");
-	i = 0;
-	a = eval arg;
-	while fmt[i] != 0 do {
-		if fmt[i] == 's' then {
-			if *a == 0 then
-				print("nil");
-			else
-				printstring(*(*a\s));
-		} else if fmt[i] == 'S' then {
-			argp = *a;
-			argl = {};
-			while *argp != 0 do {
-				argl = append argl, *(*argp\s);
-				argp++;
-			}
-			print(argl);
-		} else if (fmt[i] == 'Z') && (~*a == 0) then {
-			print("-1");
-		} else if (fmt[i] == 'T') then {
-			if *a == 0 then
-				print("nil");
-			else
-				printtextordata(*a, a[1]);
-		} else
-			print(fmt(*a, fmt[i]));
-		if fmt[i+1] != 0 then
-			print(", ");
-		i = i+1;
-		a++;
-	}
-	print(")\n");
-}
-
-defn code(*e) { return e; }
-
-syscalls = {
-	{ 0, {"sysr1",		"s",		code(0)}},
-	{ 1, {"errstr",		"s",		code(*syserrstr:arg)}},
-	{ 2, {"bind",		"ssX",		code(*sysbind:arg)}},
-	{ 3, {"chdir",		"s",		code(*sysbind:arg)}},
-	{ 4, {"close",		"D",		code(*sysclose:arg)}},
-	{ 5, {"dup",		"DD",		code(*sysdup:arg)}},
-	{ 6, {"alarm",		"D",		code(*sysalarm:arg)}},
-	{ 7, {"exec",		"sS",		code(*sysexec:arg)}},
-	{ 8, {"exits",		"s",		code(*sysexits:arg)}},
-	{ 9, {"fsession",	"DX",		code(*sysfsession:arg)}},
-	{10, {"fauth",		"DX",		code(*sysfauth:arg)}},
-	{11, {"fstat",		"DX",		code(*sysfstat:arg)}},
-	{12, {"segbrk",		"XX",		code(*syssegbrk:arg)}},
-	{13, {"mount",		"DsXs",		code(*sysmount:arg)}},
-	{14, {"open",		"sD",		code(*sysopen:arg)}},
-	{15, {"read",		"DXD",		code(*sysread:arg)}},
-	{16, {"oseek",		"DDD",		code(*sysoseek:arg)}},
-	{17, {"sleep",		"D",		code(*syssleep:arg)}},
-	{18, {"stat",		"sX",		code(*sysstat:arg)}},
-	{19, {"rfork",		"X",		code(*sysstat:arg)}},
-	{20, {"write",		"DXD",		code(*syswrite:arg)}},
-	{21, {"pipe",		"X",		code(*syspipe:arg)}},
-	{22, {"create",		"sDO",		code(*syscreate:arg)}},
-	{23, {"fd2path",	"DXD",		code(*sysfd2path:arg)}},
-	{24, {"brk_",		"X",		code(*sysbrk_:arg)}},
-	{25, {"remove",		"s",		code(*sysremove:arg)}},
-	{26, {"wstat",		"sX",		code(*syswstat:arg)}},
-	{27, {"fwstat",		"DX",		code(*sysfwstat:arg)}},
-	{28, {"notify",		"X",		code(*sysnotify:arg)}},
-	{29, {"noted",		"D",		code(*sysnoted:arg)}},
-	{30, {"segattach",	"DsXD",		code(*syssegattach:arg)}},
-	{31, {"segdetach",	"X",		code(*syssegdetach:arg)}},
-	{32, {"segfree",	"XD",		code(*syssegfree:arg)}},
-	{33, {"segflush",	"XD",		code(*syssegflush:arg)}},
-	{34, {"rendezvous",	"XX",		code(*sysrendezvous:arg)}},
-	{35, {"unmount",	"ss",		code(*sysunmount:arg)}},
-	{36, {"wait",		"X",		code(*syswait:arg)}},
-	{37, {"read9p",		"DXD",		code(*sysread9p:arg)}},
-	{38, {"write9p",	"DXD",		code(*syswrite9p:arg)}},
-	{39, {"seek",		"DZD",		code(*sysseek:arg)}},
-};
-
-defn
-mappc() {
-	map({"*text", 0x80000000, 0xFFFFFFFF, 0x80000000});
-}
-
-defn syscall() {
-	local n, sl, h, p;
-
-	map({"*data", 0, 0xffffffff, 0});
-	n = *syscall:scallnr;
-	sl = syscalls;
-	while sl != {} do {
-		h = head sl;
-		sl = tail sl;
-
-		if n == h[0] then {
-			p = h[1];
-			printsyscall(p[0], p[1], p[2]);
-		}
-	}
-}
-
-defn UPCSPRET() {
-	// return sys call number, address of first argument, location of syscall return value
-	if objtype == "386" then 
-		return { code(*(*PC-4)), code(*SP+4), code(*AX) };
-	if objtype == "mips" then
-		return { code(*(*PC-4) & 0xffff), code(*SP+4), code(*R1) };
-	if objtype == "arm" then
-		return { code(*(*PC-4) & 0xffff), code(*SP+4), code(*R0) };	// untested
-	if objtype == "alpha" then
-		return { code(*(*PC-4) & 0xffff), code(*SP+4), code(*R0) };	// untested
-}
-
-defn trapoffset() {
-	// return offset from entry point to trap instr
-	if objtype == "386" then return 5;
-	if objtype == "mips" then return 8;
-	if objtype == "arm" then return 8;	// untested
-	if objtype == "alpha" then return 8;	// untested
-}	
-
-
-defn usyscall() {	// gives args for system call in user level; not useful with -k
-	local n, sl, h, p;
-
-	// stopped at TRAP instruction in system call library
-	pcsp = UPCSPRET();
-	n = eval pcsp[0];
-	sl = syscalls;
-	while sl != {} do {
-		h = head sl;
-		sl = tail sl;
-
-		if n == h[0] then {
-			p = h[1];
-			printsyscall(p[0], p[1], pcsp[1]);
-		}
-	}
-}
-
-// print various /proc files
-defn fd() {
-	rc("cat /proc/"+itoa(pid)+"/fd");
-}
-
-defn segment() {
-	rc("cat /proc/"+itoa(pid)+"/segment");
-}
-
-defn ns() {
-	rc("cat /proc/"+itoa(pid)+"/ns");
-}
-
-defn qid(qid) {
-	complex Qid qid;
-	return itoa(qid.path\X)+"."+itoa(qid.vers\X);
-}
-
-defn cname(c) {
-	complex Cname c;
-	if c != 0 then {
-		return c.s;
-	} else
-		return "<null>";
-}
-
-// print Image cache contents
-// requires include("/sys/src/9/xxx/segment.acid")
-IHASHSIZE = 64;
-defn imagecacheline(h) {
-	while h != 0 do {
-		complex Image h;
-		print (h\X, " ", qid(h.qid), " type ", h.type\D, " ref ", h.ref, " next ", h.next\X, " ", cname(h.c.name), "\n");
-		h = h.hash;
-	}
-}
-
-defn imagecache() {
-	local i;
-
-	i=0; loop 1,IHASHSIZE do {
-		imagecacheline(imagealloc.free[i]);
-		i = i+1;
-	}
-}
-
-// manipulate processes
-defn proctab(x) {
-	return procalloc.arena+sizeofProc*x;
-}
-
-defn proc(p) {
-	complex Proc p;
-	local s, i;
-
-	if p.state != 0 then {	// 0 is Dead
-		s = p.psstate;
-		if s == 0 then {
-			s = "kproc";
-		} else {
-			s = *(s\s);
-		}
-		print(p\X, " ", p.pid, ": ", *(p.text\s), " ", *(p.user\s), " pc ", p.pc\X, " ", s, " (", *(statename[p.state]\s), ") ut ", p.time[0]\D, " st ", p.time[1]\D, " qpc ", p.qpc\X, "\n");
-	}
-}
-
-defn procs() {
-	local i;
-
-	i=0; loop 1,conf.nproc do {
-		proc(proctab(i));
-		i = i+1;
-	}
-}
-
-// segment-related
-defn procsegs(p) {
-	complex Proc p;
-	local i;
-
-	i=0; loop 1,NSEG do {
-		psegment(p.seg[i]);
-		i = i+1;
-	}
-}
-
-segtypes = { "text", "data", "bss", "stack", "shared", "physical", "shdata", "map" };
-defn psegment(s) {
-	complex Segment s;
-
-	if s != 0 then {
-		print(s\X, " ", segtypes[s.type&SG_TYPE], " ", s.base\X, "-", s.top\X, " image ", s.image\X, "\n");
-	}
-}
-
-// find physical address for an address in a given process
-defn procaddr(p, a) {
-	complex Proc p;
-	local i, s, r;
-
-	r = 0;
-	i=0; loop 1,NSEG do {
-		s = p.seg[i];
-		if s != 0 then {
-			complex Segment s;
-			if s.base <= a && a < s.top then {
-				r = segaddr(s, a);
-			}
-		}
-		i = i+1;
-	}
-	return r;
-}
-
-// find an address in a given segment
-defn segaddr(s, a) {
-	complex Segment s;
-	local pte, pg;
-
-	a = a - s.base;
-	if s.map == 0 || s.mapsize < a/PTEMAPMEM then {
-		return 0;
-	}
-
-	pte = s.map[a/PTEMAPMEM];
-	if pte == 0 then {
-		return 0;
-	}
-
-	complex Pte pte;
-	pg = pte.pages[(a%PTEMAPMEM)/BY2PG];
-	if pg == 0 then {
-		return 0;
-	}
-
-	if pg & 1 then {	// swapped out, return disk address
-		return pg&~1;
-	}
-
-	complex Page pg;
-	return (0x80000000|(pg.pa+(a%BY2PG)))\X;
-}
-
-// PC only
-MACHADDR = 0x80004000;
-PTEMAPMEM = (1024*1024);
-BY2PG = 4096;
-PTEPERTAB = (PTEMAPMEM/BY2PG);
-defn up() {
-	local mach;
-
-	mach = MACHADDR;
-	complex Mach mach;
-	return mach.externup;
-}
-
-
-
-print("/sys/lib/acid/kernel");
//GO.SYSIN DD /sys/lib/acid/kernel


^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2001-10-20  1:18 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2001-10-19 15:47 [9fans] truss rog
2001-10-20  1:18 ` rob pike
  -- strict thread matches above, loose matches on Subject: below --
2001-10-17 17:00 rob pike

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).