xref: /csrg-svn/old/dbx/process.c (revision 12484)
1 /* Copyright (c) 1982 Regents of the University of California */
2 
3 static char sccsid[] = "@(#)process.c 1.9 05/17/83";
4 
5 /*
6  * Process management.
7  *
8  * This module contains the routines to manage the execution and
9  * tracing of the debuggee process.
10  */
11 
12 #include "defs.h"
13 #include "process.h"
14 #include "machine.h"
15 #include "events.h"
16 #include "tree.h"
17 #include "operators.h"
18 #include "source.h"
19 #include "object.h"
20 #include "mappings.h"
21 #include "main.h"
22 #include "coredump.h"
23 #include <signal.h>
24 #include <errno.h>
25 #include <sys/param.h>
26 #include <machine/reg.h>
27 #include <sys/stat.h>
28 
29 #ifndef public
30 
31 typedef struct Process *Process;
32 
33 Process process;
34 
35 #include "machine.h"
36 
37 #endif
38 
39 #define NOTSTARTED 1
40 #define STOPPED 0177
41 #define FINISHED 0
42 
43 /*
44  * Cache-ing of instruction segment is done to reduce the number
45  * of system calls.
46  */
47 
48 #define CSIZE 1003       /* size of instruction cache */
49 
50 typedef struct {
51     Word addr;
52     Word val;
53 } CacheWord;
54 
55 /*
56  * This structure holds the information we need from the user structure.
57  */
58 
59 struct Process {
60     int pid;			/* process being traced */
61     int mask;			/* process status word */
62     Word reg[NREG];		/* process' registers */
63     Word oreg[NREG];		/* registers when process last stopped */
64     short status;		/* either STOPPED or FINISHED */
65     short signo;		/* signal that stopped process */
66     int exitval;		/* return value from exit() */
67     long sigset;		/* bit array of traced signals */
68     CacheWord word[CSIZE];	/* text segment cache */
69     Ttyinfo ttyinfo;		/* process' terminal characteristics */
70 };
71 
72 /*
73  * These definitions are for the arguments to "pio".
74  */
75 
76 typedef enum { PREAD, PWRITE } PioOp;
77 typedef enum { TEXTSEG, DATASEG } PioSeg;
78 
79 private struct Process pbuf;
80 
81 #define MAXNCMDARGS 10         /* maximum number of arguments to RUN */
82 
83 private Boolean just_started;
84 private int argc;
85 private String argv[MAXNCMDARGS];
86 private String infile, outfile;
87 
88 /*
89  * Initialize process information.
90  */
91 
92 public process_init()
93 {
94     register Integer i;
95     Char buf[10];
96 
97     process = &pbuf;
98     process->status = (coredump) ? STOPPED : NOTSTARTED;
99     setsigtrace();
100     for (i = 0; i < NREG; i++) {
101 	sprintf(buf, "$r%d", i);
102 	defregname(identname(buf, false), i);
103     }
104     defregname(identname("$ap", true), ARGP);
105     defregname(identname("$fp", true), FRP);
106     defregname(identname("$sp", true), STKP);
107     defregname(identname("$pc", true), PROGCTR);
108     if (coredump) {
109 	coredump_readin(process->mask, process->reg, process->signo);
110 	pc = process->reg[PROGCTR];
111 	getsrcpos();
112     }
113     arginit();
114 }
115 
116 /*
117  * Routines to get at process information from outside this module.
118  */
119 
120 public Word reg(n)
121 Integer n;
122 {
123     register Word w;
124 
125     if (n == NREG) {
126 	w = process->mask;
127     } else {
128 	w = process->reg[n];
129     }
130     return w;
131 }
132 
133 public setreg(n, w)
134 Integer n;
135 Word w;
136 {
137     process->reg[n] = w;
138 }
139 
140 /*
141  * Begin execution.
142  *
143  * We set a breakpoint at the end of the code so that the
144  * process data doesn't disappear after the program terminates.
145  */
146 
147 private Boolean remade();
148 
149 public start(argv, infile, outfile)
150 String argv[];
151 String infile, outfile;
152 {
153     String pargv[4];
154     Node cond;
155 
156     if (coredump) {
157 	coredump = false;
158 	fclose(corefile);
159 	coredump_close();
160     }
161     if (argv == nil) {
162 	argv = pargv;
163 	pargv[0] = objname;
164 	pargv[1] = nil;
165     } else {
166 	argv[argc] = nil;
167     }
168     if (remade(objname)) {
169 	reinit(argv, infile, outfile);
170     }
171     pstart(process, argv, infile, outfile);
172     if (process->status == STOPPED) {
173 	pc = 0;
174 	curfunc = program;
175 	if (objsize != 0) {
176 	    cond = build(O_EQ, build(O_SYM, pcsym), build(O_LCON, lastaddr()));
177 	    event_once(cond, buildcmdlist(build(O_ENDX)));
178 	}
179     }
180 }
181 
182 /*
183  * Check to see if the object file has changed since the symbolic
184  * information last was read.
185  */
186 
187 private time_t modtime;
188 
189 private Boolean remade(filename)
190 String filename;
191 {
192     struct stat s;
193     Boolean b;
194 
195     stat(filename, &s);
196     b = (Boolean) (modtime != 0 and modtime < s.st_mtime);
197     modtime = s.st_mtime;
198     return b;
199 }
200 
201 /*
202  * Set up what signals we want to trace.
203  */
204 
205 private setsigtrace()
206 {
207     register Integer i;
208     register Process p;
209 
210     p = process;
211     for (i = 1; i <= NSIG; i++) {
212 	psigtrace(p, i, true);
213     }
214     psigtrace(p, SIGHUP, false);
215     psigtrace(p, SIGKILL, false);
216     psigtrace(p, SIGALRM, false);
217     psigtrace(p, SIGTSTP, false);
218     psigtrace(p, SIGCONT, false);
219     psigtrace(p, SIGCHLD, false);
220 }
221 
222 /*
223  * Initialize the argument list.
224  */
225 
226 public arginit()
227 {
228     infile = nil;
229     outfile = nil;
230     argv[0] = objname;
231     argc = 1;
232 }
233 
234 /*
235  * Add an argument to the list for the debuggee.
236  */
237 
238 public newarg(arg)
239 String arg;
240 {
241     if (argc >= MAXNCMDARGS) {
242 	error("too many arguments");
243     }
244     argv[argc++] = arg;
245 }
246 
247 /*
248  * Set the standard input for the debuggee.
249  */
250 
251 public inarg(filename)
252 String filename;
253 {
254     if (infile != nil) {
255 	error("multiple input redirects");
256     }
257     infile = filename;
258 }
259 
260 /*
261  * Set the standard output for the debuggee.
262  * Probably should check to avoid overwriting an existing file.
263  */
264 
265 public outarg(filename)
266 String filename;
267 {
268     if (outfile != nil) {
269 	error("multiple output redirect");
270     }
271     outfile = filename;
272 }
273 
274 /*
275  * Start debuggee executing.
276  */
277 
278 public run()
279 {
280     process->status = STOPPED;
281     fixbps();
282     curline = 0;
283     start(argv, infile, outfile);
284     just_started = true;
285     isstopped = false;
286     cont();
287 }
288 
289 /*
290  * Continue execution wherever we left off.
291  *
292  * Note that this routine never returns.  Eventually bpact() will fail
293  * and we'll call printstatus or step will call it.
294  */
295 
296 typedef int Intfunc();
297 
298 private Intfunc *dbintr;
299 private intr();
300 
301 #define succeeds    == true
302 #define fails       == false
303 
304 public cont(signo)
305 int signo;
306 {
307     dbintr = signal(SIGINT, intr);
308     if (just_started) {
309 	just_started = false;
310     } else {
311 	if (not isstopped) {
312 	    error("can't continue execution");
313 	}
314 	isstopped = false;
315 	stepover();
316     }
317     for (;;) {
318 	if (single_stepping) {
319 	    printnews();
320 	} else {
321 	    setallbps();
322 	    resume(signo);
323 	    unsetallbps();
324 	    if (bpact() fails) {
325 		printstatus();
326 	    }
327 	}
328 	stepover();
329     }
330     /* NOTREACHED */
331 }
332 
333 /*
334  * This routine is called if we get an interrupt while "running" px
335  * but actually in the debugger.  Could happen, for example, while
336  * processing breakpoints.
337  *
338  * We basically just want to keep going; the assumption is
339  * that when the process resumes it will get the interrupt
340  * which will then be handled.
341  */
342 
343 private intr()
344 {
345     signal(SIGINT, intr);
346 }
347 
348 public fixintr()
349 {
350     signal(SIGINT, dbintr);
351 }
352 
353 /*
354  * Resume execution.
355  */
356 
357 public resume(signo)
358 int signo;
359 {
360     register Process p;
361 
362     p = process;
363     if (traceexec) {
364 	printf("execution resumes at pc 0x%x\n", process->reg[PROGCTR]);
365 	fflush(stdout);
366     }
367     pcont(p, signo);
368     pc = process->reg[PROGCTR];
369     if (traceexec) {
370 	printf("execution stops at pc 0x%x on sig %d\n",
371 	    process->reg[PROGCTR], p->signo);
372 	fflush(stdout);
373     }
374     if (p->status != STOPPED) {
375 	if (p->signo != 0) {
376 	    error("program terminated by signal %d", p->signo);
377 	} else {
378 	    error("program unexpectedly exited with %d", p->exitval);
379 	}
380     }
381 }
382 
383 /*
384  * Continue execution up to the next source line.
385  *
386  * There are two ways to define the next source line depending on what
387  * is desired when a procedure or function call is encountered.  Step
388  * stops at the beginning of the procedure or call; next skips over it.
389  */
390 
391 /*
392  * Stepc is what is called when the step command is given.
393  * It has to play with the "isstopped" information.
394  */
395 
396 public stepc()
397 {
398     if (not isstopped) {
399 	error("can't continue execution");
400     }
401     isstopped = false;
402     dostep(false);
403     isstopped = true;
404 }
405 
406 public next()
407 {
408     if (not isstopped) {
409 	error("can't continue execution");
410     }
411     isstopped = false;
412     dostep(true);
413     isstopped = true;
414 }
415 
416 /*
417  * Single-step over the current machine instruction.
418  *
419  * If we're single-stepping by source line we want to step to the
420  * next source line.  Otherwise we're going to continue so there's
421  * no reason to do all the work necessary to single-step to the next
422  * source line.
423  */
424 
425 private stepover()
426 {
427     Boolean b;
428 
429     if (single_stepping) {
430 	dostep(false);
431     } else {
432 	b = inst_tracing;
433 	inst_tracing = true;
434 	dostep(false);
435 	inst_tracing = b;
436     }
437 }
438 
439 /*
440  * Resume execution up to the given address.  It is assumed that
441  * no breakpoints exist between the current address and the one
442  * we're stepping to.  This saves us from setting all the breakpoints.
443  */
444 
445 public stepto(addr)
446 Address addr;
447 {
448     setbp(addr);
449     resume(0);
450     unsetbp(addr);
451     if (not isbperr()) {
452 	printstatus();
453     }
454 }
455 
456 /*
457  * Print the status of the process.
458  * This routine does not return.
459  */
460 
461 public printstatus()
462 {
463     if (process->status == FINISHED) {
464 	exit(0);
465     } else {
466 	curfunc = whatblock(pc);
467 	getsrcpos();
468 	if (process->signo == SIGINT) {
469 	    isstopped = true;
470 	    printerror();
471 	} else if (isbperr() and isstopped) {
472 	    printf("stopped ");
473 	    printloc();
474 	    putchar('\n');
475 	    if (curline > 0) {
476 		printlines(curline, curline);
477 	    } else {
478 		printinst(pc, pc);
479 	    }
480 	    erecover();
481 	} else {
482 	    fixbps();
483 	    fixintr();
484 	    isstopped = true;
485 	    printerror();
486 	}
487     }
488 }
489 
490 /*
491  * Print out the current location in the debuggee.
492  */
493 
494 public printloc()
495 {
496     printf("in ");
497     printname(stdout, curfunc);
498     putchar(' ');
499     if (curline > 0) {
500 	printsrcpos();
501     } else {
502 	printf("at 0x%x", pc);
503     }
504 }
505 
506 /*
507  * Some functions for testing the state of the process.
508  */
509 
510 public Boolean notstarted(p)
511 Process p;
512 {
513     return (Boolean) (p->status == NOTSTARTED);
514 }
515 
516 public Boolean isfinished(p)
517 Process p;
518 {
519     return (Boolean) (p->status == FINISHED);
520 }
521 
522 /*
523  * Return the signal number which stopped the process.
524  */
525 
526 public Integer errnum(p)
527 Process p;
528 {
529     return p->signo;
530 }
531 
532 /*
533  * Return the termination code of the process.
534  */
535 
536 public Integer exitcode(p)
537 Process p;
538 {
539     return p->exitval;
540 }
541 
542 /*
543  * These routines are used to access the debuggee process from
544  * outside this module.
545  *
546  * They invoke "pio" which eventually leads to a call to "ptrace".
547  * The system generates an I/O error when a ptrace fails, we assume
548  * during a read/write to the process that such an error is due to
549  * a misguided address and ignore it.
550  */
551 
552 extern Intfunc *onsyserr();
553 
554 private badaddr;
555 private rwerr();
556 
557 /*
558  * Read from the process' instruction area.
559  */
560 
561 public iread(buff, addr, nbytes)
562 char *buff;
563 Address addr;
564 int nbytes;
565 {
566     Intfunc *f;
567 
568     f = onsyserr(EIO, rwerr);
569     badaddr = addr;
570     if (coredump) {
571 	coredump_readtext(buff, addr, nbytes);
572     } else {
573 	pio(process, PREAD, TEXTSEG, buff, addr, nbytes);
574     }
575     onsyserr(EIO, f);
576 }
577 
578 /*
579  * Write to the process' instruction area, usually in order to set
580  * or unset a breakpoint.
581  */
582 
583 public iwrite(buff, addr, nbytes)
584 char *buff;
585 Address addr;
586 int nbytes;
587 {
588     Intfunc *f;
589 
590     if (coredump) {
591 	error("no process to write to");
592     }
593     f = onsyserr(EIO, rwerr);
594     badaddr = addr;
595     pio(process, PWRITE, TEXTSEG, buff, addr, nbytes);
596     onsyserr(EIO, f);
597 }
598 
599 /*
600  * Read for the process' data area.
601  */
602 
603 public dread(buff, addr, nbytes)
604 char *buff;
605 Address addr;
606 int nbytes;
607 {
608     Intfunc *f;
609 
610     f = onsyserr(EIO, rwerr);
611     badaddr = addr;
612     if (coredump) {
613 	coredump_readdata(buff, addr, nbytes);
614     } else {
615 	pio(process, PREAD, DATASEG, buff, addr, nbytes);
616     }
617     onsyserr(EIO, f);
618 }
619 
620 /*
621  * Write to the process' data area.
622  */
623 
624 public dwrite(buff, addr, nbytes)
625 char *buff;
626 Address addr;
627 int nbytes;
628 {
629     Intfunc *f;
630 
631     if (coredump) {
632 	error("no process to write to");
633     }
634     f = onsyserr(EIO, rwerr);
635     badaddr = addr;
636     pio(process, PWRITE, DATASEG, buff, addr, nbytes);
637     onsyserr(EIO, f);
638 }
639 
640 /*
641  * Error handler.
642  */
643 
644 private rwerr()
645 {
646     /*
647      * Current response is to ignore the error and let the result
648      * (-1) ripple back up to the process.
649      *
650     error("bad read/write process address 0x%x", badaddr);
651      */
652 }
653 
654 /*
655  * Ptrace interface.
656  */
657 
658 /*
659  * This magic macro enables us to look at the process' registers
660  * in its user structure.  Very gross.
661  */
662 
663 #define regloc(reg)     (ctob(UPAGES) + ( sizeof(int) * (reg) ))
664 
665 #define WMASK           (~(sizeof(Word) - 1))
666 #define cachehash(addr) ((unsigned) ((addr >> 2) % CSIZE))
667 
668 #define FIRSTSIG        SIGINT
669 #define LASTSIG         SIGQUIT
670 #define ischild(pid)    ((pid) == 0)
671 #define traceme()       ptrace(0, 0, 0, 0)
672 #define setrep(n)       (1 << ((n)-1))
673 #define istraced(p)     (p->sigset&setrep(p->signo))
674 
675 /*
676  * Ptrace options (specified in first argument).
677  */
678 
679 #define UREAD   3       /* read from process's user structure */
680 #define UWRITE  6       /* write to process's user structure */
681 #define IREAD   1       /* read from process's instruction space */
682 #define IWRITE  4       /* write to process's instruction space */
683 #define DREAD   2       /* read from process's data space */
684 #define DWRITE  5       /* write to process's data space */
685 #define CONT    7       /* continue stopped process */
686 #define SSTEP   9       /* continue for approximately one instruction */
687 #define PKILL   8       /* terminate the process */
688 
689 /*
690  * Start up a new process by forking and exec-ing the
691  * given argument list, returning when the process is loaded
692  * and ready to execute.  The PROCESS information (pointed to
693  * by the first argument) is appropriately filled.
694  *
695  * If the given PROCESS structure is associated with an already running
696  * process, we terminate it.
697  */
698 
699 /* VARARGS2 */
700 private pstart(p, argv, infile, outfile)
701 Process p;
702 String argv[];
703 String infile;
704 String outfile;
705 {
706     int status;
707 
708     if (p->pid != 0) {          	/* child already running? */
709 	ptrace(PKILL, p->pid, 0, 0);    /* ... kill it! */
710     }
711     psigtrace(p, SIGTRAP, true);
712     if ((p->pid = vfork()) == -1) {
713 	panic("can't fork");
714     }
715     if (ischild(p->pid)) {
716 	Fileid in, out;
717 
718 	traceme();
719 	if (infile != nil) {
720 	    in = open(infile, 0);
721 	    if (in == -1) {
722 		write(2, "can't read ", 11);
723 		write(2, infile, strlen(infile));
724 		write(2, "\n", 1);
725 		_exit(1);
726 	    }
727 	    fswap(0, in);
728 	}
729 	if (outfile != nil) {
730 	    out = creat(outfile, 0666);
731 	    if (out == -1) {
732 		write(2, "can't write ", 12);
733 		write(2, outfile, strlen(outfile));
734 		write(2, "\n", 1);
735 		_exit(1);
736 	    }
737 	    fswap(1, out);
738 	}
739 	execv(argv[0], argv);
740 	write(2, "can't exec ", 11);
741 	write(2, argv[0], strlen(argv[0]));
742 	write(2, "\n", 1);
743 	_exit(1);
744     }
745     pwait(p->pid, &status);
746     getinfo(p, status);
747     if (p->status != STOPPED) {
748 	error("program could not begin execution");
749     }
750 }
751 
752 /*
753  * Continue a stopped process.  The first argument points to a Process
754  * structure.  Before the process is restarted it's user area is modified
755  * according to the values in the structure.  When this routine finishes,
756  * the structure has the new values from the process's user area.
757  *
758  * Pcont terminates when the process stops with a signal pending that
759  * is being traced (via psigtrace), or when the process terminates.
760  */
761 
762 private pcont(p, signo)
763 Process p;
764 int signo;
765 {
766     int status;
767 
768     if (p->pid == 0) {
769 	error("program not active");
770     }
771     do {
772 	setinfo(p, signo);
773 	sigs_off();
774 	if (ptrace(CONT, p->pid, p->reg[PROGCTR], p->signo) < 0) {
775 	    panic("can't continue process");
776 	}
777 	pwait(p->pid, &status);
778 	sigs_on();
779 	getinfo(p, status);
780     } while (p->status == STOPPED and not istraced(p));
781 }
782 
783 /*
784  * Single step as best ptrace can.
785  */
786 
787 public pstep(p)
788 Process p;
789 {
790     int status;
791 
792     setinfo(p, 0);
793     sigs_off();
794     ptrace(SSTEP, p->pid, p->reg[PROGCTR], p->signo);
795     pwait(p->pid, &status);
796     sigs_on();
797     getinfo(p, status);
798 }
799 
800 /*
801  * Return from execution when the given signal is pending.
802  */
803 
804 public psigtrace(p, sig, sw)
805 Process p;
806 int sig;
807 Boolean sw;
808 {
809     if (sw) {
810 	p->sigset |= setrep(sig);
811     } else {
812 	p->sigset &= ~setrep(sig);
813     }
814 }
815 
816 /*
817  * Don't catch any signals.
818  * Particularly useful when letting a process finish uninhibited.
819  */
820 
821 public unsetsigtraces(p)
822 Process p;
823 {
824     p->sigset = 0;
825 }
826 
827 /*
828  * Turn off attention to signals not being caught.
829  */
830 
831 private Intfunc *sigfunc[NSIG];
832 
833 private sigs_off()
834 {
835     register int i;
836 
837     for (i = FIRSTSIG; i < LASTSIG; i++) {
838 	if (i != SIGKILL) {
839 	    sigfunc[i] = signal(i, SIG_IGN);
840 	}
841     }
842 }
843 
844 /*
845  * Turn back on attention to signals.
846  */
847 
848 private sigs_on()
849 {
850     register int i;
851 
852     for (i = FIRSTSIG; i < LASTSIG; i++) {
853 	if (i != SIGKILL) {
854 	    signal(i, sigfunc[i]);
855 	}
856     }
857 }
858 
859 /*
860  * Get process information from user area.
861  */
862 
863 private int rloc[] ={
864     R0, R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, AP, FP, SP, PC
865 };
866 
867 private getinfo(p, status)
868 register Process p;
869 register int status;
870 {
871     register int i;
872 
873     p->signo = (status&0177);
874     p->exitval = ((status >> 8)&0377);
875     if (p->signo != STOPPED) {
876 	p->status = FINISHED;
877     } else {
878 	p->status = p->signo;
879 	p->signo = p->exitval;
880 	p->exitval = 0;
881 	p->mask = ptrace(UREAD, p->pid, regloc(PS), 0);
882 	for (i = 0; i < NREG; i++) {
883 	    p->reg[i] = ptrace(UREAD, p->pid, regloc(rloc[i]), 0);
884 	    p->oreg[i] = p->reg[i];
885 	}
886 	savetty(stdout, &(p->ttyinfo));
887     }
888 }
889 
890 /*
891  * Set process's user area information from given process structure.
892  */
893 
894 private setinfo(p, signo)
895 register Process p;
896 int signo;
897 {
898     register int i;
899     register int r;
900 
901     if (istraced(p)) {
902 	p->signo = signo;
903     }
904     for (i = 0; i < NREG; i++) {
905 	if ((r = p->reg[i]) != p->oreg[i]) {
906 	    ptrace(UWRITE, p->pid, regloc(rloc[i]), r);
907 	}
908     }
909     restoretty(stdout, &(p->ttyinfo));
910 }
911 
912 /*
913  * Structure for reading and writing by words, but dealing with bytes.
914  */
915 
916 typedef union {
917     Word pword;
918     Byte pbyte[sizeof(Word)];
919 } Pword;
920 
921 /*
922  * Read (write) from (to) the process' address space.
923  * We must deal with ptrace's inability to look anywhere other
924  * than at a word boundary.
925  */
926 
927 private Word fetch();
928 private store();
929 
930 private pio(p, op, seg, buff, addr, nbytes)
931 Process p;
932 PioOp op;
933 PioSeg seg;
934 char *buff;
935 Address addr;
936 int nbytes;
937 {
938     register int i;
939     register Address newaddr;
940     register char *cp;
941     char *bufend;
942     Pword w;
943     Address wordaddr;
944     int byteoff;
945 
946     if (p->status != STOPPED) {
947 	error("program is not active");
948     }
949     cp = buff;
950     newaddr = addr;
951     wordaddr = (newaddr&WMASK);
952     if (wordaddr != newaddr) {
953 	w.pword = fetch(p, seg, wordaddr);
954 	for (i = newaddr - wordaddr; i < sizeof(Word) and nbytes > 0; i++) {
955 	    if (op == PREAD) {
956 		*cp++ = w.pbyte[i];
957 	    } else {
958 		w.pbyte[i] = *cp++;
959 	    }
960 	    nbytes--;
961 	}
962 	if (op == PWRITE) {
963 	    store(p, seg, wordaddr, w.pword);
964 	}
965 	newaddr = wordaddr + sizeof(Word);
966     }
967     byteoff = (nbytes&(~WMASK));
968     nbytes -= byteoff;
969     bufend = cp + nbytes;
970     while (cp < bufend) {
971 	if (op == PREAD) {
972 	    *((Word *) cp) = fetch(p, seg, newaddr);
973 	} else {
974 	    store(p, seg, newaddr, *((Word *) cp));
975 	}
976 	cp += sizeof(Word);
977 	newaddr += sizeof(Word);
978     }
979     if (byteoff > 0) {
980 	w.pword = fetch(p, seg, newaddr);
981 	for (i = 0; i < byteoff; i++) {
982 	    if (op == PREAD) {
983 		*cp++ = w.pbyte[i];
984 	    } else {
985 		w.pbyte[i] = *cp++;
986 	    }
987 	}
988 	if (op == PWRITE) {
989 	    store(p, seg, newaddr, w.pword);
990 	}
991     }
992 }
993 
994 /*
995  * Get a word from a process at the given address.
996  * The address is assumed to be on a word boundary.
997  *
998  * A simple cache scheme is used to avoid redundant ptrace calls
999  * to the instruction space since it is assumed to be pure.
1000  *
1001  * It is necessary to use a write-through scheme so that
1002  * breakpoints right next to each other don't interfere.
1003  */
1004 
1005 private Integer nfetchs, nreads, nwrites;
1006 
1007 private Word fetch(p, seg, addr)
1008 Process p;
1009 PioSeg seg;
1010 register int addr;
1011 {
1012     register CacheWord *wp;
1013     register Word w;
1014 
1015     switch (seg) {
1016 	case TEXTSEG:
1017 	    ++nfetchs;
1018 	    wp = &p->word[cachehash(addr)];
1019 	    if (addr == 0 or wp->addr != addr) {
1020 		++nreads;
1021 		w = ptrace(IREAD, p->pid, addr, 0);
1022 		wp->addr = addr;
1023 		wp->val = w;
1024 	    } else {
1025 		w = wp->val;
1026 	    }
1027 	    break;
1028 
1029 	case DATASEG:
1030 	    w = ptrace(DREAD, p->pid, addr, 0);
1031 	    break;
1032 
1033 	default:
1034 	    panic("fetch: bad seg %d", seg);
1035 	    /* NOTREACHED */
1036     }
1037     return w;
1038 }
1039 
1040 /*
1041  * Put a word into the process' address space at the given address.
1042  * The address is assumed to be on a word boundary.
1043  */
1044 
1045 private store(p, seg, addr, data)
1046 Process p;
1047 PioSeg seg;
1048 int addr;
1049 Word data;
1050 {
1051     register CacheWord *wp;
1052 
1053     switch (seg) {
1054 	case TEXTSEG:
1055 	    ++nwrites;
1056 	    wp = &p->word[cachehash(addr)];
1057 	    wp->addr = addr;
1058 	    wp->val = data;
1059 	    ptrace(IWRITE, p->pid, addr, data);
1060 	    break;
1061 
1062 	case DATASEG:
1063 	    ptrace(DWRITE, p->pid, addr, data);
1064 	    break;
1065 
1066 	default:
1067 	    panic("store: bad seg %d", seg);
1068 	    /* NOTREACHED */
1069     }
1070 }
1071 
1072 public printptraceinfo()
1073 {
1074     printf("%d fetchs, %d reads, %d writes\n", nfetchs, nreads, nwrites);
1075 }
1076 
1077 /*
1078  * Swap file numbers so as to redirect standard input and output.
1079  */
1080 
1081 private fswap(oldfd, newfd)
1082 int oldfd;
1083 int newfd;
1084 {
1085     if (oldfd != newfd) {
1086 	close(oldfd);
1087 	dup(newfd);
1088 	close(newfd);
1089     }
1090 }
1091