19677Slinton /* Copyright (c) 1982 Regents of the University of California */ 29677Slinton 3*16617Ssam static char sccsid[] = "@(#)process.c 1.12 8/19/83"; 49677Slinton 5*16617Ssam static char rcsid[] = "$Header: process.c,v 1.3 84/03/27 10:23:24 linton Exp $"; 6*16617Ssam 79677Slinton /* 89677Slinton * Process management. 99677Slinton * 109677Slinton * This module contains the routines to manage the execution and 119677Slinton * tracing of the debuggee process. 129677Slinton */ 139677Slinton 149677Slinton #include "defs.h" 159677Slinton #include "process.h" 169677Slinton #include "machine.h" 179677Slinton #include "events.h" 189677Slinton #include "tree.h" 1914757Slinton #include "eval.h" 209677Slinton #include "operators.h" 219677Slinton #include "source.h" 229677Slinton #include "object.h" 239677Slinton #include "mappings.h" 249677Slinton #include "main.h" 259677Slinton #include "coredump.h" 269677Slinton #include <signal.h> 279677Slinton #include <errno.h> 289677Slinton #include <sys/param.h> 29*16617Ssam #include <sys/dir.h> 30*16617Ssam #include <sys/user.h> 319843Slinton #include <machine/reg.h> 329677Slinton #include <sys/stat.h> 339677Slinton 349677Slinton #ifndef public 359677Slinton 369677Slinton typedef struct Process *Process; 379677Slinton 389677Slinton Process process; 399677Slinton 4014757Slinton #define DEFSIG -1 4114757Slinton 429677Slinton #include "machine.h" 439677Slinton 449677Slinton #endif 459677Slinton 469677Slinton #define NOTSTARTED 1 479677Slinton #define STOPPED 0177 489677Slinton #define FINISHED 0 499677Slinton 509677Slinton /* 51*16617Ssam * A cache of the instruction segment is kept to reduce the number 52*16617Ssam * of system calls. Might be better just to read the entire 53*16617Ssam * code space into memory. 549677Slinton */ 559677Slinton 569677Slinton #define CSIZE 1003 /* size of instruction cache */ 579677Slinton 589677Slinton typedef struct { 599677Slinton Word addr; 609677Slinton Word val; 619677Slinton } CacheWord; 629677Slinton 639677Slinton /* 649677Slinton * This structure holds the information we need from the user structure. 659677Slinton */ 669677Slinton 679677Slinton struct Process { 689677Slinton int pid; /* process being traced */ 6911768Slinton int mask; /* process status word */ 7011768Slinton Word reg[NREG]; /* process' registers */ 719677Slinton Word oreg[NREG]; /* registers when process last stopped */ 729677Slinton short status; /* either STOPPED or FINISHED */ 739677Slinton short signo; /* signal that stopped process */ 749677Slinton int exitval; /* return value from exit() */ 759677Slinton long sigset; /* bit array of traced signals */ 769677Slinton CacheWord word[CSIZE]; /* text segment cache */ 7711768Slinton Ttyinfo ttyinfo; /* process' terminal characteristics */ 78*16617Ssam Address sigstatus; /* process' handler for current signal */ 799677Slinton }; 809677Slinton 819677Slinton /* 829677Slinton * These definitions are for the arguments to "pio". 839677Slinton */ 849677Slinton 859677Slinton typedef enum { PREAD, PWRITE } PioOp; 869677Slinton typedef enum { TEXTSEG, DATASEG } PioSeg; 879677Slinton 889677Slinton private struct Process pbuf; 899677Slinton 9013841Slinton #define MAXNCMDARGS 100 /* maximum number of arguments to RUN */ 919677Slinton 9214395Slinton extern int errno; 9314395Slinton 949677Slinton private Boolean just_started; 959677Slinton private int argc; 969677Slinton private String argv[MAXNCMDARGS]; 979677Slinton private String infile, outfile; 989677Slinton 999677Slinton /* 1009677Slinton * Initialize process information. 1019677Slinton */ 1029677Slinton 1039677Slinton public process_init() 1049677Slinton { 1059677Slinton register Integer i; 1069677Slinton Char buf[10]; 1079677Slinton 1089677Slinton process = &pbuf; 1099677Slinton process->status = (coredump) ? STOPPED : NOTSTARTED; 1109677Slinton setsigtrace(); 1119677Slinton for (i = 0; i < NREG; i++) { 1129677Slinton sprintf(buf, "$r%d", i); 1139677Slinton defregname(identname(buf, false), i); 1149677Slinton } 1159677Slinton defregname(identname("$ap", true), ARGP); 1169677Slinton defregname(identname("$fp", true), FRP); 1179677Slinton defregname(identname("$sp", true), STKP); 1189677Slinton defregname(identname("$pc", true), PROGCTR); 1199677Slinton if (coredump) { 1209677Slinton coredump_readin(process->mask, process->reg, process->signo); 12112484Slinton pc = process->reg[PROGCTR]; 12212484Slinton getsrcpos(); 1239677Slinton } 12412484Slinton arginit(); 1259677Slinton } 1269677Slinton 1279677Slinton /* 1289677Slinton * Routines to get at process information from outside this module. 1299677Slinton */ 1309677Slinton 1319677Slinton public Word reg(n) 1329677Slinton Integer n; 1339677Slinton { 1349677Slinton register Word w; 1359677Slinton 1369677Slinton if (n == NREG) { 1379677Slinton w = process->mask; 1389677Slinton } else { 1399677Slinton w = process->reg[n]; 1409677Slinton } 1419677Slinton return w; 1429677Slinton } 1439677Slinton 1449677Slinton public setreg(n, w) 1459677Slinton Integer n; 1469677Slinton Word w; 1479677Slinton { 1489677Slinton process->reg[n] = w; 1499677Slinton } 1509677Slinton 1519677Slinton /* 1529677Slinton * Begin execution. 1539677Slinton * 1549677Slinton * We set a breakpoint at the end of the code so that the 1559677Slinton * process data doesn't disappear after the program terminates. 1569677Slinton */ 1579677Slinton 1589677Slinton private Boolean remade(); 1599677Slinton 1609677Slinton public start(argv, infile, outfile) 1619677Slinton String argv[]; 1629677Slinton String infile, outfile; 1639677Slinton { 1649677Slinton String pargv[4]; 1659677Slinton Node cond; 1669677Slinton 1679677Slinton if (coredump) { 1689677Slinton coredump = false; 1699677Slinton fclose(corefile); 1709677Slinton coredump_close(); 1719677Slinton } 1729677Slinton if (argv == nil) { 1739677Slinton argv = pargv; 1749677Slinton pargv[0] = objname; 1759677Slinton pargv[1] = nil; 1769677Slinton } else { 1779677Slinton argv[argc] = nil; 1789677Slinton } 1799677Slinton if (remade(objname)) { 1809677Slinton reinit(argv, infile, outfile); 1819677Slinton } 1829677Slinton pstart(process, argv, infile, outfile); 1839677Slinton if (process->status == STOPPED) { 1849677Slinton pc = 0; 185*16617Ssam setcurfunc(program); 1869677Slinton if (objsize != 0) { 1879677Slinton cond = build(O_EQ, build(O_SYM, pcsym), build(O_LCON, lastaddr())); 1889677Slinton event_once(cond, buildcmdlist(build(O_ENDX))); 1899677Slinton } 1909677Slinton } 1919677Slinton } 1929677Slinton 1939677Slinton /* 1949677Slinton * Check to see if the object file has changed since the symbolic 1959677Slinton * information last was read. 1969677Slinton */ 1979677Slinton 1989677Slinton private time_t modtime; 1999677Slinton 2009677Slinton private Boolean remade(filename) 2019677Slinton String filename; 2029677Slinton { 2039677Slinton struct stat s; 2049677Slinton Boolean b; 2059677Slinton 2069677Slinton stat(filename, &s); 2079677Slinton b = (Boolean) (modtime != 0 and modtime < s.st_mtime); 2089677Slinton modtime = s.st_mtime; 2099677Slinton return b; 2109677Slinton } 2119677Slinton 2129677Slinton /* 2139677Slinton * Set up what signals we want to trace. 2149677Slinton */ 2159677Slinton 2169677Slinton private setsigtrace() 2179677Slinton { 2189677Slinton register Integer i; 2199677Slinton register Process p; 2209677Slinton 2219677Slinton p = process; 2229677Slinton for (i = 1; i <= NSIG; i++) { 2239677Slinton psigtrace(p, i, true); 2249677Slinton } 2259677Slinton psigtrace(p, SIGHUP, false); 2269677Slinton psigtrace(p, SIGKILL, false); 2279677Slinton psigtrace(p, SIGALRM, false); 2289677Slinton psigtrace(p, SIGTSTP, false); 2299677Slinton psigtrace(p, SIGCONT, false); 2309677Slinton psigtrace(p, SIGCHLD, false); 2319677Slinton } 2329677Slinton 2339677Slinton /* 2349677Slinton * Initialize the argument list. 2359677Slinton */ 2369677Slinton 2379677Slinton public arginit() 2389677Slinton { 2399677Slinton infile = nil; 2409677Slinton outfile = nil; 2419677Slinton argv[0] = objname; 2429677Slinton argc = 1; 2439677Slinton } 2449677Slinton 2459677Slinton /* 2469677Slinton * Add an argument to the list for the debuggee. 2479677Slinton */ 2489677Slinton 2499677Slinton public newarg(arg) 2509677Slinton String arg; 2519677Slinton { 2529677Slinton if (argc >= MAXNCMDARGS) { 2539677Slinton error("too many arguments"); 2549677Slinton } 2559677Slinton argv[argc++] = arg; 2569677Slinton } 2579677Slinton 2589677Slinton /* 2599677Slinton * Set the standard input for the debuggee. 2609677Slinton */ 2619677Slinton 2629677Slinton public inarg(filename) 2639677Slinton String filename; 2649677Slinton { 2659677Slinton if (infile != nil) { 2669677Slinton error("multiple input redirects"); 2679677Slinton } 2689677Slinton infile = filename; 2699677Slinton } 2709677Slinton 2719677Slinton /* 2729677Slinton * Set the standard output for the debuggee. 2739677Slinton * Probably should check to avoid overwriting an existing file. 2749677Slinton */ 2759677Slinton 2769677Slinton public outarg(filename) 2779677Slinton String filename; 2789677Slinton { 2799677Slinton if (outfile != nil) { 2809677Slinton error("multiple output redirect"); 2819677Slinton } 2829677Slinton outfile = filename; 2839677Slinton } 2849677Slinton 2859677Slinton /* 2869677Slinton * Start debuggee executing. 2879677Slinton */ 2889677Slinton 2899677Slinton public run() 2909677Slinton { 2919677Slinton process->status = STOPPED; 2929677Slinton fixbps(); 2939677Slinton curline = 0; 2949677Slinton start(argv, infile, outfile); 2959677Slinton just_started = true; 2969677Slinton isstopped = false; 29714757Slinton cont(0); 2989677Slinton } 2999677Slinton 3009677Slinton /* 3019677Slinton * Continue execution wherever we left off. 3029677Slinton * 3039677Slinton * Note that this routine never returns. Eventually bpact() will fail 3049677Slinton * and we'll call printstatus or step will call it. 3059677Slinton */ 3069677Slinton 3079677Slinton typedef int Intfunc(); 3089677Slinton 3099677Slinton private Intfunc *dbintr; 3109677Slinton private intr(); 3119677Slinton 3129677Slinton #define succeeds == true 3139677Slinton #define fails == false 3149677Slinton 31511867Slinton public cont(signo) 316*16617Ssam integer signo; 3179677Slinton { 318*16617Ssam integer s; 319*16617Ssam 3209677Slinton dbintr = signal(SIGINT, intr); 3219677Slinton if (just_started) { 3229677Slinton just_started = false; 3239677Slinton } else { 3249677Slinton if (not isstopped) { 3259677Slinton error("can't continue execution"); 3269677Slinton } 3279677Slinton isstopped = false; 32811867Slinton stepover(); 3299677Slinton } 330*16617Ssam s = signo; 3319677Slinton for (;;) { 3329677Slinton if (single_stepping) { 3339677Slinton printnews(); 3349677Slinton } else { 3359677Slinton setallbps(); 336*16617Ssam resume(s); 3379677Slinton unsetallbps(); 338*16617Ssam s = DEFSIG; 3399677Slinton if (bpact() fails) { 3409677Slinton printstatus(); 3419677Slinton } 3429677Slinton } 34311867Slinton stepover(); 3449677Slinton } 3459677Slinton /* NOTREACHED */ 3469677Slinton } 3479677Slinton 3489677Slinton /* 3499677Slinton * This routine is called if we get an interrupt while "running" px 3509677Slinton * but actually in the debugger. Could happen, for example, while 3519677Slinton * processing breakpoints. 3529677Slinton * 3539677Slinton * We basically just want to keep going; the assumption is 3549677Slinton * that when the process resumes it will get the interrupt 3559677Slinton * which will then be handled. 3569677Slinton */ 3579677Slinton 3589677Slinton private intr() 3599677Slinton { 3609677Slinton signal(SIGINT, intr); 3619677Slinton } 3629677Slinton 3639677Slinton public fixintr() 3649677Slinton { 3659677Slinton signal(SIGINT, dbintr); 3669677Slinton } 3679677Slinton 3689677Slinton /* 3699677Slinton * Resume execution. 3709677Slinton */ 3719677Slinton 37211867Slinton public resume(signo) 37311867Slinton int signo; 3749677Slinton { 3759677Slinton register Process p; 3769677Slinton 3779677Slinton p = process; 37811867Slinton pcont(p, signo); 3799677Slinton pc = process->reg[PROGCTR]; 38011832Slinton if (p->status != STOPPED) { 38111867Slinton if (p->signo != 0) { 38211867Slinton error("program terminated by signal %d", p->signo); 38314757Slinton } else if (not runfirst) { 38411867Slinton error("program unexpectedly exited with %d", p->exitval); 38511867Slinton } 38611832Slinton } 3879677Slinton } 3889677Slinton 3899677Slinton /* 3909677Slinton * Continue execution up to the next source line. 3919677Slinton * 3929677Slinton * There are two ways to define the next source line depending on what 3939677Slinton * is desired when a procedure or function call is encountered. Step 3949677Slinton * stops at the beginning of the procedure or call; next skips over it. 3959677Slinton */ 3969677Slinton 3979677Slinton /* 3989677Slinton * Stepc is what is called when the step command is given. 3999677Slinton * It has to play with the "isstopped" information. 4009677Slinton */ 4019677Slinton 4029677Slinton public stepc() 4039677Slinton { 4049677Slinton if (not isstopped) { 4059677Slinton error("can't continue execution"); 4069677Slinton } 4079677Slinton isstopped = false; 4089677Slinton dostep(false); 4099677Slinton isstopped = true; 4109677Slinton } 4119677Slinton 4129677Slinton public next() 4139677Slinton { 414*16617Ssam Address oldfrp, newfrp; 415*16617Ssam 4169677Slinton if (not isstopped) { 4179677Slinton error("can't continue execution"); 4189677Slinton } 4199677Slinton isstopped = false; 420*16617Ssam oldfrp = reg(FRP); 421*16617Ssam do { 422*16617Ssam dostep(true); 423*16617Ssam pc = reg(PROGCTR); 424*16617Ssam newfrp = reg(FRP); 425*16617Ssam } while (newfrp < oldfrp and newfrp != 0); 4269677Slinton isstopped = true; 4279677Slinton } 4289677Slinton 42911867Slinton /* 430*16617Ssam * Continue execution until the current function returns, or, 431*16617Ssam * if the given argument is non-nil, until execution returns to 432*16617Ssam * somewhere within the given function. 433*16617Ssam */ 434*16617Ssam 435*16617Ssam public rtnfunc (f) 436*16617Ssam Symbol f; 437*16617Ssam { 438*16617Ssam Address addr; 439*16617Ssam Symbol t; 440*16617Ssam 441*16617Ssam if (not isstopped) { 442*16617Ssam error("can't continue execution"); 443*16617Ssam } else if (f != nil and not isactive(f)) { 444*16617Ssam error("%s is not active", symname(f)); 445*16617Ssam } else { 446*16617Ssam addr = return_addr(); 447*16617Ssam if (addr == nil) { 448*16617Ssam error("no place to return to"); 449*16617Ssam } else { 450*16617Ssam isstopped = false; 451*16617Ssam contto(addr); 452*16617Ssam if (f != nil) { 453*16617Ssam for (;;) { 454*16617Ssam t = whatblock(pc); 455*16617Ssam addr = return_addr(); 456*16617Ssam if (t == f or addr == nil) break; 457*16617Ssam contto(addr); 458*16617Ssam } 459*16617Ssam } 460*16617Ssam if (bpact() fails) { 461*16617Ssam isstopped = true; 462*16617Ssam printstatus(); 463*16617Ssam } 464*16617Ssam } 465*16617Ssam } 466*16617Ssam } 467*16617Ssam 468*16617Ssam /* 46911867Slinton * Single-step over the current machine instruction. 47011867Slinton * 47111867Slinton * If we're single-stepping by source line we want to step to the 47211867Slinton * next source line. Otherwise we're going to continue so there's 47311867Slinton * no reason to do all the work necessary to single-step to the next 47411867Slinton * source line. 47511867Slinton */ 47611867Slinton 477*16617Ssam public stepover() 4789677Slinton { 47911867Slinton Boolean b; 48011867Slinton 481*16617Ssam if (traceexec) { 482*16617Ssam printf("!! stepping over 0x%x\n", process->reg[PROGCTR]); 483*16617Ssam } 48411867Slinton if (single_stepping) { 48511867Slinton dostep(false); 48611867Slinton } else { 48711867Slinton b = inst_tracing; 48811867Slinton inst_tracing = true; 48911867Slinton dostep(false); 49011867Slinton inst_tracing = b; 49111867Slinton } 492*16617Ssam if (traceexec) { 493*16617Ssam printf("!! stepped over to 0x%x\n", process->reg[PROGCTR]); 494*16617Ssam } 4959677Slinton } 4969677Slinton 4979677Slinton /* 4989677Slinton * Resume execution up to the given address. It is assumed that 4999677Slinton * no breakpoints exist between the current address and the one 5009677Slinton * we're stepping to. This saves us from setting all the breakpoints. 5019677Slinton */ 5029677Slinton 5039677Slinton public stepto(addr) 5049677Slinton Address addr; 5059677Slinton { 506*16617Ssam xto(addr, false); 507*16617Ssam } 508*16617Ssam 509*16617Ssam private contto (addr) 510*16617Ssam Address addr; 511*16617Ssam { 512*16617Ssam xto(addr, true); 513*16617Ssam } 514*16617Ssam 515*16617Ssam private xto (addr, catchbps) 516*16617Ssam Address addr; 517*16617Ssam boolean catchbps; 518*16617Ssam { 519*16617Ssam Address curpc; 520*16617Ssam 521*16617Ssam if (catchbps) { 522*16617Ssam stepover(); 5239677Slinton } 524*16617Ssam curpc = process->reg[PROGCTR]; 525*16617Ssam if (addr != curpc) { 526*16617Ssam if (traceexec) { 527*16617Ssam printf("!! stepping from 0x%x to 0x%x\n", curpc, addr); 528*16617Ssam } 529*16617Ssam if (catchbps) { 530*16617Ssam setallbps(); 531*16617Ssam } 532*16617Ssam setbp(addr); 533*16617Ssam resume(DEFSIG); 534*16617Ssam unsetbp(addr); 535*16617Ssam if (catchbps) { 536*16617Ssam unsetallbps(); 537*16617Ssam } 538*16617Ssam if (not isbperr()) { 539*16617Ssam printstatus(); 540*16617Ssam } 541*16617Ssam } 5429677Slinton } 5439677Slinton 5449677Slinton /* 5459677Slinton * Print the status of the process. 5469677Slinton * This routine does not return. 5479677Slinton */ 5489677Slinton 5499677Slinton public printstatus() 5509677Slinton { 55114395Slinton int status; 55214395Slinton 5539843Slinton if (process->status == FINISHED) { 5549843Slinton exit(0); 5559843Slinton } else { 556*16617Ssam setcurfunc(whatblock(pc)); 5579677Slinton getsrcpos(); 5589843Slinton if (process->signo == SIGINT) { 5599843Slinton isstopped = true; 5609843Slinton printerror(); 5619843Slinton } else if (isbperr() and isstopped) { 5629843Slinton printf("stopped "); 56311172Slinton printloc(); 56411172Slinton putchar('\n'); 5659843Slinton if (curline > 0) { 5669843Slinton printlines(curline, curline); 5679843Slinton } else { 5689843Slinton printinst(pc, pc); 5699843Slinton } 5709843Slinton erecover(); 5719677Slinton } else { 5729843Slinton fixintr(); 5739677Slinton isstopped = true; 5749677Slinton printerror(); 5759677Slinton } 5769677Slinton } 5779677Slinton } 5789677Slinton 5799677Slinton /* 58011172Slinton * Print out the current location in the debuggee. 58111172Slinton */ 58211172Slinton 58311172Slinton public printloc() 58411172Slinton { 58511172Slinton printf("in "); 58611172Slinton printname(stdout, curfunc); 58711172Slinton putchar(' '); 58814757Slinton if (curline > 0 and not useInstLoc) { 58911172Slinton printsrcpos(); 59011172Slinton } else { 59114757Slinton useInstLoc = false; 59214757Slinton curline = 0; 59311172Slinton printf("at 0x%x", pc); 59411172Slinton } 59511172Slinton } 59611172Slinton 59711172Slinton /* 5989677Slinton * Some functions for testing the state of the process. 5999677Slinton */ 6009677Slinton 6019677Slinton public Boolean notstarted(p) 6029677Slinton Process p; 6039677Slinton { 6049677Slinton return (Boolean) (p->status == NOTSTARTED); 6059677Slinton } 6069677Slinton 6079677Slinton public Boolean isfinished(p) 6089677Slinton Process p; 6099677Slinton { 6109677Slinton return (Boolean) (p->status == FINISHED); 6119677Slinton } 6129677Slinton 6139677Slinton /* 6149677Slinton * Return the signal number which stopped the process. 6159677Slinton */ 6169677Slinton 6179677Slinton public Integer errnum(p) 6189677Slinton Process p; 6199677Slinton { 6209677Slinton return p->signo; 6219677Slinton } 6229677Slinton 6239677Slinton /* 6249677Slinton * Return the termination code of the process. 6259677Slinton */ 6269677Slinton 6279677Slinton public Integer exitcode(p) 6289677Slinton Process p; 6299677Slinton { 6309677Slinton return p->exitval; 6319677Slinton } 6329677Slinton 6339677Slinton /* 6349677Slinton * These routines are used to access the debuggee process from 6359677Slinton * outside this module. 6369677Slinton * 6379677Slinton * They invoke "pio" which eventually leads to a call to "ptrace". 63814757Slinton * The system generates an I/O error when a ptrace fails. During reads 63914757Slinton * these are ignored, during writes they are reported as an error, and 64014757Slinton * for anything else they cause a fatal error. 6419677Slinton */ 6429677Slinton 6439677Slinton extern Intfunc *onsyserr(); 6449677Slinton 6459677Slinton private badaddr; 64614757Slinton private read_err(), write_err(); 6479677Slinton 6489677Slinton /* 6499677Slinton * Read from the process' instruction area. 6509677Slinton */ 6519677Slinton 6529677Slinton public iread(buff, addr, nbytes) 6539677Slinton char *buff; 6549677Slinton Address addr; 6559677Slinton int nbytes; 6569677Slinton { 6579677Slinton Intfunc *f; 6589677Slinton 65914757Slinton f = onsyserr(EIO, read_err); 6609677Slinton badaddr = addr; 6619677Slinton if (coredump) { 6629677Slinton coredump_readtext(buff, addr, nbytes); 6639677Slinton } else { 6649677Slinton pio(process, PREAD, TEXTSEG, buff, addr, nbytes); 6659677Slinton } 6669677Slinton onsyserr(EIO, f); 6679677Slinton } 6689677Slinton 6699677Slinton /* 6709677Slinton * Write to the process' instruction area, usually in order to set 6719677Slinton * or unset a breakpoint. 6729677Slinton */ 6739677Slinton 6749677Slinton public iwrite(buff, addr, nbytes) 6759677Slinton char *buff; 6769677Slinton Address addr; 6779677Slinton int nbytes; 6789677Slinton { 6799677Slinton Intfunc *f; 6809677Slinton 6819677Slinton if (coredump) { 6829677Slinton error("no process to write to"); 6839677Slinton } 68414757Slinton f = onsyserr(EIO, write_err); 6859677Slinton badaddr = addr; 6869677Slinton pio(process, PWRITE, TEXTSEG, buff, addr, nbytes); 6879677Slinton onsyserr(EIO, f); 6889677Slinton } 6899677Slinton 6909677Slinton /* 6919677Slinton * Read for the process' data area. 6929677Slinton */ 6939677Slinton 6949677Slinton public dread(buff, addr, nbytes) 6959677Slinton char *buff; 6969677Slinton Address addr; 6979677Slinton int nbytes; 6989677Slinton { 6999677Slinton Intfunc *f; 7009677Slinton 70114757Slinton f = onsyserr(EIO, read_err); 7029677Slinton badaddr = addr; 7039677Slinton if (coredump) { 7049677Slinton coredump_readdata(buff, addr, nbytes); 7059677Slinton } else { 7069677Slinton pio(process, PREAD, DATASEG, buff, addr, nbytes); 7079677Slinton } 7089677Slinton onsyserr(EIO, f); 7099677Slinton } 7109677Slinton 7119677Slinton /* 7129677Slinton * Write to the process' data area. 7139677Slinton */ 7149677Slinton 7159677Slinton public dwrite(buff, addr, nbytes) 7169677Slinton char *buff; 7179677Slinton Address addr; 7189677Slinton int nbytes; 7199677Slinton { 7209677Slinton Intfunc *f; 7219677Slinton 7229677Slinton if (coredump) { 7239677Slinton error("no process to write to"); 7249677Slinton } 72514757Slinton f = onsyserr(EIO, write_err); 7269677Slinton badaddr = addr; 7279677Slinton pio(process, PWRITE, DATASEG, buff, addr, nbytes); 7289677Slinton onsyserr(EIO, f); 7299677Slinton } 7309677Slinton 7319677Slinton /* 73214757Slinton * Trap for errors in reading or writing to a process. 73314757Slinton * The current approach is to "ignore" read errors and complain 73414757Slinton * bitterly about write errors. 7359677Slinton */ 7369677Slinton 73714757Slinton private read_err() 7389677Slinton { 73911560Slinton /* 74014757Slinton * Ignore. 74111560Slinton */ 7429677Slinton } 7439677Slinton 74414757Slinton private write_err() 74514757Slinton { 74614757Slinton error("can't write to process (address 0x%x)", badaddr); 74714757Slinton } 74814757Slinton 7499677Slinton /* 7509677Slinton * Ptrace interface. 7519677Slinton */ 7529677Slinton 7539677Slinton /* 7549677Slinton * This magic macro enables us to look at the process' registers 75514757Slinton * in its user structure. 7569677Slinton */ 7579677Slinton 7589677Slinton #define regloc(reg) (ctob(UPAGES) + ( sizeof(int) * (reg) )) 7599677Slinton 7609677Slinton #define WMASK (~(sizeof(Word) - 1)) 7619677Slinton #define cachehash(addr) ((unsigned) ((addr >> 2) % CSIZE)) 7629677Slinton 7639677Slinton #define FIRSTSIG SIGINT 7649677Slinton #define LASTSIG SIGQUIT 7659677Slinton #define ischild(pid) ((pid) == 0) 7669677Slinton #define traceme() ptrace(0, 0, 0, 0) 7679677Slinton #define setrep(n) (1 << ((n)-1)) 7689677Slinton #define istraced(p) (p->sigset&setrep(p->signo)) 7699677Slinton 7709677Slinton /* 7719677Slinton * Ptrace options (specified in first argument). 7729677Slinton */ 7739677Slinton 7749677Slinton #define UREAD 3 /* read from process's user structure */ 7759677Slinton #define UWRITE 6 /* write to process's user structure */ 7769677Slinton #define IREAD 1 /* read from process's instruction space */ 7779677Slinton #define IWRITE 4 /* write to process's instruction space */ 7789677Slinton #define DREAD 2 /* read from process's data space */ 7799677Slinton #define DWRITE 5 /* write to process's data space */ 7809677Slinton #define CONT 7 /* continue stopped process */ 7819677Slinton #define SSTEP 9 /* continue for approximately one instruction */ 7829677Slinton #define PKILL 8 /* terminate the process */ 7839677Slinton 7849677Slinton /* 7859677Slinton * Start up a new process by forking and exec-ing the 7869677Slinton * given argument list, returning when the process is loaded 7879677Slinton * and ready to execute. The PROCESS information (pointed to 7889677Slinton * by the first argument) is appropriately filled. 7899677Slinton * 7909677Slinton * If the given PROCESS structure is associated with an already running 7919677Slinton * process, we terminate it. 7929677Slinton */ 7939677Slinton 7949677Slinton /* VARARGS2 */ 7959677Slinton private pstart(p, argv, infile, outfile) 7969677Slinton Process p; 7979677Slinton String argv[]; 7989677Slinton String infile; 7999677Slinton String outfile; 8009677Slinton { 8019677Slinton int status; 8029677Slinton 803*16617Ssam if (p->pid != 0) { 804*16617Ssam pterm(p); 8059677Slinton } 8069677Slinton psigtrace(p, SIGTRAP, true); 80714395Slinton p->pid = vfork(); 80814395Slinton if (p->pid == -1) { 8099677Slinton panic("can't fork"); 8109677Slinton } 8119677Slinton if (ischild(p->pid)) { 8129677Slinton traceme(); 8139677Slinton if (infile != nil) { 814*16617Ssam infrom(infile); 8159677Slinton } 8169677Slinton if (outfile != nil) { 817*16617Ssam outto(outfile); 8189677Slinton } 81911832Slinton execv(argv[0], argv); 82011172Slinton write(2, "can't exec ", 11); 82111172Slinton write(2, argv[0], strlen(argv[0])); 82211172Slinton write(2, "\n", 1); 82311172Slinton _exit(1); 8249677Slinton } 8259677Slinton pwait(p->pid, &status); 8269677Slinton getinfo(p, status); 8279677Slinton if (p->status != STOPPED) { 8289677Slinton error("program could not begin execution"); 8299677Slinton } 83014395Slinton ptraced(p->pid); 8319677Slinton } 8329677Slinton 8339677Slinton /* 834*16617Ssam * Terminate a ptrace'd process. 835*16617Ssam */ 836*16617Ssam 837*16617Ssam public pterm (p) 838*16617Ssam Process p; 839*16617Ssam { 840*16617Ssam integer status; 841*16617Ssam 842*16617Ssam if (p != nil and p->pid != 0) { 843*16617Ssam ptrace(PKILL, p->pid, 0, 0); 844*16617Ssam pwait(p->pid, &status); 845*16617Ssam unptraced(p->pid); 846*16617Ssam } 847*16617Ssam } 848*16617Ssam 849*16617Ssam /* 85011867Slinton * Continue a stopped process. The first argument points to a Process 85111867Slinton * structure. Before the process is restarted it's user area is modified 85211867Slinton * according to the values in the structure. When this routine finishes, 8539677Slinton * the structure has the new values from the process's user area. 8549677Slinton * 8559677Slinton * Pcont terminates when the process stops with a signal pending that 8569677Slinton * is being traced (via psigtrace), or when the process terminates. 8579677Slinton */ 8589677Slinton 85911867Slinton private pcont(p, signo) 8609677Slinton Process p; 86111867Slinton int signo; 8629677Slinton { 863*16617Ssam int s, status; 8649677Slinton 8659677Slinton if (p->pid == 0) { 8669677Slinton error("program not active"); 8679677Slinton } 868*16617Ssam s = signo; 8699677Slinton do { 870*16617Ssam setinfo(p, s); 871*16617Ssam if (traceexec) { 872*16617Ssam printf("!! pcont from 0x%x with signal %d (%d)\n", 873*16617Ssam p->reg[PROGCTR], s, p->signo); 874*16617Ssam fflush(stdout); 875*16617Ssam } 8769677Slinton sigs_off(); 8779677Slinton if (ptrace(CONT, p->pid, p->reg[PROGCTR], p->signo) < 0) { 87814395Slinton panic("error %d trying to continue process", errno); 8799677Slinton } 8809677Slinton pwait(p->pid, &status); 8819677Slinton sigs_on(); 8829677Slinton getinfo(p, status); 883*16617Ssam if (traceexec and not istraced(p)) { 884*16617Ssam printf("!! ignored signal %d at 0x%x\n", p->signo, p->reg[PROGCTR]); 885*16617Ssam fflush(stdout); 886*16617Ssam } 887*16617Ssam s = p->signo; 8889677Slinton } while (p->status == STOPPED and not istraced(p)); 889*16617Ssam if (traceexec) { 890*16617Ssam printf("!! pcont to 0x%x on signal %d\n", p->reg[PROGCTR], p->signo); 891*16617Ssam fflush(stdout); 892*16617Ssam } 8939677Slinton } 8949677Slinton 8959677Slinton /* 8969677Slinton * Single step as best ptrace can. 8979677Slinton */ 8989677Slinton 899*16617Ssam public pstep(p, signo) 9009677Slinton Process p; 901*16617Ssam integer signo; 9029677Slinton { 9039677Slinton int status; 9049677Slinton 905*16617Ssam setinfo(p, signo); 906*16617Ssam if (traceexec) { 907*16617Ssam printf("!! pstep from pc 0x%x with signal %d (%d)\n", 908*16617Ssam p->reg[PROGCTR], signo, p->signo); 909*16617Ssam fflush(stdout); 910*16617Ssam } 9119677Slinton sigs_off(); 912*16617Ssam if (ptrace(SSTEP, p->pid, p->reg[PROGCTR], p->signo) < 0) { 913*16617Ssam panic("error %d trying to step process", errno); 914*16617Ssam } 9159677Slinton pwait(p->pid, &status); 9169677Slinton sigs_on(); 9179677Slinton getinfo(p, status); 918*16617Ssam if (traceexec) { 919*16617Ssam printf("!! pstep to pc 0x%x on signal %d\n", p->reg[PROGCTR], p->signo); 920*16617Ssam fflush(stdout); 921*16617Ssam } 922*16617Ssam if (p->status != STOPPED) { 923*16617Ssam error("program unexpectedly exited with %d\n", p->exitval); 924*16617Ssam } 9259677Slinton } 9269677Slinton 9279677Slinton /* 9289677Slinton * Return from execution when the given signal is pending. 9299677Slinton */ 9309677Slinton 9319677Slinton public psigtrace(p, sig, sw) 9329677Slinton Process p; 9339677Slinton int sig; 9349677Slinton Boolean sw; 9359677Slinton { 9369677Slinton if (sw) { 9379677Slinton p->sigset |= setrep(sig); 9389677Slinton } else { 9399677Slinton p->sigset &= ~setrep(sig); 9409677Slinton } 9419677Slinton } 9429677Slinton 9439677Slinton /* 9449677Slinton * Don't catch any signals. 9459677Slinton * Particularly useful when letting a process finish uninhibited. 9469677Slinton */ 9479677Slinton 9489677Slinton public unsetsigtraces(p) 9499677Slinton Process p; 9509677Slinton { 9519677Slinton p->sigset = 0; 9529677Slinton } 9539677Slinton 9549677Slinton /* 9559677Slinton * Turn off attention to signals not being caught. 9569677Slinton */ 9579677Slinton 9589677Slinton private Intfunc *sigfunc[NSIG]; 9599677Slinton 9609677Slinton private sigs_off() 9619677Slinton { 9629677Slinton register int i; 9639677Slinton 9649677Slinton for (i = FIRSTSIG; i < LASTSIG; i++) { 9659677Slinton if (i != SIGKILL) { 9669677Slinton sigfunc[i] = signal(i, SIG_IGN); 9679677Slinton } 9689677Slinton } 9699677Slinton } 9709677Slinton 9719677Slinton /* 9729677Slinton * Turn back on attention to signals. 9739677Slinton */ 9749677Slinton 9759677Slinton private sigs_on() 9769677Slinton { 9779677Slinton register int i; 9789677Slinton 9799677Slinton for (i = FIRSTSIG; i < LASTSIG; i++) { 9809677Slinton if (i != SIGKILL) { 9819677Slinton signal(i, sigfunc[i]); 9829677Slinton } 9839677Slinton } 9849677Slinton } 9859677Slinton 9869677Slinton /* 9879677Slinton * Get process information from user area. 9889677Slinton */ 9899677Slinton 9909677Slinton private int rloc[] ={ 9919677Slinton R0, R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, AP, FP, SP, PC 9929677Slinton }; 9939677Slinton 9949677Slinton private getinfo(p, status) 9959677Slinton register Process p; 9969677Slinton register int status; 9979677Slinton { 9989677Slinton register int i; 999*16617Ssam Address addr; 10009677Slinton 10019677Slinton p->signo = (status&0177); 10029677Slinton p->exitval = ((status >> 8)&0377); 10039677Slinton if (p->signo != STOPPED) { 10049677Slinton p->status = FINISHED; 100514757Slinton p->pid = 0; 1006*16617Ssam p->reg[PROGCTR] = 0; 10079677Slinton } else { 10089677Slinton p->status = p->signo; 10099677Slinton p->signo = p->exitval; 10109677Slinton p->exitval = 0; 10119677Slinton p->mask = ptrace(UREAD, p->pid, regloc(PS), 0); 10129677Slinton for (i = 0; i < NREG; i++) { 10139677Slinton p->reg[i] = ptrace(UREAD, p->pid, regloc(rloc[i]), 0); 10149677Slinton p->oreg[i] = p->reg[i]; 10159677Slinton } 101611768Slinton savetty(stdout, &(p->ttyinfo)); 1017*16617Ssam addr = (Address) &(((struct user *) 0)->u_signal[p->signo]); 1018*16617Ssam p->sigstatus = (Address) ptrace(UREAD, p->pid, addr, 0); 10199677Slinton } 10209677Slinton } 10219677Slinton 10229677Slinton /* 10239677Slinton * Set process's user area information from given process structure. 10249677Slinton */ 10259677Slinton 102611867Slinton private setinfo(p, signo) 10279677Slinton register Process p; 102811867Slinton int signo; 10299677Slinton { 10309677Slinton register int i; 10319677Slinton register int r; 10329677Slinton 103314757Slinton if (signo == DEFSIG) { 1034*16617Ssam if (istraced(p) and (p->sigstatus == 0 or p->sigstatus == 1)) { 103514757Slinton p->signo = 0; 103614757Slinton } 103714757Slinton } else { 103811867Slinton p->signo = signo; 10399677Slinton } 10409677Slinton for (i = 0; i < NREG; i++) { 10419677Slinton if ((r = p->reg[i]) != p->oreg[i]) { 10429677Slinton ptrace(UWRITE, p->pid, regloc(rloc[i]), r); 10439677Slinton } 10449677Slinton } 104511768Slinton restoretty(stdout, &(p->ttyinfo)); 10469677Slinton } 10479677Slinton 10489677Slinton /* 1049*16617Ssam * Return the address associated with the current signal. 1050*16617Ssam * (Plus two since the address points to the beginning of a procedure). 1051*16617Ssam */ 1052*16617Ssam 1053*16617Ssam public Address usignal (p) 1054*16617Ssam Process p; 1055*16617Ssam { 1056*16617Ssam Address r; 1057*16617Ssam 1058*16617Ssam r = p->sigstatus; 1059*16617Ssam if (r != 0 and r != 1) { 1060*16617Ssam r += 2; 1061*16617Ssam } 1062*16617Ssam return r; 1063*16617Ssam } 1064*16617Ssam 1065*16617Ssam /* 10669677Slinton * Structure for reading and writing by words, but dealing with bytes. 10679677Slinton */ 10689677Slinton 10699677Slinton typedef union { 10709677Slinton Word pword; 10719677Slinton Byte pbyte[sizeof(Word)]; 10729677Slinton } Pword; 10739677Slinton 10749677Slinton /* 10759677Slinton * Read (write) from (to) the process' address space. 10769677Slinton * We must deal with ptrace's inability to look anywhere other 10779677Slinton * than at a word boundary. 10789677Slinton */ 10799677Slinton 10809677Slinton private Word fetch(); 10819677Slinton private store(); 10829677Slinton 10839677Slinton private pio(p, op, seg, buff, addr, nbytes) 10849677Slinton Process p; 10859677Slinton PioOp op; 10869677Slinton PioSeg seg; 10879677Slinton char *buff; 10889677Slinton Address addr; 10899677Slinton int nbytes; 10909677Slinton { 10919677Slinton register int i; 10929677Slinton register Address newaddr; 10939677Slinton register char *cp; 10949677Slinton char *bufend; 10959677Slinton Pword w; 10969677Slinton Address wordaddr; 10979677Slinton int byteoff; 10989677Slinton 10999677Slinton if (p->status != STOPPED) { 11009677Slinton error("program is not active"); 11019677Slinton } 11029677Slinton cp = buff; 11039677Slinton newaddr = addr; 11049677Slinton wordaddr = (newaddr&WMASK); 11059677Slinton if (wordaddr != newaddr) { 11069677Slinton w.pword = fetch(p, seg, wordaddr); 11079677Slinton for (i = newaddr - wordaddr; i < sizeof(Word) and nbytes > 0; i++) { 11089677Slinton if (op == PREAD) { 11099677Slinton *cp++ = w.pbyte[i]; 11109677Slinton } else { 11119677Slinton w.pbyte[i] = *cp++; 11129677Slinton } 11139677Slinton nbytes--; 11149677Slinton } 11159677Slinton if (op == PWRITE) { 11169677Slinton store(p, seg, wordaddr, w.pword); 11179677Slinton } 11189677Slinton newaddr = wordaddr + sizeof(Word); 11199677Slinton } 11209677Slinton byteoff = (nbytes&(~WMASK)); 11219677Slinton nbytes -= byteoff; 11229677Slinton bufend = cp + nbytes; 11239677Slinton while (cp < bufend) { 11249677Slinton if (op == PREAD) { 11259677Slinton *((Word *) cp) = fetch(p, seg, newaddr); 11269677Slinton } else { 11279677Slinton store(p, seg, newaddr, *((Word *) cp)); 11289677Slinton } 11299677Slinton cp += sizeof(Word); 11309677Slinton newaddr += sizeof(Word); 11319677Slinton } 11329677Slinton if (byteoff > 0) { 11339677Slinton w.pword = fetch(p, seg, newaddr); 11349677Slinton for (i = 0; i < byteoff; i++) { 11359677Slinton if (op == PREAD) { 11369677Slinton *cp++ = w.pbyte[i]; 11379677Slinton } else { 11389677Slinton w.pbyte[i] = *cp++; 11399677Slinton } 11409677Slinton } 11419677Slinton if (op == PWRITE) { 11429677Slinton store(p, seg, newaddr, w.pword); 11439677Slinton } 11449677Slinton } 11459677Slinton } 11469677Slinton 11479677Slinton /* 11489677Slinton * Get a word from a process at the given address. 11499677Slinton * The address is assumed to be on a word boundary. 11509677Slinton * 11519677Slinton * A simple cache scheme is used to avoid redundant ptrace calls 11529677Slinton * to the instruction space since it is assumed to be pure. 11539677Slinton * 11549677Slinton * It is necessary to use a write-through scheme so that 11559677Slinton * breakpoints right next to each other don't interfere. 11569677Slinton */ 11579677Slinton 11589677Slinton private Integer nfetchs, nreads, nwrites; 11599677Slinton 11609677Slinton private Word fetch(p, seg, addr) 11619677Slinton Process p; 11629677Slinton PioSeg seg; 11639677Slinton register int addr; 11649677Slinton { 11659677Slinton register CacheWord *wp; 11669677Slinton register Word w; 11679677Slinton 11689677Slinton switch (seg) { 11699677Slinton case TEXTSEG: 11709677Slinton ++nfetchs; 11719677Slinton wp = &p->word[cachehash(addr)]; 11729677Slinton if (addr == 0 or wp->addr != addr) { 11739677Slinton ++nreads; 11749677Slinton w = ptrace(IREAD, p->pid, addr, 0); 11759677Slinton wp->addr = addr; 11769677Slinton wp->val = w; 11779677Slinton } else { 11789677Slinton w = wp->val; 11799677Slinton } 11809677Slinton break; 11819677Slinton 11829677Slinton case DATASEG: 11839677Slinton w = ptrace(DREAD, p->pid, addr, 0); 11849677Slinton break; 11859677Slinton 11869677Slinton default: 11879677Slinton panic("fetch: bad seg %d", seg); 11889677Slinton /* NOTREACHED */ 11899677Slinton } 11909677Slinton return w; 11919677Slinton } 11929677Slinton 11939677Slinton /* 11949677Slinton * Put a word into the process' address space at the given address. 11959677Slinton * The address is assumed to be on a word boundary. 11969677Slinton */ 11979677Slinton 11989677Slinton private store(p, seg, addr, data) 11999677Slinton Process p; 12009677Slinton PioSeg seg; 12019677Slinton int addr; 12029677Slinton Word data; 12039677Slinton { 12049677Slinton register CacheWord *wp; 12059677Slinton 12069677Slinton switch (seg) { 12079677Slinton case TEXTSEG: 12089677Slinton ++nwrites; 12099677Slinton wp = &p->word[cachehash(addr)]; 12109677Slinton wp->addr = addr; 12119677Slinton wp->val = data; 12129677Slinton ptrace(IWRITE, p->pid, addr, data); 12139677Slinton break; 12149677Slinton 12159677Slinton case DATASEG: 12169677Slinton ptrace(DWRITE, p->pid, addr, data); 12179677Slinton break; 12189677Slinton 12199677Slinton default: 12209677Slinton panic("store: bad seg %d", seg); 12219677Slinton /* NOTREACHED */ 12229677Slinton } 12239677Slinton } 12249677Slinton 12259677Slinton public printptraceinfo() 12269677Slinton { 12279677Slinton printf("%d fetchs, %d reads, %d writes\n", nfetchs, nreads, nwrites); 12289677Slinton } 12299677Slinton 12309677Slinton /* 1231*16617Ssam * Redirect input. 1232*16617Ssam * Assuming this is called from a child, we should be careful to avoid 1233*16617Ssam * (possibly) shared standard I/O buffers. 12349677Slinton */ 12359677Slinton 1236*16617Ssam private infrom (filename) 1237*16617Ssam String filename; 1238*16617Ssam { 1239*16617Ssam Fileid in; 1240*16617Ssam 1241*16617Ssam in = open(filename, 0); 1242*16617Ssam if (in == -1) { 1243*16617Ssam write(2, "can't read ", 11); 1244*16617Ssam write(2, filename, strlen(filename)); 1245*16617Ssam write(2, "\n", 1); 1246*16617Ssam _exit(1); 1247*16617Ssam } 1248*16617Ssam fswap(0, in); 1249*16617Ssam } 1250*16617Ssam 1251*16617Ssam /* 1252*16617Ssam * Redirect standard output. 1253*16617Ssam * Same assumptions as for "infrom" above. 1254*16617Ssam */ 1255*16617Ssam 1256*16617Ssam private outto (filename) 1257*16617Ssam String filename; 1258*16617Ssam { 1259*16617Ssam Fileid out; 1260*16617Ssam 1261*16617Ssam out = creat(filename, 0666); 1262*16617Ssam if (out == -1) { 1263*16617Ssam write(2, "can't write ", 12); 1264*16617Ssam write(2, filename, strlen(filename)); 1265*16617Ssam write(2, "\n", 1); 1266*16617Ssam _exit(1); 1267*16617Ssam } 1268*16617Ssam fswap(1, out); 1269*16617Ssam } 1270*16617Ssam 1271*16617Ssam /* 1272*16617Ssam * Swap file numbers, useful for redirecting standard input or output. 1273*16617Ssam */ 1274*16617Ssam 12759677Slinton private fswap(oldfd, newfd) 1276*16617Ssam Fileid oldfd; 1277*16617Ssam Fileid newfd; 12789677Slinton { 12799677Slinton if (oldfd != newfd) { 12809677Slinton close(oldfd); 12819677Slinton dup(newfd); 12829677Slinton close(newfd); 12839677Slinton } 12849677Slinton } 1285