xref: /csrg-svn/old/dbx/process.c (revision 14757)
19677Slinton /* Copyright (c) 1982 Regents of the University of California */
29677Slinton 
3*14757Slinton static char sccsid[] = "@(#)process.c 1.12 08/19/83";
49677Slinton 
59677Slinton /*
69677Slinton  * Process management.
79677Slinton  *
89677Slinton  * This module contains the routines to manage the execution and
99677Slinton  * tracing of the debuggee process.
109677Slinton  */
119677Slinton 
129677Slinton #include "defs.h"
139677Slinton #include "process.h"
149677Slinton #include "machine.h"
159677Slinton #include "events.h"
169677Slinton #include "tree.h"
17*14757Slinton #include "eval.h"
189677Slinton #include "operators.h"
199677Slinton #include "source.h"
209677Slinton #include "object.h"
219677Slinton #include "mappings.h"
229677Slinton #include "main.h"
239677Slinton #include "coredump.h"
249677Slinton #include <signal.h>
259677Slinton #include <errno.h>
269677Slinton #include <sys/param.h>
279843Slinton #include <machine/reg.h>
289677Slinton #include <sys/stat.h>
299677Slinton 
309677Slinton #ifndef public
319677Slinton 
329677Slinton typedef struct Process *Process;
339677Slinton 
349677Slinton Process process;
359677Slinton 
36*14757Slinton #define DEFSIG -1
37*14757Slinton 
389677Slinton #include "machine.h"
399677Slinton 
409677Slinton #endif
419677Slinton 
429677Slinton #define NOTSTARTED 1
439677Slinton #define STOPPED 0177
449677Slinton #define FINISHED 0
459677Slinton 
469677Slinton /*
479677Slinton  * Cache-ing of instruction segment is done to reduce the number
489677Slinton  * of system calls.
499677Slinton  */
509677Slinton 
519677Slinton #define CSIZE 1003       /* size of instruction cache */
529677Slinton 
539677Slinton typedef struct {
549677Slinton     Word addr;
559677Slinton     Word val;
569677Slinton } CacheWord;
579677Slinton 
589677Slinton /*
599677Slinton  * This structure holds the information we need from the user structure.
609677Slinton  */
619677Slinton 
629677Slinton struct Process {
639677Slinton     int pid;			/* process being traced */
6411768Slinton     int mask;			/* process status word */
6511768Slinton     Word reg[NREG];		/* process' registers */
669677Slinton     Word oreg[NREG];		/* registers when process last stopped */
679677Slinton     short status;		/* either STOPPED or FINISHED */
689677Slinton     short signo;		/* signal that stopped process */
699677Slinton     int exitval;		/* return value from exit() */
709677Slinton     long sigset;		/* bit array of traced signals */
719677Slinton     CacheWord word[CSIZE];	/* text segment cache */
7211768Slinton     Ttyinfo ttyinfo;		/* process' terminal characteristics */
739677Slinton };
749677Slinton 
759677Slinton /*
769677Slinton  * These definitions are for the arguments to "pio".
779677Slinton  */
789677Slinton 
799677Slinton typedef enum { PREAD, PWRITE } PioOp;
809677Slinton typedef enum { TEXTSEG, DATASEG } PioSeg;
819677Slinton 
829677Slinton private struct Process pbuf;
839677Slinton 
8413841Slinton #define MAXNCMDARGS 100         /* maximum number of arguments to RUN */
859677Slinton 
8614395Slinton extern int errno;
8714395Slinton 
889677Slinton private Boolean just_started;
899677Slinton private int argc;
909677Slinton private String argv[MAXNCMDARGS];
919677Slinton private String infile, outfile;
929677Slinton 
939677Slinton /*
949677Slinton  * Initialize process information.
959677Slinton  */
969677Slinton 
979677Slinton public process_init()
989677Slinton {
999677Slinton     register Integer i;
1009677Slinton     Char buf[10];
1019677Slinton 
1029677Slinton     process = &pbuf;
1039677Slinton     process->status = (coredump) ? STOPPED : NOTSTARTED;
1049677Slinton     setsigtrace();
1059677Slinton     for (i = 0; i < NREG; i++) {
1069677Slinton 	sprintf(buf, "$r%d", i);
1079677Slinton 	defregname(identname(buf, false), i);
1089677Slinton     }
1099677Slinton     defregname(identname("$ap", true), ARGP);
1109677Slinton     defregname(identname("$fp", true), FRP);
1119677Slinton     defregname(identname("$sp", true), STKP);
1129677Slinton     defregname(identname("$pc", true), PROGCTR);
1139677Slinton     if (coredump) {
1149677Slinton 	coredump_readin(process->mask, process->reg, process->signo);
11512484Slinton 	pc = process->reg[PROGCTR];
11612484Slinton 	getsrcpos();
1179677Slinton     }
11812484Slinton     arginit();
1199677Slinton }
1209677Slinton 
1219677Slinton /*
1229677Slinton  * Routines to get at process information from outside this module.
1239677Slinton  */
1249677Slinton 
1259677Slinton public Word reg(n)
1269677Slinton Integer n;
1279677Slinton {
1289677Slinton     register Word w;
1299677Slinton 
1309677Slinton     if (n == NREG) {
1319677Slinton 	w = process->mask;
1329677Slinton     } else {
1339677Slinton 	w = process->reg[n];
1349677Slinton     }
1359677Slinton     return w;
1369677Slinton }
1379677Slinton 
1389677Slinton public setreg(n, w)
1399677Slinton Integer n;
1409677Slinton Word w;
1419677Slinton {
1429677Slinton     process->reg[n] = w;
1439677Slinton }
1449677Slinton 
1459677Slinton /*
1469677Slinton  * Begin execution.
1479677Slinton  *
1489677Slinton  * We set a breakpoint at the end of the code so that the
1499677Slinton  * process data doesn't disappear after the program terminates.
1509677Slinton  */
1519677Slinton 
1529677Slinton private Boolean remade();
1539677Slinton 
1549677Slinton public start(argv, infile, outfile)
1559677Slinton String argv[];
1569677Slinton String infile, outfile;
1579677Slinton {
1589677Slinton     String pargv[4];
1599677Slinton     Node cond;
1609677Slinton 
1619677Slinton     if (coredump) {
1629677Slinton 	coredump = false;
1639677Slinton 	fclose(corefile);
1649677Slinton 	coredump_close();
1659677Slinton     }
1669677Slinton     if (argv == nil) {
1679677Slinton 	argv = pargv;
1689677Slinton 	pargv[0] = objname;
1699677Slinton 	pargv[1] = nil;
1709677Slinton     } else {
1719677Slinton 	argv[argc] = nil;
1729677Slinton     }
1739677Slinton     if (remade(objname)) {
1749677Slinton 	reinit(argv, infile, outfile);
1759677Slinton     }
1769677Slinton     pstart(process, argv, infile, outfile);
1779677Slinton     if (process->status == STOPPED) {
1789677Slinton 	pc = 0;
1799677Slinton 	curfunc = program;
1809677Slinton 	if (objsize != 0) {
1819677Slinton 	    cond = build(O_EQ, build(O_SYM, pcsym), build(O_LCON, lastaddr()));
1829677Slinton 	    event_once(cond, buildcmdlist(build(O_ENDX)));
1839677Slinton 	}
1849677Slinton     }
1859677Slinton }
1869677Slinton 
1879677Slinton /*
1889677Slinton  * Check to see if the object file has changed since the symbolic
1899677Slinton  * information last was read.
1909677Slinton  */
1919677Slinton 
1929677Slinton private time_t modtime;
1939677Slinton 
1949677Slinton private Boolean remade(filename)
1959677Slinton String filename;
1969677Slinton {
1979677Slinton     struct stat s;
1989677Slinton     Boolean b;
1999677Slinton 
2009677Slinton     stat(filename, &s);
2019677Slinton     b = (Boolean) (modtime != 0 and modtime < s.st_mtime);
2029677Slinton     modtime = s.st_mtime;
2039677Slinton     return b;
2049677Slinton }
2059677Slinton 
2069677Slinton /*
2079677Slinton  * Set up what signals we want to trace.
2089677Slinton  */
2099677Slinton 
2109677Slinton private setsigtrace()
2119677Slinton {
2129677Slinton     register Integer i;
2139677Slinton     register Process p;
2149677Slinton 
2159677Slinton     p = process;
2169677Slinton     for (i = 1; i <= NSIG; i++) {
2179677Slinton 	psigtrace(p, i, true);
2189677Slinton     }
2199677Slinton     psigtrace(p, SIGHUP, false);
2209677Slinton     psigtrace(p, SIGKILL, false);
2219677Slinton     psigtrace(p, SIGALRM, false);
2229677Slinton     psigtrace(p, SIGTSTP, false);
2239677Slinton     psigtrace(p, SIGCONT, false);
2249677Slinton     psigtrace(p, SIGCHLD, false);
2259677Slinton }
2269677Slinton 
2279677Slinton /*
2289677Slinton  * Initialize the argument list.
2299677Slinton  */
2309677Slinton 
2319677Slinton public arginit()
2329677Slinton {
2339677Slinton     infile = nil;
2349677Slinton     outfile = nil;
2359677Slinton     argv[0] = objname;
2369677Slinton     argc = 1;
2379677Slinton }
2389677Slinton 
2399677Slinton /*
2409677Slinton  * Add an argument to the list for the debuggee.
2419677Slinton  */
2429677Slinton 
2439677Slinton public newarg(arg)
2449677Slinton String arg;
2459677Slinton {
2469677Slinton     if (argc >= MAXNCMDARGS) {
2479677Slinton 	error("too many arguments");
2489677Slinton     }
2499677Slinton     argv[argc++] = arg;
2509677Slinton }
2519677Slinton 
2529677Slinton /*
2539677Slinton  * Set the standard input for the debuggee.
2549677Slinton  */
2559677Slinton 
2569677Slinton public inarg(filename)
2579677Slinton String filename;
2589677Slinton {
2599677Slinton     if (infile != nil) {
2609677Slinton 	error("multiple input redirects");
2619677Slinton     }
2629677Slinton     infile = filename;
2639677Slinton }
2649677Slinton 
2659677Slinton /*
2669677Slinton  * Set the standard output for the debuggee.
2679677Slinton  * Probably should check to avoid overwriting an existing file.
2689677Slinton  */
2699677Slinton 
2709677Slinton public outarg(filename)
2719677Slinton String filename;
2729677Slinton {
2739677Slinton     if (outfile != nil) {
2749677Slinton 	error("multiple output redirect");
2759677Slinton     }
2769677Slinton     outfile = filename;
2779677Slinton }
2789677Slinton 
2799677Slinton /*
2809677Slinton  * Start debuggee executing.
2819677Slinton  */
2829677Slinton 
2839677Slinton public run()
2849677Slinton {
2859677Slinton     process->status = STOPPED;
2869677Slinton     fixbps();
2879677Slinton     curline = 0;
2889677Slinton     start(argv, infile, outfile);
2899677Slinton     just_started = true;
2909677Slinton     isstopped = false;
291*14757Slinton     cont(0);
2929677Slinton }
2939677Slinton 
2949677Slinton /*
2959677Slinton  * Continue execution wherever we left off.
2969677Slinton  *
2979677Slinton  * Note that this routine never returns.  Eventually bpact() will fail
2989677Slinton  * and we'll call printstatus or step will call it.
2999677Slinton  */
3009677Slinton 
3019677Slinton typedef int Intfunc();
3029677Slinton 
3039677Slinton private Intfunc *dbintr;
3049677Slinton private intr();
3059677Slinton 
3069677Slinton #define succeeds    == true
3079677Slinton #define fails       == false
3089677Slinton 
30911867Slinton public cont(signo)
31011867Slinton int signo;
3119677Slinton {
3129677Slinton     dbintr = signal(SIGINT, intr);
3139677Slinton     if (just_started) {
3149677Slinton 	just_started = false;
3159677Slinton     } else {
3169677Slinton 	if (not isstopped) {
3179677Slinton 	    error("can't continue execution");
3189677Slinton 	}
3199677Slinton 	isstopped = false;
32011867Slinton 	stepover();
3219677Slinton     }
3229677Slinton     for (;;) {
3239677Slinton 	if (single_stepping) {
3249677Slinton 	    printnews();
3259677Slinton 	} else {
3269677Slinton 	    setallbps();
32711867Slinton 	    resume(signo);
3289677Slinton 	    unsetallbps();
3299677Slinton 	    if (bpact() fails) {
3309677Slinton 		printstatus();
3319677Slinton 	    }
3329677Slinton 	}
33311867Slinton 	stepover();
3349677Slinton     }
3359677Slinton     /* NOTREACHED */
3369677Slinton }
3379677Slinton 
3389677Slinton /*
3399677Slinton  * This routine is called if we get an interrupt while "running" px
3409677Slinton  * but actually in the debugger.  Could happen, for example, while
3419677Slinton  * processing breakpoints.
3429677Slinton  *
3439677Slinton  * We basically just want to keep going; the assumption is
3449677Slinton  * that when the process resumes it will get the interrupt
3459677Slinton  * which will then be handled.
3469677Slinton  */
3479677Slinton 
3489677Slinton private intr()
3499677Slinton {
3509677Slinton     signal(SIGINT, intr);
3519677Slinton }
3529677Slinton 
3539677Slinton public fixintr()
3549677Slinton {
3559677Slinton     signal(SIGINT, dbintr);
3569677Slinton }
3579677Slinton 
3589677Slinton /*
3599677Slinton  * Resume execution.
3609677Slinton  */
3619677Slinton 
36211867Slinton public resume(signo)
36311867Slinton int signo;
3649677Slinton {
3659677Slinton     register Process p;
3669677Slinton 
3679677Slinton     p = process;
3689677Slinton     if (traceexec) {
3699677Slinton 	printf("execution resumes at pc 0x%x\n", process->reg[PROGCTR]);
3709677Slinton 	fflush(stdout);
3719677Slinton     }
37211867Slinton     pcont(p, signo);
3739677Slinton     pc = process->reg[PROGCTR];
3749677Slinton     if (traceexec) {
3759677Slinton 	printf("execution stops at pc 0x%x on sig %d\n",
3769677Slinton 	    process->reg[PROGCTR], p->signo);
3779677Slinton 	fflush(stdout);
3789677Slinton     }
37911832Slinton     if (p->status != STOPPED) {
38011867Slinton 	if (p->signo != 0) {
38111867Slinton 	    error("program terminated by signal %d", p->signo);
382*14757Slinton 	} else if (not runfirst) {
38311867Slinton 	    error("program unexpectedly exited with %d", p->exitval);
38411867Slinton 	}
38511832Slinton     }
3869677Slinton }
3879677Slinton 
3889677Slinton /*
3899677Slinton  * Continue execution up to the next source line.
3909677Slinton  *
3919677Slinton  * There are two ways to define the next source line depending on what
3929677Slinton  * is desired when a procedure or function call is encountered.  Step
3939677Slinton  * stops at the beginning of the procedure or call; next skips over it.
3949677Slinton  */
3959677Slinton 
3969677Slinton /*
3979677Slinton  * Stepc is what is called when the step command is given.
3989677Slinton  * It has to play with the "isstopped" information.
3999677Slinton  */
4009677Slinton 
4019677Slinton public stepc()
4029677Slinton {
4039677Slinton     if (not isstopped) {
4049677Slinton 	error("can't continue execution");
4059677Slinton     }
4069677Slinton     isstopped = false;
4079677Slinton     dostep(false);
4089677Slinton     isstopped = true;
4099677Slinton }
4109677Slinton 
4119677Slinton public next()
4129677Slinton {
4139677Slinton     if (not isstopped) {
4149677Slinton 	error("can't continue execution");
4159677Slinton     }
4169677Slinton     isstopped = false;
4179677Slinton     dostep(true);
4189677Slinton     isstopped = true;
4199677Slinton }
4209677Slinton 
42111867Slinton /*
42211867Slinton  * Single-step over the current machine instruction.
42311867Slinton  *
42411867Slinton  * If we're single-stepping by source line we want to step to the
42511867Slinton  * next source line.  Otherwise we're going to continue so there's
42611867Slinton  * no reason to do all the work necessary to single-step to the next
42711867Slinton  * source line.
42811867Slinton  */
42911867Slinton 
43011867Slinton private stepover()
4319677Slinton {
43211867Slinton     Boolean b;
43311867Slinton 
43411867Slinton     if (single_stepping) {
43511867Slinton 	dostep(false);
43611867Slinton     } else {
43711867Slinton 	b = inst_tracing;
43811867Slinton 	inst_tracing = true;
43911867Slinton 	dostep(false);
44011867Slinton 	inst_tracing = b;
44111867Slinton     }
4429677Slinton }
4439677Slinton 
4449677Slinton /*
4459677Slinton  * Resume execution up to the given address.  It is assumed that
4469677Slinton  * no breakpoints exist between the current address and the one
4479677Slinton  * we're stepping to.  This saves us from setting all the breakpoints.
4489677Slinton  */
4499677Slinton 
4509677Slinton public stepto(addr)
4519677Slinton Address addr;
4529677Slinton {
4539677Slinton     setbp(addr);
454*14757Slinton     resume(DEFSIG);
4559677Slinton     unsetbp(addr);
4569677Slinton     if (not isbperr()) {
4579677Slinton 	printstatus();
4589677Slinton     }
4599677Slinton }
4609677Slinton 
4619677Slinton /*
4629677Slinton  * Print the status of the process.
4639677Slinton  * This routine does not return.
4649677Slinton  */
4659677Slinton 
4669677Slinton public printstatus()
4679677Slinton {
46814395Slinton     int status;
46914395Slinton 
4709843Slinton     if (process->status == FINISHED) {
4719843Slinton 	exit(0);
4729843Slinton     } else {
4739843Slinton 	curfunc = whatblock(pc);
4749677Slinton 	getsrcpos();
4759843Slinton 	if (process->signo == SIGINT) {
4769843Slinton 	    isstopped = true;
4779843Slinton 	    printerror();
4789843Slinton 	} else if (isbperr() and isstopped) {
4799843Slinton 	    printf("stopped ");
48011172Slinton 	    printloc();
48111172Slinton 	    putchar('\n');
4829843Slinton 	    if (curline > 0) {
4839843Slinton 		printlines(curline, curline);
4849843Slinton 	    } else {
4859843Slinton 		printinst(pc, pc);
4869843Slinton 	    }
4879843Slinton 	    erecover();
4889677Slinton 	} else {
4899843Slinton 	    fixbps();
4909843Slinton 	    fixintr();
4919677Slinton 	    isstopped = true;
4929677Slinton 	    printerror();
4939677Slinton 	}
4949677Slinton     }
4959677Slinton }
4969677Slinton 
4979677Slinton /*
49811172Slinton  * Print out the current location in the debuggee.
49911172Slinton  */
50011172Slinton 
50111172Slinton public printloc()
50211172Slinton {
50311172Slinton     printf("in ");
50411172Slinton     printname(stdout, curfunc);
50511172Slinton     putchar(' ');
506*14757Slinton     if (curline > 0 and not useInstLoc) {
50711172Slinton 	printsrcpos();
50811172Slinton     } else {
509*14757Slinton 	useInstLoc = false;
510*14757Slinton 	curline = 0;
51111172Slinton 	printf("at 0x%x", pc);
51211172Slinton     }
51311172Slinton }
51411172Slinton 
51511172Slinton /*
5169677Slinton  * Some functions for testing the state of the process.
5179677Slinton  */
5189677Slinton 
5199677Slinton public Boolean notstarted(p)
5209677Slinton Process p;
5219677Slinton {
5229677Slinton     return (Boolean) (p->status == NOTSTARTED);
5239677Slinton }
5249677Slinton 
5259677Slinton public Boolean isfinished(p)
5269677Slinton Process p;
5279677Slinton {
5289677Slinton     return (Boolean) (p->status == FINISHED);
5299677Slinton }
5309677Slinton 
5319677Slinton /*
5329677Slinton  * Return the signal number which stopped the process.
5339677Slinton  */
5349677Slinton 
5359677Slinton public Integer errnum(p)
5369677Slinton Process p;
5379677Slinton {
5389677Slinton     return p->signo;
5399677Slinton }
5409677Slinton 
5419677Slinton /*
5429677Slinton  * Return the termination code of the process.
5439677Slinton  */
5449677Slinton 
5459677Slinton public Integer exitcode(p)
5469677Slinton Process p;
5479677Slinton {
5489677Slinton     return p->exitval;
5499677Slinton }
5509677Slinton 
5519677Slinton /*
5529677Slinton  * These routines are used to access the debuggee process from
5539677Slinton  * outside this module.
5549677Slinton  *
5559677Slinton  * They invoke "pio" which eventually leads to a call to "ptrace".
556*14757Slinton  * The system generates an I/O error when a ptrace fails.  During reads
557*14757Slinton  * these are ignored, during writes they are reported as an error, and
558*14757Slinton  * for anything else they cause a fatal error.
5599677Slinton  */
5609677Slinton 
5619677Slinton extern Intfunc *onsyserr();
5629677Slinton 
5639677Slinton private badaddr;
564*14757Slinton private read_err(), write_err();
5659677Slinton 
5669677Slinton /*
5679677Slinton  * Read from the process' instruction area.
5689677Slinton  */
5699677Slinton 
5709677Slinton public iread(buff, addr, nbytes)
5719677Slinton char *buff;
5729677Slinton Address addr;
5739677Slinton int nbytes;
5749677Slinton {
5759677Slinton     Intfunc *f;
5769677Slinton 
577*14757Slinton     f = onsyserr(EIO, read_err);
5789677Slinton     badaddr = addr;
5799677Slinton     if (coredump) {
5809677Slinton 	coredump_readtext(buff, addr, nbytes);
5819677Slinton     } else {
5829677Slinton 	pio(process, PREAD, TEXTSEG, buff, addr, nbytes);
5839677Slinton     }
5849677Slinton     onsyserr(EIO, f);
5859677Slinton }
5869677Slinton 
5879677Slinton /*
5889677Slinton  * Write to the process' instruction area, usually in order to set
5899677Slinton  * or unset a breakpoint.
5909677Slinton  */
5919677Slinton 
5929677Slinton public iwrite(buff, addr, nbytes)
5939677Slinton char *buff;
5949677Slinton Address addr;
5959677Slinton int nbytes;
5969677Slinton {
5979677Slinton     Intfunc *f;
5989677Slinton 
5999677Slinton     if (coredump) {
6009677Slinton 	error("no process to write to");
6019677Slinton     }
602*14757Slinton     f = onsyserr(EIO, write_err);
6039677Slinton     badaddr = addr;
6049677Slinton     pio(process, PWRITE, TEXTSEG, buff, addr, nbytes);
6059677Slinton     onsyserr(EIO, f);
6069677Slinton }
6079677Slinton 
6089677Slinton /*
6099677Slinton  * Read for the process' data area.
6109677Slinton  */
6119677Slinton 
6129677Slinton public dread(buff, addr, nbytes)
6139677Slinton char *buff;
6149677Slinton Address addr;
6159677Slinton int nbytes;
6169677Slinton {
6179677Slinton     Intfunc *f;
6189677Slinton 
619*14757Slinton     f = onsyserr(EIO, read_err);
6209677Slinton     badaddr = addr;
6219677Slinton     if (coredump) {
6229677Slinton 	coredump_readdata(buff, addr, nbytes);
6239677Slinton     } else {
6249677Slinton 	pio(process, PREAD, DATASEG, buff, addr, nbytes);
6259677Slinton     }
6269677Slinton     onsyserr(EIO, f);
6279677Slinton }
6289677Slinton 
6299677Slinton /*
6309677Slinton  * Write to the process' data area.
6319677Slinton  */
6329677Slinton 
6339677Slinton public dwrite(buff, addr, nbytes)
6349677Slinton char *buff;
6359677Slinton Address addr;
6369677Slinton int nbytes;
6379677Slinton {
6389677Slinton     Intfunc *f;
6399677Slinton 
6409677Slinton     if (coredump) {
6419677Slinton 	error("no process to write to");
6429677Slinton     }
643*14757Slinton     f = onsyserr(EIO, write_err);
6449677Slinton     badaddr = addr;
6459677Slinton     pio(process, PWRITE, DATASEG, buff, addr, nbytes);
6469677Slinton     onsyserr(EIO, f);
6479677Slinton }
6489677Slinton 
6499677Slinton /*
650*14757Slinton  * Trap for errors in reading or writing to a process.
651*14757Slinton  * The current approach is to "ignore" read errors and complain
652*14757Slinton  * bitterly about write errors.
6539677Slinton  */
6549677Slinton 
655*14757Slinton private read_err()
6569677Slinton {
65711560Slinton     /*
658*14757Slinton      * Ignore.
65911560Slinton      */
6609677Slinton }
6619677Slinton 
662*14757Slinton private write_err()
663*14757Slinton {
664*14757Slinton     error("can't write to process (address 0x%x)", badaddr);
665*14757Slinton }
666*14757Slinton 
6679677Slinton /*
6689677Slinton  * Ptrace interface.
6699677Slinton  */
6709677Slinton 
6719677Slinton /*
6729677Slinton  * This magic macro enables us to look at the process' registers
673*14757Slinton  * in its user structure.
6749677Slinton  */
6759677Slinton 
6769677Slinton #define regloc(reg)     (ctob(UPAGES) + ( sizeof(int) * (reg) ))
6779677Slinton 
6789677Slinton #define WMASK           (~(sizeof(Word) - 1))
6799677Slinton #define cachehash(addr) ((unsigned) ((addr >> 2) % CSIZE))
6809677Slinton 
6819677Slinton #define FIRSTSIG        SIGINT
6829677Slinton #define LASTSIG         SIGQUIT
6839677Slinton #define ischild(pid)    ((pid) == 0)
6849677Slinton #define traceme()       ptrace(0, 0, 0, 0)
6859677Slinton #define setrep(n)       (1 << ((n)-1))
6869677Slinton #define istraced(p)     (p->sigset&setrep(p->signo))
6879677Slinton 
6889677Slinton /*
6899677Slinton  * Ptrace options (specified in first argument).
6909677Slinton  */
6919677Slinton 
6929677Slinton #define UREAD   3       /* read from process's user structure */
6939677Slinton #define UWRITE  6       /* write to process's user structure */
6949677Slinton #define IREAD   1       /* read from process's instruction space */
6959677Slinton #define IWRITE  4       /* write to process's instruction space */
6969677Slinton #define DREAD   2       /* read from process's data space */
6979677Slinton #define DWRITE  5       /* write to process's data space */
6989677Slinton #define CONT    7       /* continue stopped process */
6999677Slinton #define SSTEP   9       /* continue for approximately one instruction */
7009677Slinton #define PKILL   8       /* terminate the process */
7019677Slinton 
7029677Slinton /*
7039677Slinton  * Start up a new process by forking and exec-ing the
7049677Slinton  * given argument list, returning when the process is loaded
7059677Slinton  * and ready to execute.  The PROCESS information (pointed to
7069677Slinton  * by the first argument) is appropriately filled.
7079677Slinton  *
7089677Slinton  * If the given PROCESS structure is associated with an already running
7099677Slinton  * process, we terminate it.
7109677Slinton  */
7119677Slinton 
7129677Slinton /* VARARGS2 */
7139677Slinton private pstart(p, argv, infile, outfile)
7149677Slinton Process p;
7159677Slinton String argv[];
7169677Slinton String infile;
7179677Slinton String outfile;
7189677Slinton {
7199677Slinton     int status;
72014395Slinton     Fileid in, out;
7219677Slinton 
722*14757Slinton     if (p->pid != 0) {			/* child already running? */
723*14757Slinton 	ptrace(PKILL, p->pid, 0, 0);	/* ... kill it! */
72414395Slinton 	pwait(p->pid, &status);		/* wait for it to exit */
72514395Slinton 	unptraced(p->pid);
7269677Slinton     }
7279677Slinton     psigtrace(p, SIGTRAP, true);
72814395Slinton     p->pid = vfork();
72914395Slinton     if (p->pid == -1) {
7309677Slinton 	panic("can't fork");
7319677Slinton     }
7329677Slinton     if (ischild(p->pid)) {
7339677Slinton 	traceme();
7349677Slinton 	if (infile != nil) {
73511172Slinton 	    in = open(infile, 0);
73611172Slinton 	    if (in == -1) {
73711172Slinton 		write(2, "can't read ", 11);
73811172Slinton 		write(2, infile, strlen(infile));
73911172Slinton 		write(2, "\n", 1);
74011172Slinton 		_exit(1);
7419677Slinton 	    }
74211172Slinton 	    fswap(0, in);
7439677Slinton 	}
7449677Slinton 	if (outfile != nil) {
74511172Slinton 	    out = creat(outfile, 0666);
74611172Slinton 	    if (out == -1) {
74711172Slinton 		write(2, "can't write ", 12);
74811172Slinton 		write(2, outfile, strlen(outfile));
74911172Slinton 		write(2, "\n", 1);
75011172Slinton 		_exit(1);
7519677Slinton 	    }
75211172Slinton 	    fswap(1, out);
7539677Slinton 	}
75411832Slinton 	execv(argv[0], argv);
75511172Slinton 	write(2, "can't exec ", 11);
75611172Slinton 	write(2, argv[0], strlen(argv[0]));
75711172Slinton 	write(2, "\n", 1);
75811172Slinton 	_exit(1);
7599677Slinton     }
7609677Slinton     pwait(p->pid, &status);
7619677Slinton     getinfo(p, status);
7629677Slinton     if (p->status != STOPPED) {
7639677Slinton 	error("program could not begin execution");
7649677Slinton     }
76514395Slinton     ptraced(p->pid);
7669677Slinton }
7679677Slinton 
7689677Slinton /*
76911867Slinton  * Continue a stopped process.  The first argument points to a Process
77011867Slinton  * structure.  Before the process is restarted it's user area is modified
77111867Slinton  * according to the values in the structure.  When this routine finishes,
7729677Slinton  * the structure has the new values from the process's user area.
7739677Slinton  *
7749677Slinton  * Pcont terminates when the process stops with a signal pending that
7759677Slinton  * is being traced (via psigtrace), or when the process terminates.
7769677Slinton  */
7779677Slinton 
77811867Slinton private pcont(p, signo)
7799677Slinton Process p;
78011867Slinton int signo;
7819677Slinton {
7829677Slinton     int status;
7839677Slinton 
7849677Slinton     if (p->pid == 0) {
7859677Slinton 	error("program not active");
7869677Slinton     }
7879677Slinton     do {
78811867Slinton 	setinfo(p, signo);
7899677Slinton 	sigs_off();
7909677Slinton 	if (ptrace(CONT, p->pid, p->reg[PROGCTR], p->signo) < 0) {
79114395Slinton 	    panic("error %d trying to continue process", errno);
7929677Slinton 	}
7939677Slinton 	pwait(p->pid, &status);
7949677Slinton 	sigs_on();
7959677Slinton 	getinfo(p, status);
7969677Slinton     } while (p->status == STOPPED and not istraced(p));
7979677Slinton }
7989677Slinton 
7999677Slinton /*
8009677Slinton  * Single step as best ptrace can.
8019677Slinton  */
8029677Slinton 
8039677Slinton public pstep(p)
8049677Slinton Process p;
8059677Slinton {
8069677Slinton     int status;
8079677Slinton 
808*14757Slinton     setinfo(p, DEFSIG);
8099677Slinton     sigs_off();
8109677Slinton     ptrace(SSTEP, p->pid, p->reg[PROGCTR], p->signo);
8119677Slinton     pwait(p->pid, &status);
8129677Slinton     sigs_on();
8139677Slinton     getinfo(p, status);
8149677Slinton }
8159677Slinton 
8169677Slinton /*
8179677Slinton  * Return from execution when the given signal is pending.
8189677Slinton  */
8199677Slinton 
8209677Slinton public psigtrace(p, sig, sw)
8219677Slinton Process p;
8229677Slinton int sig;
8239677Slinton Boolean sw;
8249677Slinton {
8259677Slinton     if (sw) {
8269677Slinton 	p->sigset |= setrep(sig);
8279677Slinton     } else {
8289677Slinton 	p->sigset &= ~setrep(sig);
8299677Slinton     }
8309677Slinton }
8319677Slinton 
8329677Slinton /*
8339677Slinton  * Don't catch any signals.
8349677Slinton  * Particularly useful when letting a process finish uninhibited.
8359677Slinton  */
8369677Slinton 
8379677Slinton public unsetsigtraces(p)
8389677Slinton Process p;
8399677Slinton {
8409677Slinton     p->sigset = 0;
8419677Slinton }
8429677Slinton 
8439677Slinton /*
8449677Slinton  * Turn off attention to signals not being caught.
8459677Slinton  */
8469677Slinton 
8479677Slinton private Intfunc *sigfunc[NSIG];
8489677Slinton 
8499677Slinton private sigs_off()
8509677Slinton {
8519677Slinton     register int i;
8529677Slinton 
8539677Slinton     for (i = FIRSTSIG; i < LASTSIG; i++) {
8549677Slinton 	if (i != SIGKILL) {
8559677Slinton 	    sigfunc[i] = signal(i, SIG_IGN);
8569677Slinton 	}
8579677Slinton     }
8589677Slinton }
8599677Slinton 
8609677Slinton /*
8619677Slinton  * Turn back on attention to signals.
8629677Slinton  */
8639677Slinton 
8649677Slinton private sigs_on()
8659677Slinton {
8669677Slinton     register int i;
8679677Slinton 
8689677Slinton     for (i = FIRSTSIG; i < LASTSIG; i++) {
8699677Slinton 	if (i != SIGKILL) {
8709677Slinton 	    signal(i, sigfunc[i]);
8719677Slinton 	}
8729677Slinton     }
8739677Slinton }
8749677Slinton 
8759677Slinton /*
8769677Slinton  * Get process information from user area.
8779677Slinton  */
8789677Slinton 
8799677Slinton private int rloc[] ={
8809677Slinton     R0, R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, AP, FP, SP, PC
8819677Slinton };
8829677Slinton 
8839677Slinton private getinfo(p, status)
8849677Slinton register Process p;
8859677Slinton register int status;
8869677Slinton {
8879677Slinton     register int i;
8889677Slinton 
8899677Slinton     p->signo = (status&0177);
8909677Slinton     p->exitval = ((status >> 8)&0377);
8919677Slinton     if (p->signo != STOPPED) {
8929677Slinton 	p->status = FINISHED;
893*14757Slinton 	p->pid = 0;
8949677Slinton     } else {
8959677Slinton 	p->status = p->signo;
8969677Slinton 	p->signo = p->exitval;
8979677Slinton 	p->exitval = 0;
8989677Slinton 	p->mask = ptrace(UREAD, p->pid, regloc(PS), 0);
8999677Slinton 	for (i = 0; i < NREG; i++) {
9009677Slinton 	    p->reg[i] = ptrace(UREAD, p->pid, regloc(rloc[i]), 0);
9019677Slinton 	    p->oreg[i] = p->reg[i];
9029677Slinton 	}
90311768Slinton 	savetty(stdout, &(p->ttyinfo));
9049677Slinton     }
9059677Slinton }
9069677Slinton 
9079677Slinton /*
9089677Slinton  * Set process's user area information from given process structure.
9099677Slinton  */
9109677Slinton 
91111867Slinton private setinfo(p, signo)
9129677Slinton register Process p;
91311867Slinton int signo;
9149677Slinton {
9159677Slinton     register int i;
9169677Slinton     register int r;
9179677Slinton 
918*14757Slinton     if (signo == DEFSIG) {
919*14757Slinton 	if (istraced(p)) {
920*14757Slinton 	    p->signo = 0;
921*14757Slinton 	}
922*14757Slinton     } else {
92311867Slinton 	p->signo = signo;
9249677Slinton     }
9259677Slinton     for (i = 0; i < NREG; i++) {
9269677Slinton 	if ((r = p->reg[i]) != p->oreg[i]) {
9279677Slinton 	    ptrace(UWRITE, p->pid, regloc(rloc[i]), r);
9289677Slinton 	}
9299677Slinton     }
93011768Slinton     restoretty(stdout, &(p->ttyinfo));
9319677Slinton }
9329677Slinton 
9339677Slinton /*
9349677Slinton  * Structure for reading and writing by words, but dealing with bytes.
9359677Slinton  */
9369677Slinton 
9379677Slinton typedef union {
9389677Slinton     Word pword;
9399677Slinton     Byte pbyte[sizeof(Word)];
9409677Slinton } Pword;
9419677Slinton 
9429677Slinton /*
9439677Slinton  * Read (write) from (to) the process' address space.
9449677Slinton  * We must deal with ptrace's inability to look anywhere other
9459677Slinton  * than at a word boundary.
9469677Slinton  */
9479677Slinton 
9489677Slinton private Word fetch();
9499677Slinton private store();
9509677Slinton 
9519677Slinton private pio(p, op, seg, buff, addr, nbytes)
9529677Slinton Process p;
9539677Slinton PioOp op;
9549677Slinton PioSeg seg;
9559677Slinton char *buff;
9569677Slinton Address addr;
9579677Slinton int nbytes;
9589677Slinton {
9599677Slinton     register int i;
9609677Slinton     register Address newaddr;
9619677Slinton     register char *cp;
9629677Slinton     char *bufend;
9639677Slinton     Pword w;
9649677Slinton     Address wordaddr;
9659677Slinton     int byteoff;
9669677Slinton 
9679677Slinton     if (p->status != STOPPED) {
9689677Slinton 	error("program is not active");
9699677Slinton     }
9709677Slinton     cp = buff;
9719677Slinton     newaddr = addr;
9729677Slinton     wordaddr = (newaddr&WMASK);
9739677Slinton     if (wordaddr != newaddr) {
9749677Slinton 	w.pword = fetch(p, seg, wordaddr);
9759677Slinton 	for (i = newaddr - wordaddr; i < sizeof(Word) and nbytes > 0; i++) {
9769677Slinton 	    if (op == PREAD) {
9779677Slinton 		*cp++ = w.pbyte[i];
9789677Slinton 	    } else {
9799677Slinton 		w.pbyte[i] = *cp++;
9809677Slinton 	    }
9819677Slinton 	    nbytes--;
9829677Slinton 	}
9839677Slinton 	if (op == PWRITE) {
9849677Slinton 	    store(p, seg, wordaddr, w.pword);
9859677Slinton 	}
9869677Slinton 	newaddr = wordaddr + sizeof(Word);
9879677Slinton     }
9889677Slinton     byteoff = (nbytes&(~WMASK));
9899677Slinton     nbytes -= byteoff;
9909677Slinton     bufend = cp + nbytes;
9919677Slinton     while (cp < bufend) {
9929677Slinton 	if (op == PREAD) {
9939677Slinton 	    *((Word *) cp) = fetch(p, seg, newaddr);
9949677Slinton 	} else {
9959677Slinton 	    store(p, seg, newaddr, *((Word *) cp));
9969677Slinton 	}
9979677Slinton 	cp += sizeof(Word);
9989677Slinton 	newaddr += sizeof(Word);
9999677Slinton     }
10009677Slinton     if (byteoff > 0) {
10019677Slinton 	w.pword = fetch(p, seg, newaddr);
10029677Slinton 	for (i = 0; i < byteoff; i++) {
10039677Slinton 	    if (op == PREAD) {
10049677Slinton 		*cp++ = w.pbyte[i];
10059677Slinton 	    } else {
10069677Slinton 		w.pbyte[i] = *cp++;
10079677Slinton 	    }
10089677Slinton 	}
10099677Slinton 	if (op == PWRITE) {
10109677Slinton 	    store(p, seg, newaddr, w.pword);
10119677Slinton 	}
10129677Slinton     }
10139677Slinton }
10149677Slinton 
10159677Slinton /*
10169677Slinton  * Get a word from a process at the given address.
10179677Slinton  * The address is assumed to be on a word boundary.
10189677Slinton  *
10199677Slinton  * A simple cache scheme is used to avoid redundant ptrace calls
10209677Slinton  * to the instruction space since it is assumed to be pure.
10219677Slinton  *
10229677Slinton  * It is necessary to use a write-through scheme so that
10239677Slinton  * breakpoints right next to each other don't interfere.
10249677Slinton  */
10259677Slinton 
10269677Slinton private Integer nfetchs, nreads, nwrites;
10279677Slinton 
10289677Slinton private Word fetch(p, seg, addr)
10299677Slinton Process p;
10309677Slinton PioSeg seg;
10319677Slinton register int addr;
10329677Slinton {
10339677Slinton     register CacheWord *wp;
10349677Slinton     register Word w;
10359677Slinton 
10369677Slinton     switch (seg) {
10379677Slinton 	case TEXTSEG:
10389677Slinton 	    ++nfetchs;
10399677Slinton 	    wp = &p->word[cachehash(addr)];
10409677Slinton 	    if (addr == 0 or wp->addr != addr) {
10419677Slinton 		++nreads;
10429677Slinton 		w = ptrace(IREAD, p->pid, addr, 0);
10439677Slinton 		wp->addr = addr;
10449677Slinton 		wp->val = w;
10459677Slinton 	    } else {
10469677Slinton 		w = wp->val;
10479677Slinton 	    }
10489677Slinton 	    break;
10499677Slinton 
10509677Slinton 	case DATASEG:
10519677Slinton 	    w = ptrace(DREAD, p->pid, addr, 0);
10529677Slinton 	    break;
10539677Slinton 
10549677Slinton 	default:
10559677Slinton 	    panic("fetch: bad seg %d", seg);
10569677Slinton 	    /* NOTREACHED */
10579677Slinton     }
10589677Slinton     return w;
10599677Slinton }
10609677Slinton 
10619677Slinton /*
10629677Slinton  * Put a word into the process' address space at the given address.
10639677Slinton  * The address is assumed to be on a word boundary.
10649677Slinton  */
10659677Slinton 
10669677Slinton private store(p, seg, addr, data)
10679677Slinton Process p;
10689677Slinton PioSeg seg;
10699677Slinton int addr;
10709677Slinton Word data;
10719677Slinton {
10729677Slinton     register CacheWord *wp;
10739677Slinton 
10749677Slinton     switch (seg) {
10759677Slinton 	case TEXTSEG:
10769677Slinton 	    ++nwrites;
10779677Slinton 	    wp = &p->word[cachehash(addr)];
10789677Slinton 	    wp->addr = addr;
10799677Slinton 	    wp->val = data;
10809677Slinton 	    ptrace(IWRITE, p->pid, addr, data);
10819677Slinton 	    break;
10829677Slinton 
10839677Slinton 	case DATASEG:
10849677Slinton 	    ptrace(DWRITE, p->pid, addr, data);
10859677Slinton 	    break;
10869677Slinton 
10879677Slinton 	default:
10889677Slinton 	    panic("store: bad seg %d", seg);
10899677Slinton 	    /* NOTREACHED */
10909677Slinton     }
10919677Slinton }
10929677Slinton 
10939677Slinton public printptraceinfo()
10949677Slinton {
10959677Slinton     printf("%d fetchs, %d reads, %d writes\n", nfetchs, nreads, nwrites);
10969677Slinton }
10979677Slinton 
10989677Slinton /*
10999677Slinton  * Swap file numbers so as to redirect standard input and output.
11009677Slinton  */
11019677Slinton 
11029677Slinton private fswap(oldfd, newfd)
11039677Slinton int oldfd;
11049677Slinton int newfd;
11059677Slinton {
11069677Slinton     if (oldfd != newfd) {
11079677Slinton 	close(oldfd);
11089677Slinton 	dup(newfd);
11099677Slinton 	close(newfd);
11109677Slinton     }
11119677Slinton }
1112