added WHEREIS to list of keywords, fixing a problem which
[unix-history] / usr / src / old / dbx / process.c
CommitLineData
9a3bab7a
ML
1/* Copyright (c) 1982 Regents of the University of California */
2
4e067a2c 3static char sccsid[] = "@(#)process.c 1.9 %G%";
9a3bab7a
ML
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>
b0edae1c 26#include <machine/reg.h>
9a3bab7a
ML
27#include <sys/stat.h>
28
29#ifndef public
30
31typedef struct Process *Process;
32
33Process 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
50typedef 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
59struct Process {
60 int pid; /* process being traced */
5870175c
ML
61 int mask; /* process status word */
62 Word reg[NREG]; /* process' registers */
9a3bab7a
ML
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 */
5870175c 69 Ttyinfo ttyinfo; /* process' terminal characteristics */
9a3bab7a
ML
70};
71
72/*
73 * These definitions are for the arguments to "pio".
74 */
75
76typedef enum { PREAD, PWRITE } PioOp;
77typedef enum { TEXTSEG, DATASEG } PioSeg;
78
79private struct Process pbuf;
80
81#define MAXNCMDARGS 10 /* maximum number of arguments to RUN */
82
83private Boolean just_started;
84private int argc;
85private String argv[MAXNCMDARGS];
86private String infile, outfile;
87
88/*
89 * Initialize process information.
90 */
91
92public 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);
4e067a2c
ML
110 pc = process->reg[PROGCTR];
111 getsrcpos();
9a3bab7a 112 }
4e067a2c 113 arginit();
9a3bab7a
ML
114}
115
116/*
117 * Routines to get at process information from outside this module.
118 */
119
120public Word reg(n)
121Integer 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
133public setreg(n, w)
134Integer n;
135Word 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
147private Boolean remade();
148
149public start(argv, infile, outfile)
150String argv[];
151String 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
187private time_t modtime;
188
189private Boolean remade(filename)
190String 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
205private 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
226public 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
238public newarg(arg)
239String 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
251public inarg(filename)
252String 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
265public outarg(filename)
266String filename;
267{
268 if (outfile != nil) {
269 error("multiple output redirect");
270 }
271 outfile = filename;
272}
273
274/*
275 * Start debuggee executing.
276 */
277
278public 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
296typedef int Intfunc();
297
298private Intfunc *dbintr;
299private intr();
300
301#define succeeds == true
302#define fails == false
303
36fd36ba
ML
304public cont(signo)
305int signo;
9a3bab7a
ML
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;
36fd36ba 315 stepover();
9a3bab7a
ML
316 }
317 for (;;) {
318 if (single_stepping) {
319 printnews();
320 } else {
321 setallbps();
36fd36ba 322 resume(signo);
9a3bab7a
ML
323 unsetallbps();
324 if (bpact() fails) {
325 printstatus();
326 }
327 }
36fd36ba 328 stepover();
9a3bab7a
ML
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
343private intr()
344{
345 signal(SIGINT, intr);
346}
347
348public fixintr()
349{
350 signal(SIGINT, dbintr);
351}
352
353/*
354 * Resume execution.
355 */
356
36fd36ba
ML
357public resume(signo)
358int signo;
9a3bab7a
ML
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 }
36fd36ba 367 pcont(p, signo);
9a3bab7a
ML
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 }
e0a80343 374 if (p->status != STOPPED) {
36fd36ba
ML
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 }
e0a80343 380 }
9a3bab7a
ML
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
396public stepc()
397{
398 if (not isstopped) {
399 error("can't continue execution");
400 }
401 isstopped = false;
402 dostep(false);
403 isstopped = true;
404}
405
406public next()
407{
408 if (not isstopped) {
409 error("can't continue execution");
410 }
411 isstopped = false;
412 dostep(true);
413 isstopped = true;
414}
415
36fd36ba
ML
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
425private stepover()
9a3bab7a 426{
36fd36ba
ML
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 }
9a3bab7a
ML
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
445public stepto(addr)
446Address addr;
447{
448 setbp(addr);
36fd36ba 449 resume(0);
9a3bab7a
ML
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
461public printstatus()
462{
b0edae1c
ML
463 if (process->status == FINISHED) {
464 exit(0);
9a3bab7a 465 } else {
b0edae1c
ML
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 ");
86d0cc79
ML
473 printloc();
474 putchar('\n');
b0edae1c 475 if (curline > 0) {
b0edae1c
ML
476 printlines(curline, curline);
477 } else {
b0edae1c
ML
478 printinst(pc, pc);
479 }
480 erecover();
9a3bab7a 481 } else {
b0edae1c
ML
482 fixbps();
483 fixintr();
9a3bab7a
ML
484 isstopped = true;
485 printerror();
486 }
487 }
488}
489
86d0cc79
ML
490/*
491 * Print out the current location in the debuggee.
492 */
493
494public 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
9a3bab7a
ML
506/*
507 * Some functions for testing the state of the process.
508 */
509
510public Boolean notstarted(p)
511Process p;
512{
513 return (Boolean) (p->status == NOTSTARTED);
514}
515
516public Boolean isfinished(p)
517Process p;
518{
519 return (Boolean) (p->status == FINISHED);
520}
521
522/*
523 * Return the signal number which stopped the process.
524 */
525
526public Integer errnum(p)
527Process p;
528{
529 return p->signo;
530}
531
532/*
533 * Return the termination code of the process.
534 */
535
536public Integer exitcode(p)
537Process 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".
ae743e15
ML
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.
9a3bab7a
ML
550 */
551
552extern Intfunc *onsyserr();
553
554private badaddr;
555private rwerr();
556
557/*
558 * Read from the process' instruction area.
559 */
560
561public iread(buff, addr, nbytes)
562char *buff;
563Address addr;
564int 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
583public iwrite(buff, addr, nbytes)
584char *buff;
585Address addr;
586int 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
603public dread(buff, addr, nbytes)
604char *buff;
605Address addr;
606int 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
624public dwrite(buff, addr, nbytes)
625char *buff;
626Address addr;
627int 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
644private rwerr()
645{
ae743e15
ML
646 /*
647 * Current response is to ignore the error and let the result
648 * (-1) ripple back up to the process.
649 *
9a3bab7a 650 error("bad read/write process address 0x%x", badaddr);
ae743e15 651 */
9a3bab7a
ML
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 */
700private pstart(p, argv, infile, outfile)
701Process p;
702String argv[];
703String infile;
704String outfile;
705{
706 int status;
9a3bab7a
ML
707
708 if (p->pid != 0) { /* child already running? */
709 ptrace(PKILL, p->pid, 0, 0); /* ... kill it! */
710 }
711 psigtrace(p, SIGTRAP, true);
86d0cc79 712 if ((p->pid = vfork()) == -1) {
9a3bab7a
ML
713 panic("can't fork");
714 }
715 if (ischild(p->pid)) {
86d0cc79
ML
716 Fileid in, out;
717
9a3bab7a
ML
718 traceme();
719 if (infile != nil) {
86d0cc79
ML
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);
9a3bab7a 726 }
86d0cc79 727 fswap(0, in);
9a3bab7a
ML
728 }
729 if (outfile != nil) {
86d0cc79
ML
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);
9a3bab7a 736 }
86d0cc79 737 fswap(1, out);
9a3bab7a 738 }
e0a80343 739 execv(argv[0], argv);
86d0cc79
ML
740 write(2, "can't exec ", 11);
741 write(2, argv[0], strlen(argv[0]));
742 write(2, "\n", 1);
743 _exit(1);
9a3bab7a
ML
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/*
36fd36ba
ML
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,
9a3bab7a
ML
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
36fd36ba 762private pcont(p, signo)
9a3bab7a 763Process p;
36fd36ba 764int signo;
9a3bab7a
ML
765{
766 int status;
767
768 if (p->pid == 0) {
769 error("program not active");
770 }
771 do {
36fd36ba 772 setinfo(p, signo);
9a3bab7a
ML
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
787public pstep(p)
788Process p;
789{
790 int status;
791
36fd36ba 792 setinfo(p, 0);
9a3bab7a
ML
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
804public psigtrace(p, sig, sw)
805Process p;
806int sig;
807Boolean 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
821public unsetsigtraces(p)
822Process p;
823{
824 p->sigset = 0;
825}
826
827/*
828 * Turn off attention to signals not being caught.
829 */
830
831private Intfunc *sigfunc[NSIG];
832
833private 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
848private 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
863private int rloc[] ={
864 R0, R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, AP, FP, SP, PC
865};
866
867private getinfo(p, status)
868register Process p;
869register 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 }
5870175c 886 savetty(stdout, &(p->ttyinfo));
9a3bab7a
ML
887 }
888}
889
890/*
891 * Set process's user area information from given process structure.
892 */
893
36fd36ba 894private setinfo(p, signo)
9a3bab7a 895register Process p;
36fd36ba 896int signo;
9a3bab7a
ML
897{
898 register int i;
899 register int r;
900
901 if (istraced(p)) {
36fd36ba 902 p->signo = signo;
9a3bab7a
ML
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 }
5870175c 909 restoretty(stdout, &(p->ttyinfo));
9a3bab7a
ML
910}
911
912/*
913 * Structure for reading and writing by words, but dealing with bytes.
914 */
915
916typedef 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
927private Word fetch();
928private store();
929
930private pio(p, op, seg, buff, addr, nbytes)
931Process p;
932PioOp op;
933PioSeg seg;
934char *buff;
935Address addr;
936int 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
1005private Integer nfetchs, nreads, nwrites;
1006
1007private Word fetch(p, seg, addr)
1008Process p;
1009PioSeg seg;
1010register 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
1045private store(p, seg, addr, data)
1046Process p;
1047PioSeg seg;
1048int addr;
1049Word 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
1072public 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
1081private fswap(oldfd, newfd)
1082int oldfd;
1083int newfd;
1084{
1085 if (oldfd != newfd) {
1086 close(oldfd);
1087 dup(newfd);
1088 close(newfd);
1089 }
1090}