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