shithub: riscv

ref: 3f869a689403c385ca761be5535a5de2db054db4
dir: /sys/src/cmd/ratrace.c/

View raw version
#include <u.h>
#include <libc.h>
#include <thread.h>

enum {
	Stacksize	= 8*1024,
	Bufsize		= 8*1024,
};

Channel *out;
Channel *quit;
Channel *forkc;
int nread = 0;

typedef struct Msg Msg;
struct Msg {
	char	*buf;
	int	pid;
};

void
die(char *s)
{
	fprint(2, "%s\n", s);
	exits(s);
}

void
cwrite(int fd, char *path, char *cmd, int len)
{
	if (write(fd, cmd, len) < len) {
		fprint(2, "cwrite: %s: failed %d bytes: %r\n", path, len);
		sendp(quit, nil);
		threadexits(nil);
	}
}

void
reader(void *v)
{
	int cfd, tfd, forking = 0, pid, newpid;
	char *ctl, *truss;
	Msg *s;

	pid = (int)(uintptr)v;
	ctl = smprint("/proc/%d/ctl", pid);
	if ((cfd = open(ctl, OWRITE)) < 0)
		die(smprint("%s: %r", ctl));
	truss = smprint("/proc/%d/syscall", pid);
	if ((tfd = open(truss, OREAD)) < 0)
		die(smprint("%s: %r", truss));

	cwrite(cfd, ctl, "stop", 4);
	cwrite(cfd, truss, "startsyscall", 12);

	s = mallocz(sizeof(Msg) + Bufsize, 1);
	s->pid = pid;
	s->buf = (char *)&s[1];
	while(pread(tfd, s->buf, Bufsize - 1, 0) > 0){
		if (forking && s->buf[1] == '=' && s->buf[3] != '-') {
			forking = 0;
			newpid = strtol(&s->buf[3], 0, 0);
			sendp(forkc, (void*)newpid);
			procrfork(reader, (void*)newpid, Stacksize, 0);
		}

		/*
		 * There are three tests here and they (I hope) guarantee
		 * no false positives.
		 */
		if (strstr(s->buf, " Rfork") != nil) {
			char *a[8];
			char *rf;

			rf = strdup(s->buf);
         		if (tokenize(rf, a, 8) == 5) {
				ulong flags;

				flags = strtoul(a[4], 0, 16);
				if (flags & RFPROC)
					forking = 1;
			}
			free(rf);
		}
		sendp(out, s);
		cwrite(cfd, truss, "startsyscall", 12);
		s = mallocz(sizeof(Msg) + Bufsize, 1);
		s->pid = pid;
		s->buf = (char *)&s[1];
	}
	sendp(quit, nil);
	threadexitsall(nil);
}

void
writer(void *arg)
{
	int lastpid;
	Alt a[4];
	Msg *s;

	lastpid = (int)(uintptr)arg;

	a[0].op = CHANRCV;
	a[0].c = quit;
	a[0].v = nil;
	a[1].op = CHANRCV;
	a[1].c = out;
	a[1].v = &s;
	a[2].op = CHANRCV;
	a[2].c = forkc;
	a[2].v = nil;
	a[3].op = CHANEND;

	for(;;)
		switch(alt(a)){
		case 0:
			nread--;
			if(nread <= 0)
				goto done;
			break;
		case 1:
			if(s->pid != lastpid){
				lastpid = s->pid;
				fprint(2, s->buf[1]=='='? "\n%d ...": "\n", lastpid);
			}
			fprint(2, "%s", s->buf);
			free(s);
			break;
		case 2:
			nread++;
			break;
		}
done:
	exits(nil);
}

void
usage(void)
{
	fprint(2, "Usage: ratrace [-c cmd [arg...]] | [pid]\n");
	exits("usage");
}

void
threadmain(int argc, char **argv)
{
	int pid;
	char *cmd = nil;
	char **args = nil;

	/*
	 * don't bother with fancy arg processing, because it picks up options
	 * for the command you are starting.  Just check for -c as argv[1]
	 * and then take it from there.
	 */
	if (argc < 2)
		usage();
	if (argv[1][0] == '-')
		switch(argv[1][1]) {
		case 'c':
			if (argc < 3)
				usage();
			cmd = strdup(argv[2]);
			args = &argv[2];
			break;
		default:
			usage();
		}

	/* run a command? */
	if(cmd) {
		pid = fork();
		if (pid < 0)
			sysfatal("fork failed: %r");
		if(pid == 0) {
			exec(cmd, args);
			if(cmd[0] != '/')
				exec(smprint("/bin/%s", cmd), args);
			sysfatal("exec %s failed: %r", cmd);
		}
	} else {
		if(argc != 2)
			usage();
		pid = atoi(argv[1]);
	}

	out   = chancreate(sizeof(char*), 0);
	quit  = chancreate(sizeof(char*), 0);
	forkc = chancreate(sizeof(ulong *), 0);
	nread++;
	procrfork(writer, (void*)pid, Stacksize, 0);
	reader((void*)pid);
}