Subversion Repositories planix.SVN

Rev

Blame | Last modification | View Log | RSS feed

// poor emulation of SVR5 truss command - traces system calls

include("/sys/lib/acid/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("/sys/lib/acid/truss");