|  | // portable acid for all architectures | 
|  |  | 
|  | defn pfl(addr) | 
|  | { | 
|  | print(pcfile(addr), ":", pcline(addr), "\n"); | 
|  | } | 
|  |  | 
|  | defn | 
|  | notestk(addr) | 
|  | { | 
|  | local pc, sp; | 
|  | complex Ureg addr; | 
|  |  | 
|  | pc = addr.pc\X; | 
|  | sp = addr.sp\X; | 
|  |  | 
|  | print("Note pc:", pc, " sp:", sp, " ", fmt(pc, 'a'), " "); | 
|  | pfl(pc); | 
|  | _stk({"PC", pc, "SP", sp, linkreg(addr)}, 1); | 
|  | } | 
|  |  | 
|  | defn | 
|  | notelstk(addr) | 
|  | { | 
|  | local pc, sp; | 
|  | complex Ureg addr; | 
|  |  | 
|  | pc = addr.pc\X; | 
|  | sp = addr.sp\X; | 
|  |  | 
|  | print("Note pc:", pc, " sp:", sp, " ", fmt(pc, 'a'), " "); | 
|  | pfl(pc); | 
|  | _stk({"PC", pc, "SP", sp, linkreg(addr)}, 1); | 
|  | } | 
|  |  | 
|  | defn params(param) | 
|  | { | 
|  | while param do { | 
|  | sym = head param; | 
|  | print(sym[0], "=", itoa(sym[1], "%#ux")); | 
|  | param = tail param; | 
|  | if param then | 
|  | print (","); | 
|  | } | 
|  | } | 
|  |  | 
|  | stkprefix = ""; | 
|  | stkignore = {}; | 
|  | stkend = 0; | 
|  |  | 
|  | defn locals(l) | 
|  | { | 
|  | local sym; | 
|  |  | 
|  | while l do { | 
|  | sym = head l; | 
|  | print(stkprefix, "\t", sym[0], "=", itoa(sym[1], "%#ux"), "\n"); | 
|  | l = tail l; | 
|  | } | 
|  | } | 
|  |  | 
|  | defn _stkign(frame) | 
|  | { | 
|  | local file; | 
|  |  | 
|  | file = pcfile(frame[0]); | 
|  | s = stkignore; | 
|  | while s do { | 
|  | if regexp(head s, file) then | 
|  | return 1; | 
|  | s = tail s; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | // print a stack trace | 
|  | // | 
|  | // in a run of leading frames in files matched by regexps in stkignore, | 
|  | // only print the last one. | 
|  | defn _stk(regs, dolocals) | 
|  | { | 
|  | local stk, frame, pc, fn, done, callerpc, paramlist, locallist; | 
|  |  | 
|  | stk = strace(regs); | 
|  | if stkignore then { | 
|  | while stk && tail stk && _stkign(head tail stk) do | 
|  | stk = tail stk; | 
|  | } | 
|  |  | 
|  | callerpc = 0; | 
|  | done = 0; | 
|  | while stk && !done do { | 
|  | frame = head stk; | 
|  | stk = tail stk; | 
|  | fn = frame[0]; | 
|  | pc = frame[1]; | 
|  | callerpc = frame[2]; | 
|  | paramlist = frame[3]; | 
|  | locallist = frame[4]; | 
|  |  | 
|  | print(stkprefix, fmt(fn, 'a'), "("); | 
|  | params(paramlist); | 
|  | print(")"); | 
|  | if pc != fn then | 
|  | print("+", itoa(pc-fn, "%#ux")); | 
|  | print(" "); | 
|  | pfl(pc); | 
|  | if dolocals then | 
|  | locals(locallist); | 
|  | if fn == var("threadmain") || fn == var("p9main") then | 
|  | done=1; | 
|  | if fn == var("threadstart") || fn == var("scheduler") then | 
|  | done=1; | 
|  | if callerpc == 0 then | 
|  | done=1; | 
|  | } | 
|  | if callerpc && !done then { | 
|  | print(stkprefix, fmt(callerpc, 'a'), " "); | 
|  | pfl(callerpc); | 
|  | } | 
|  | } | 
|  |  | 
|  | defn findsrc(file) | 
|  | { | 
|  | local lst, src; | 
|  |  | 
|  | if file[0] == '/' then { | 
|  | src = file(file); | 
|  | if src != {} then { | 
|  | srcfiles = append srcfiles, file; | 
|  | srctext = append srctext, src; | 
|  | return src; | 
|  | } | 
|  | return {}; | 
|  | } | 
|  |  | 
|  | lst = srcpath; | 
|  | while head lst do { | 
|  | src = file(head lst+file); | 
|  | if src != {} then { | 
|  | srcfiles = append srcfiles, file; | 
|  | srctext = append srctext, src; | 
|  | return src; | 
|  | } | 
|  | lst = tail lst; | 
|  | } | 
|  | } | 
|  |  | 
|  | defn line(addr) | 
|  | { | 
|  | local src, file; | 
|  |  | 
|  | file = pcfile(addr); | 
|  | src = match(file, srcfiles); | 
|  |  | 
|  | if src >= 0 then | 
|  | src = srctext[src]; | 
|  | else | 
|  | src = findsrc(file); | 
|  |  | 
|  | if src == {} then { | 
|  | print("no source for ", file, "\n"); | 
|  | return {}; | 
|  | } | 
|  | line = pcline(addr)-1; | 
|  | print(file, ":", src[line], "\n"); | 
|  | } | 
|  |  | 
|  | defn addsrcdir(dir) | 
|  | { | 
|  | dir = dir+"/"; | 
|  |  | 
|  | if match(dir, srcpath) >= 0 then { | 
|  | print("already in srcpath\n"); | 
|  | return {}; | 
|  | } | 
|  |  | 
|  | srcpath = {dir}+srcpath; | 
|  | } | 
|  |  | 
|  | defn source() | 
|  | { | 
|  | local l; | 
|  |  | 
|  | l = srcpath; | 
|  | while l do { | 
|  | print(head l, "\n"); | 
|  | l = tail l; | 
|  | } | 
|  | l = srcfiles; | 
|  |  | 
|  | while l do { | 
|  | print("\t", head l, "\n"); | 
|  | l = tail l; | 
|  | } | 
|  | } | 
|  |  | 
|  | defn Bsrc(addr) | 
|  | { | 
|  | local lst; | 
|  |  | 
|  | lst = srcpath; | 
|  | file = pcfile(addr); | 
|  | if file[0] == '/' && access(file) then { | 
|  | rc("B "+file+":"+itoa(pcline(addr))); | 
|  | return {}; | 
|  | } | 
|  | while head lst do { | 
|  | name = head lst+file; | 
|  | if access(name) then { | 
|  | rc("B "+name+":"+itoa(pcline(addr))); | 
|  | return {}; | 
|  | } | 
|  | lst = tail lst; | 
|  | } | 
|  | print("no source for ", file, "\n"); | 
|  | } | 
|  |  | 
|  | defn srcline(addr) | 
|  | { | 
|  | local text, cline, line, file, src; | 
|  | file = pcfile(addr); | 
|  | src = match(file,srcfiles); | 
|  | if (src>=0) then | 
|  | src = srctext[src]; | 
|  | else | 
|  | src = findsrc(file); | 
|  | if (src=={}) then | 
|  | { | 
|  | return "(no source)"; | 
|  | } | 
|  | return src[pcline(addr)-1]; | 
|  | } | 
|  |  | 
|  | defn src(addr) | 
|  | { | 
|  | local src, file, line, cline, text; | 
|  |  | 
|  | file = pcfile(addr); | 
|  | src = match(file, srcfiles); | 
|  |  | 
|  | if src >= 0 then | 
|  | src = srctext[src]; | 
|  | else | 
|  | src = findsrc(file); | 
|  |  | 
|  | if src == {} then { | 
|  | print("no source for ", file, "\n"); | 
|  | return {}; | 
|  | } | 
|  |  | 
|  | cline = pcline(addr)-1; | 
|  | print(file, ":", cline+1, "\n"); | 
|  | line = cline-5; | 
|  | loop 0,10 do { | 
|  | if line >= 0 then { | 
|  | if line == cline then | 
|  | print(">"); | 
|  | else | 
|  | print(" "); | 
|  | text = src[line]; | 
|  | if text == {} then | 
|  | return {}; | 
|  | print(line+1, "\t", text, "\n"); | 
|  | } | 
|  | line = line+1; | 
|  | } | 
|  | } | 
|  |  | 
|  | defn step()					// single step the process | 
|  | { | 
|  | local lst, lpl, addr, bput; | 
|  |  | 
|  | bput = 0; | 
|  | if match(*PC, bplist) >= 0 then {	// Sitting on a breakpoint | 
|  | bput = fmt(*PC, bpfmt); | 
|  | *bput = @bput; | 
|  | } | 
|  |  | 
|  | lst = follow(*PC); | 
|  |  | 
|  | lpl = lst; | 
|  | while lpl do {				// place break points | 
|  | *(head lpl) = bpinst; | 
|  | lpl = tail lpl; | 
|  | } | 
|  |  | 
|  | startstop(pid);				// do the step | 
|  |  | 
|  | while lst do {				// remove the breakpoints | 
|  | addr = fmt(head lst, bpfmt); | 
|  | *addr = @addr; | 
|  | lst = tail lst; | 
|  | } | 
|  | if bput != 0 then | 
|  | *bput = bpinst; | 
|  | } | 
|  |  | 
|  | defn bpset(addr)				// set a breakpoint | 
|  | { | 
|  | if status(pid) != "Stopped" then { | 
|  | print("Waiting...\n"); | 
|  | stop(pid); | 
|  | } | 
|  | if match(addr, bplist) >= 0 then | 
|  | print("breakpoint already set at ", fmt(addr, 'a'), "\n"); | 
|  | else { | 
|  | *fmt(addr, bpfmt) = bpinst; | 
|  | bplist = append bplist, addr; | 
|  | } | 
|  | } | 
|  |  | 
|  | defn bptab()					// print a table of breakpoints | 
|  | { | 
|  | local lst, addr; | 
|  |  | 
|  | lst = bplist; | 
|  | while lst do { | 
|  | addr = head lst; | 
|  | print("\t", fmt(addr, 'X'), " ", fmt(addr, 'a'), "  ", fmt(addr, 'i'), "\n"); | 
|  | lst = tail lst; | 
|  | } | 
|  | } | 
|  |  | 
|  | defn bpdel(addr)				// delete a breakpoint | 
|  | { | 
|  | local n, pc, nbplist; | 
|  |  | 
|  | if addr == 0 then { | 
|  | while bplist do { | 
|  | pc = head bplist; | 
|  | pc = fmt(pc, bpfmt); | 
|  | *pc = @pc; | 
|  | bplist = tail bplist; | 
|  | } | 
|  | return {}; | 
|  | } | 
|  |  | 
|  | n = match(addr, bplist); | 
|  | if n < 0  then { | 
|  | print("no breakpoint at ", fmt(addr, 'a'), "\n"); | 
|  | return {}; | 
|  | } | 
|  |  | 
|  | addr = fmt(addr, bpfmt); | 
|  | *addr = @addr; | 
|  |  | 
|  | nbplist = {};				// delete from list | 
|  | while bplist do { | 
|  | pc = head bplist; | 
|  | if pc != addr then | 
|  | nbplist = append nbplist, pc; | 
|  | bplist = tail bplist; | 
|  | } | 
|  | bplist = nbplist;			// delete from memory | 
|  | } | 
|  |  | 
|  | defn cont()					// continue execution | 
|  | { | 
|  | local addr; | 
|  |  | 
|  | addr = fmt(*PC, bpfmt); | 
|  | if match(addr, bplist) >= 0 then {	// Sitting on a breakpoint | 
|  | *addr = @addr; | 
|  | step();				// Step over | 
|  | *addr = bpinst; | 
|  | } | 
|  | startstop(pid);				// Run | 
|  | } | 
|  |  | 
|  | defn stopped(pid)		// called from acid when a process changes state | 
|  | { | 
|  | pfixstop(pid); | 
|  | pstop(pid);		// stub so this is easy to replace | 
|  | } | 
|  |  | 
|  | defn procs()			// print status of processes | 
|  | { | 
|  | local c, lst, cpid; | 
|  |  | 
|  | cpid = pid; | 
|  | lst = proclist; | 
|  | while lst do { | 
|  | np = head lst; | 
|  | setproc(np); | 
|  | if np == cpid then | 
|  | c = '>'; | 
|  | else | 
|  | c = ' '; | 
|  | print(fmt(c, 'c'), np, ": ", status(np), " at ", fmt(*PC, 'a'), " setproc(", np, ")\n"); | 
|  | lst = tail lst; | 
|  | } | 
|  | pid = cpid; | 
|  | if pid != 0 then | 
|  | setproc(pid); | 
|  | } | 
|  |  | 
|  | _asmlines = 30; | 
|  |  | 
|  | defn asm(addr) | 
|  | { | 
|  | local bound; | 
|  |  | 
|  | bound = fnbound(addr); | 
|  |  | 
|  | addr = fmt(addr, 'i'); | 
|  | loop 1,_asmlines do { | 
|  | print(fmt(addr, 'a'), " ", fmt(addr, 'X')); | 
|  | print("\t", @addr++, "\n"); | 
|  | if bound != {} && addr > bound[1] then { | 
|  | lasmaddr = addr; | 
|  | return {}; | 
|  | } | 
|  | } | 
|  | lasmaddr = addr; | 
|  | } | 
|  |  | 
|  | defn casm() | 
|  | { | 
|  | asm(lasmaddr); | 
|  | } | 
|  |  | 
|  | defn xasm(addr) | 
|  | { | 
|  | local bound; | 
|  |  | 
|  | bound = fnbound(addr); | 
|  |  | 
|  | addr = fmt(addr, 'i'); | 
|  | loop 1,_asmlines do { | 
|  | print(fmt(addr, 'a'), " ", fmt(addr, 'X')); | 
|  | print("\t", *addr++, "\n"); | 
|  | if bound != {} && addr > bound[1] then { | 
|  | lasmaddr = addr; | 
|  | return {}; | 
|  | } | 
|  | } | 
|  | lasmaddr = addr; | 
|  | } | 
|  |  | 
|  | defn xcasm() | 
|  | { | 
|  | xasm(lasmaddr); | 
|  | } | 
|  |  | 
|  | defn win() | 
|  | { | 
|  | local npid, estr; | 
|  |  | 
|  | bplist = {}; | 
|  | notes = {}; | 
|  |  | 
|  | estr = "/sys/lib/acid/window '0 0 600 400' "+textfile; | 
|  | if progargs != "" then | 
|  | estr = estr+" "+progargs; | 
|  |  | 
|  | npid = rc(estr); | 
|  | npid = atoi(npid); | 
|  | if npid == 0 then | 
|  | error("win failed to create process"); | 
|  |  | 
|  | setproc(npid); | 
|  | stopped(npid); | 
|  | } | 
|  |  | 
|  | defn win2() | 
|  | { | 
|  | local npid, estr; | 
|  |  | 
|  | bplist = {}; | 
|  | notes = {}; | 
|  |  | 
|  | estr = "/sys/lib/acid/transcript '0 0 600 400' '100 100 700 500' "+textfile; | 
|  | if progargs != "" then | 
|  | estr = estr+" "+progargs; | 
|  |  | 
|  | npid = rc(estr); | 
|  | npid = atoi(npid); | 
|  | if npid == 0 then | 
|  | error("win failed to create process"); | 
|  |  | 
|  | setproc(npid); | 
|  | stopped(npid); | 
|  | } | 
|  |  | 
|  | printstopped = 1; | 
|  | defn new() | 
|  | { | 
|  | local a; | 
|  |  | 
|  | bplist = {}; | 
|  | newproc(progargs); | 
|  | a = var("p9main"); | 
|  | if a == {} then | 
|  | a = var("main"); | 
|  | if a == {} then | 
|  | return {}; | 
|  | bpset(a); | 
|  | while *PC != a do | 
|  | cont(); | 
|  | bpdel(a); | 
|  | } | 
|  |  | 
|  | defn stmnt()			// step one statement | 
|  | { | 
|  | local line; | 
|  |  | 
|  | line = pcline(*PC); | 
|  | while 1 do { | 
|  | step(); | 
|  | if line != pcline(*PC) then { | 
|  | src(*PC); | 
|  | return {}; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | defn func()			// step until we leave the current function | 
|  | { | 
|  | local bound, end, start, pc; | 
|  |  | 
|  | bound = fnbound(*PC); | 
|  | if bound == {} then { | 
|  | print("cannot locate text symbol\n"); | 
|  | return {}; | 
|  | } | 
|  |  | 
|  | pc = *PC; | 
|  | start = bound[0]; | 
|  | end = bound[1]; | 
|  | while pc >= start && pc < end do { | 
|  | step(); | 
|  | pc = *PC; | 
|  | } | 
|  | } | 
|  |  | 
|  | defn next() | 
|  | { | 
|  | local sp, bound, pc; | 
|  |  | 
|  | sp = *SP; | 
|  | bound = fnbound(*PC); | 
|  | if bound == {} then { | 
|  | print("cannot locate text symbol\n"); | 
|  | return {}; | 
|  | } | 
|  | stmnt(); | 
|  | pc = *PC; | 
|  | if pc >= bound[0] && pc < bound[1] then | 
|  | return {}; | 
|  |  | 
|  | while (pc < bound[0] || pc > bound[1]) && sp >= *SP do { | 
|  | step(); | 
|  | pc = *PC; | 
|  | } | 
|  | src(*PC); | 
|  | } | 
|  |  | 
|  | defn maps() | 
|  | { | 
|  | local m, mm; | 
|  |  | 
|  | m = map(); | 
|  | while m != {} do { | 
|  | mm = head m; | 
|  | m = tail m; | 
|  | print(mm[2]\X, " ", mm[3]\X, " ", mm[4]\X, " ", mm[0], " ", mm[1], "\n"); | 
|  | } | 
|  | } | 
|  |  | 
|  | defn dump(addr, n, fmt) | 
|  | { | 
|  | loop 0, n do { | 
|  | print(fmt(addr, 'X'), ": "); | 
|  | addr = mem(addr, fmt); | 
|  | } | 
|  | } | 
|  |  | 
|  | defn mem(addr, fmt) | 
|  | { | 
|  |  | 
|  | local i, c, n; | 
|  |  | 
|  | i = 0; | 
|  | while fmt[i] != 0 do { | 
|  | c = fmt[i]; | 
|  | n = 0; | 
|  | while '0' <= fmt[i] && fmt[i] <= '9' do { | 
|  | n = 10*n + fmt[i]-'0'; | 
|  | i = i+1; | 
|  | } | 
|  | if n <= 0 then n = 1; | 
|  | addr = fmt(addr, fmt[i]); | 
|  | while n > 0 do { | 
|  | print(*addr++, " "); | 
|  | n = n-1; | 
|  | } | 
|  | i = i+1; | 
|  | } | 
|  | print("\n"); | 
|  | return addr; | 
|  | } | 
|  |  | 
|  | defn symbols(pattern) | 
|  | { | 
|  | local l, s, name; | 
|  |  | 
|  | l = symbols; | 
|  | while l do { | 
|  | s = head l; | 
|  | if regexp(pattern, s[0]) then { | 
|  | name = s[4]; | 
|  | if name == {} then | 
|  | name = ""; | 
|  | print(s[0], "\t", s[1], "\t", s[2], "\t", s[3], "\t", name, "\n"); | 
|  | } | 
|  | l = tail l; | 
|  | } | 
|  | } | 
|  |  | 
|  | defn havesymbol(name) | 
|  | { | 
|  | local l, s; | 
|  |  | 
|  | l = symbols; | 
|  | while l do { | 
|  | s = head l; | 
|  | l = tail l; | 
|  | if s[0] == name then | 
|  | return 1; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | defn spsrch(len) | 
|  | { | 
|  | local addr, a, s, e; | 
|  |  | 
|  | addr = *SP; | 
|  | s = origin & 0x7fffffff; | 
|  | e = etext & 0x7fffffff; | 
|  | loop 1, len do { | 
|  | a = *addr++; | 
|  | c = a & 0x7fffffff; | 
|  | if c > s && c < e then { | 
|  | print("src(", a, ")\n"); | 
|  | pfl(a); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | defn acidtypes() | 
|  | { | 
|  | local syms; | 
|  | local l; | 
|  |  | 
|  | l = textfile(); | 
|  | if l != {} then { | 
|  | syms = "acidtypes"; | 
|  | while l != {} do { | 
|  | syms = syms + " " + ((head l)[0]); | 
|  | l = tail l; | 
|  | } | 
|  | includepipe(syms); | 
|  | } | 
|  | } | 
|  |  | 
|  | defn getregs() | 
|  | { | 
|  | local regs, l; | 
|  |  | 
|  | regs = {}; | 
|  | l = registers; | 
|  | while l != {} do { | 
|  | regs = append regs, var(l[0]); | 
|  | l = tail l; | 
|  | } | 
|  | return regs; | 
|  | } | 
|  |  | 
|  | defn setregs(regs) | 
|  | { | 
|  | local l; | 
|  |  | 
|  | l = registers; | 
|  | while l != {} do { | 
|  | var(l[0]) = regs[0]; | 
|  | l = tail l; | 
|  | regs = tail regs; | 
|  | } | 
|  | return regs; | 
|  | } | 
|  |  | 
|  | defn resetregs() | 
|  | { | 
|  | local l; | 
|  |  | 
|  | l = registers; | 
|  | while l != {} do { | 
|  | var(l[0]) = register(l[0]); | 
|  | l = tail l; | 
|  | } | 
|  | } | 
|  |  | 
|  | defn clearregs() | 
|  | { | 
|  | local l; | 
|  |  | 
|  | l = registers; | 
|  | while l != {} do { | 
|  | var(l[0]) = refconst(~0); | 
|  | l = tail l; | 
|  | } | 
|  | } | 
|  |  | 
|  | progargs=""; | 
|  | print(acidfile); | 
|  |  |