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