![cinap_lenrek](/assets/img/avatar_default.png)
when there where multiple syscalls returning out of order, it would print blank lines between the exits. avoid this by remembering if the last char written was a newline and conditionally insert newline on out of order return. sometimes, ratrace would return before all messages have been printed. make the writer process the parent so ratrace wont exit until all readers are finished avoiding the problem.
214 lines
3.4 KiB
C
214 lines
3.4 KiB
C
#include <u.h>
|
|
#include <libc.h>
|
|
#include <thread.h>
|
|
|
|
enum {
|
|
Stacksize = 8*1024,
|
|
};
|
|
|
|
Channel *out;
|
|
Channel *quit;
|
|
Channel *forkc;
|
|
int readers = 1;
|
|
|
|
typedef struct Msg Msg;
|
|
struct Msg {
|
|
int pid;
|
|
char buf[8*1024];
|
|
};
|
|
|
|
typedef struct Reader Reader;
|
|
struct Reader {
|
|
int pid;
|
|
|
|
int tfd;
|
|
int cfd;
|
|
|
|
Msg* msg;
|
|
};
|
|
|
|
void
|
|
die(Reader *r)
|
|
{
|
|
Msg *s;
|
|
|
|
s = r->msg;
|
|
snprint(s->buf, sizeof(s->buf), " = %r\n");
|
|
s->pid = r->pid;
|
|
sendp(quit, s);
|
|
if(r->tfd >= 0)
|
|
close(r->tfd);
|
|
if(r->cfd >= 0)
|
|
close(r->cfd);
|
|
threadexits(nil);
|
|
}
|
|
|
|
void
|
|
cwrite(Reader *r, char *cmd)
|
|
{
|
|
if (write(r->cfd, cmd, strlen(cmd)) < 0)
|
|
die(r);
|
|
}
|
|
|
|
void
|
|
reader(void *v)
|
|
{
|
|
int forking = 0, newpid;
|
|
Reader r;
|
|
Msg *s;
|
|
|
|
r.pid = (int)(uintptr)v;
|
|
r.tfd = r.cfd = -1;
|
|
|
|
r.msg = s = mallocz(sizeof(Msg), 1);
|
|
snprint(s->buf, sizeof(s->buf), "/proc/%d/ctl", r.pid);
|
|
if ((r.cfd = open(s->buf, OWRITE)) < 0)
|
|
die(&r);
|
|
snprint(s->buf, sizeof(s->buf), "/proc/%d/syscall", r.pid);
|
|
if ((r.tfd = open(s->buf, OREAD)) < 0)
|
|
die(&r);
|
|
|
|
cwrite(&r, "stop");
|
|
cwrite(&r, "startsyscall");
|
|
|
|
while(pread(r.tfd, s->buf, sizeof(s->buf)-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);
|
|
}
|
|
s->pid = r.pid;
|
|
sendp(out, s);
|
|
|
|
r.msg = s = mallocz(sizeof(Msg), 1);
|
|
cwrite(&r, "startsyscall");
|
|
}
|
|
die(&r);
|
|
}
|
|
|
|
void
|
|
writer(int lastpid)
|
|
{
|
|
char lastc = -1;
|
|
Alt a[4];
|
|
Msg *s;
|
|
int n;
|
|
|
|
a[0].op = CHANRCV;
|
|
a[0].c = quit;
|
|
a[0].v = &s;
|
|
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;
|
|
|
|
while(readers > 0){
|
|
switch(alt(a)){
|
|
case 0:
|
|
readers--;
|
|
case 1:
|
|
if(s->pid != lastpid){
|
|
lastpid = s->pid;
|
|
if(lastc != '\n'){
|
|
lastc = '\n';
|
|
write(2, &lastc, 1);
|
|
}
|
|
if(s->buf[1] == '=')
|
|
fprint(2, "%d ...", lastpid);
|
|
}
|
|
n = strlen(s->buf);
|
|
if(n > 0){
|
|
write(2, s->buf, n);
|
|
lastc = s->buf[n-1];
|
|
}
|
|
free(s);
|
|
break;
|
|
case 2:
|
|
readers++;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
usage(void)
|
|
{
|
|
fprint(2, "Usage: ratrace [-c cmd [arg...]] | [pid]\n");
|
|
threadexits("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(Msg*), 0);
|
|
quit = chancreate(sizeof(Msg*), 0);
|
|
forkc = chancreate(sizeof(void*), 0);
|
|
procrfork(reader, (void*)pid, Stacksize, 0);
|
|
|
|
writer(pid);
|
|
threadexits(nil);
|
|
}
|