4.4BSD snapshot (revision 8.1); add 1993 to copyright
[unix-history] / usr / src / usr.bin / pascal / pdx / process / ptrace.c
CommitLineData
505bf312 1/*-
2839532b
KB
2 * Copyright (c) 1980, 1993
3 * The Regents of the University of California. All rights reserved.
505bf312
KB
4 *
5 * %sccs.include.redist.c%
f644bb55 6 */
fa1f8751 7
f644bb55 8#ifndef lint
2839532b 9static char sccsid[] = "@(#)ptrace.c 8.1 (Berkeley) %G%";
505bf312 10#endif /* not lint */
7838db54
ML
11
12/*
13 * routines for tracing the execution of a process
14 *
15 * The system call "ptrace" does all the work, these
16 * routines just try to interface easily to it.
17 */
18
19#include "defs.h"
20#include <signal.h>
21#include <sys/param.h>
f3338386 22#include <machine/reg.h>
7838db54
ML
23#include "process.h"
24#include "object.h"
25#include "process.rep"
26
f9e6f1f3 27# include "pxinfo.h"
7838db54 28
82d3cd01 29#ifdef mc68000
fa1f8751
ML
30# define U_PAGE 0x2400
31# define U_AR0 (14*sizeof(int))
32 LOCAL int ar0val = -1;
33#endif
34
7838db54
ML
35/*
36 * This magic macro enables us to look at the process' registers
37 * in its user structure. Very gross.
38 */
39
82d3cd01 40#if defined(vax) || defined(tahoe)
fa1f8751
ML
41# define regloc(reg) (ctob(UPAGES) + ( sizeof(int) * (reg) ))
42#else
43# define regloc(reg) (ar0val + ( sizeof(int) * (reg) ))
44#endif
7838db54 45
f9e6f1f3
ML
46#define WMASK (~(sizeof(WORD) - 1))
47#define cachehash(addr) ((unsigned) ((addr >> 2) % CSIZE))
7838db54 48
f9e6f1f3
ML
49#define ischild(pid) ((pid) == 0)
50#define traceme() ptrace(0, 0, 0, 0)
51#define setrep(n) (1 << ((n)-1))
52#define istraced(p) (p->sigset&setrep(p->signo))
7838db54
ML
53
54/*
55 * ptrace options (specified in first argument)
56 */
57
f9e6f1f3
ML
58#define UREAD 3 /* read from process's user structure */
59#define UWRITE 6 /* write to process's user structure */
60#define IREAD 1 /* read from process's instruction space */
61#define IWRITE 4 /* write to process's instruction space */
62#define DREAD 2 /* read from process's data space */
63#define DWRITE 5 /* write to process's data space */
64#define CONT 7 /* continue stopped process */
65#define SSTEP 9 /* continue for approximately one instruction */
66#define PKILL 8 /* terminate the process */
7838db54
ML
67
68/*
69 * Start up a new process by forking and exec-ing the
70 * given argument list, returning when the process is loaded
71 * and ready to execute. The PROCESS information (pointed to
72 * by the first argument) is appropriately filled.
73 *
74 * If the given PROCESS structure is associated with an already running
75 * process, we terminate it.
76 */
77
78/* VARARGS2 */
33ece7d5 79pstart(p, cmd, argv, infile, outfile)
7838db54 80PROCESS *p;
33ece7d5 81char *cmd;
7838db54
ML
82char **argv;
83char *infile;
84char *outfile;
85{
f9e6f1f3
ML
86 int status;
87 FILE *in, *out;
88
89 if (p->pid != 0) { /* child already running? */
90 ptrace(PKILL, p->pid, 0, 0); /* ... kill it! */
91 }
85c677b9 92#ifdef tahoe
82d3cd01 93 INTFP = (ADDRESS)0;
85c677b9 94#endif tahoe
f9e6f1f3
ML
95 psigtrace(p, SIGTRAP, TRUE);
96 if ((p->pid = fork()) == -1) {
97 panic("can't fork");
98 }
99 if (ischild(p->pid)) {
100 traceme();
101 if (infile != NIL) {
102 if ((in = fopen(infile, "r")) == NIL) {
103 printf("can't read %s\n", infile);
104 exit(1);
105 }
106 fswap(0, fileno(in));
7838db54 107 }
f9e6f1f3
ML
108 if (outfile != NIL) {
109 if ((out = fopen(outfile, "w")) == NIL) {
110 printf("can't write %s\n", outfile);
111 exit(1);
112 }
113 fswap(1, fileno(out));
7838db54 114 }
f9e6f1f3
ML
115 execvp(cmd, argv);
116 panic("can't exec %s", argv[0]);
117 }
118 pwait(p->pid, &status);
119 getinfo(p, status);
7838db54
ML
120}
121
122/*
123 * Continue a stopped process. The argument points to a PROCESS structure.
124 * Before the process is restarted it's user area is modified according to
125 * the values in the structure. When this routine finishes,
126 * the structure has the new values from the process's user area.
127 *
128 * Pcont terminates when the process stops with a signal pending that
129 * is being traced (via psigtrace), or when the process terminates.
130 */
131
132pcont(p)
133PROCESS *p;
134{
f9e6f1f3 135 int status;
7838db54 136
f9e6f1f3
ML
137 if (p->pid == 0) {
138 error("program not active");
139 }
140 do {
141 setinfo(p);
142 sigs_off();
143 if (ptrace(CONT, p->pid, p->pc, p->signo) < 0) {
144 panic("can't continue process");
7838db54 145 }
f9e6f1f3
ML
146 pwait(p->pid, &status);
147 sigs_on();
148 getinfo(p, status);
149 } while (p->status == STOPPED && !istraced(p));
7838db54
ML
150}
151
152/*
153 * single step as best ptrace can
154 */
155
156pstep(p)
157PROCESS *p;
158{
f9e6f1f3
ML
159 int status;
160
161 setinfo(p);
162 sigs_off();
163 ptrace(SSTEP, p->pid, p->pc, p->signo);
164 pwait(p->pid, &status);
165 sigs_on();
166 getinfo(p, status);
7838db54
ML
167}
168
169/*
170 * Return from execution when the given signal is pending.
171 */
172
173psigtrace(p, sig, sw)
174PROCESS *p;
175int sig;
176int sw;
177{
f9e6f1f3
ML
178 if (sw) {
179 p->sigset |= setrep(sig);
180 } else {
181 p->sigset &= ~setrep(sig);
182 }
7838db54
ML
183}
184
185/*
186 * Don't catch any signals.
187 * Particularly useful when letting a process finish uninhibited (i.e. px).
188 */
189
190unsetsigtraces(p)
191PROCESS *p;
192{
f9e6f1f3 193 p->sigset = 0;
7838db54
ML
194}
195
196/*
197 * turn off attention to signals not being caught
198 */
199
8e41b0ae 200LOCAL void *onintr, *onquit;
7838db54
ML
201
202LOCAL sigs_off()
203{
82d3cd01
KM
204 onintr = signal(SIGINT, SIG_IGN);
205 onquit = signal(SIGQUIT, SIG_IGN);
7838db54
ML
206}
207
208/*
209 * turn back on attention to signals
210 */
211
212LOCAL sigs_on()
213{
82d3cd01
KM
214 (void) signal(SIGINT, onintr);
215 (void) signal(SIGQUIT, onquit);
7838db54
ML
216}
217
218/*
219 * get PROCESS information from process's user area
220 */
221
fa1f8751
ML
222#if vax
223 LOCAL int rloc[] ={
224 R0, R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11,
225 };
82d3cd01
KM
226#endif
227#if tahoe
228 LOCAL int rloc[] ={
229 R0, R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12,
230 };
231#endif
232#if mc68000
fa1f8751
ML
233 LOCAL int rloc[] ={
234 R0, R1, R2, R3, R4, R5, R6, R7, AR0, AR1, AR2, AR3, AR4, AR5,
235 };
236#endif
7838db54
ML
237
238LOCAL getinfo(p, status)
239register PROCESS *p;
240register int status;
241{
f9e6f1f3
ML
242 register int i;
243
244 p->signo = (status&0177);
245 p->exitval = ((status >> 8)&0377);
246 if (p->signo == STOPPED) {
247 p->status = p->signo;
248 p->signo = p->exitval;
249 p->exitval = 0;
250 } else {
251 p->status = FINISHED;
252 return;
253 }
82d3cd01 254#if !defined(vax) && !defined(tahoe)
fa1f8751
ML
255 if (ar0val < 0){
256 ar0val = ptrace(UREAD, p->pid, U_AR0, 0);
257 ar0val -= U_PAGE;
258 }
259#endif
f9e6f1f3
ML
260 for (i = 0; i < NREG; i++) {
261 p->reg[i] = ptrace(UREAD, p->pid, regloc(rloc[i]), 0);
262 p->oreg[i] = p->reg[i];
263 }
82d3cd01 264#if defined(vax) || defined(tahoe)
f9e6f1f3 265 p->fp = p->ofp = ptrace(UREAD, p->pid, regloc(FP), 0);
f9e6f1f3
ML
266 p->sp = p->osp = ptrace(UREAD, p->pid, regloc(SP), 0);
267 p->pc = p->opc = ptrace(UREAD, p->pid, regloc(PC), 0);
82d3cd01
KM
268#endif
269#ifdef vax
270 p->ap = p->oap = ptrace(UREAD, p->pid, regloc(AP), 0);
271#endif
272#ifdef mc68000
fa1f8751
ML
273 p->fp = p->ofp = ptrace(UREAD, p->pid, regloc(AR6), 0);
274 p->ap = p->oap = p->fp;
275 p->sp = p->osp = ptrace(UREAD, p->pid, regloc(SP), 0);
276 p->pc = p->opc = ptrace(UREAD, p->pid, regloc(PC), 0);
277#endif
7838db54
ML
278}
279
280/*
281 * set process's user area information from given PROCESS structure
282 */
283
284LOCAL setinfo(p)
285register PROCESS *p;
286{
f9e6f1f3
ML
287 register int i;
288 register int r;
289
290 if (istraced(p)) {
291 p->signo = 0;
292 }
293 for (i = 0; i < NREG; i++) {
294 if ((r = p->reg[i]) != p->oreg[i]) {
295 ptrace(UWRITE, p->pid, regloc(rloc[i]), r);
7838db54 296 }
f9e6f1f3 297 }
82d3cd01 298#if vax || tahoe
f9e6f1f3
ML
299 if ((r = p->fp) != p->ofp) {
300 ptrace(UWRITE, p->pid, regloc(FP), r);
301 }
82d3cd01
KM
302#endif
303#if vax
f9e6f1f3
ML
304 if ((r = p->ap) != p->oap) {
305 ptrace(UWRITE, p->pid, regloc(AP), r);
306 }
82d3cd01
KM
307#endif
308#if mc68000
fa1f8751
ML
309 if ((r = p->fp) != p->ofp) {
310 ptrace(UWRITE, p->pid, regloc(AR6), r);
311 }
82d3cd01 312#endif
fa1f8751
ML
313 if ((r = p->sp) != p->osp) {
314 ptrace(UWRITE, p->pid, regloc(SP), r);
315 }
f9e6f1f3
ML
316 if ((r = p->pc) != p->opc) {
317 ptrace(UWRITE, p->pid, regloc(PC), r);
318 }
7838db54
ML
319}
320
321/*
322 * Structure for reading and writing by words, but dealing with bytes.
323 */
324
325typedef union {
f9e6f1f3
ML
326 WORD pword;
327 BYTE pbyte[sizeof(WORD)];
7838db54
ML
328} PWORD;
329
330/*
331 * Read (write) from (to) the process' address space.
332 * We must deal with ptrace's inability to look anywhere other
333 * than at a word boundary.
334 */
335
336LOCAL WORD fetch();
337LOCAL store();
338
339pio(p, op, seg, buff, addr, nbytes)
340PROCESS *p;
341PIO_OP op;
342PIO_SEG seg;
343char *buff;
344ADDRESS addr;
345int nbytes;
346{
82d3cd01 347 register int i, k;
f9e6f1f3
ML
348 register ADDRESS newaddr;
349 register char *cp;
350 char *bufend;
351 PWORD w;
352 ADDRESS wordaddr;
353 int byteoff;
354
355 if (p->status != STOPPED) {
356 error("program is not active");
357 }
358 cp = buff;
359 newaddr = addr;
360 wordaddr = (newaddr&WMASK);
361 if (wordaddr != newaddr) {
362 w.pword = fetch(p, seg, wordaddr);
363 for (i = newaddr - wordaddr; i<sizeof(WORD) && nbytes>0; i++) {
364 if (op == PREAD) {
365 *cp++ = w.pbyte[i];
366 } else {
367 w.pbyte[i] = *cp++;
368 }
369 nbytes--;
7838db54 370 }
f9e6f1f3
ML
371 if (op == PWRITE) {
372 store(p, seg, wordaddr, w.pword);
7838db54 373 }
f9e6f1f3
ML
374 newaddr = wordaddr + sizeof(WORD);
375 }
376 byteoff = (nbytes&(~WMASK));
377 nbytes -= byteoff;
378 bufend = cp + nbytes;
379 while (cp < bufend) {
380 if (op == PREAD) {
82d3cd01
KM
381 w.pword = fetch(p, seg, newaddr);
382 for (k = 0; k < sizeof(WORD); k++) {
383 *cp++ = w.pbyte[k];
384 }
f9e6f1f3 385 } else {
82d3cd01
KM
386 for (k = 0; k < sizeof(WORD); k++) {
387 w.pbyte[k] = *cp++;
388 }
389 store(p, seg, newaddr, w.pword);
7838db54 390 }
f9e6f1f3
ML
391 newaddr += sizeof(WORD);
392 }
393 if (byteoff > 0) {
394 w.pword = fetch(p, seg, newaddr);
395 for (i = 0; i < byteoff; i++) {
396 if (op == PREAD) {
397 *cp++ = w.pbyte[i];
398 } else {
399 w.pbyte[i] = *cp++;
400 }
7838db54 401 }
f9e6f1f3
ML
402 if (op == PWRITE) {
403 store(p, seg, newaddr, w.pword);
404 }
405 }
7838db54
ML
406}
407
408/*
409 * Get a word from a process at the given address.
410 * The address is assumed to be on a word boundary.
411 *
412 * We use a simple cache scheme to avoid redundant references to
413 * the instruction space (which is assumed to be pure). In the
414 * case of px, the "instruction" space lies between ENDOFF and
415 * ENDOFF + objsize.
416 *
417 * It is necessary to use a write-through scheme so that
418 * breakpoints right next to each other don't interfere.
419 */
420
421LOCAL WORD fetch(p, seg, addr)
422PROCESS *p;
423PIO_SEG seg;
424register int addr;
425{
f9e6f1f3
ML
426 register CACHEWORD *wp;
427 register WORD w;
428
429 switch (seg) {
430 case TEXTSEG:
82d3cd01
KM
431 panic("tried to fetch from px i-space");
432 /* NOTREACHED */
433
434 case DATASEG:
435 if (addr >= ENDOFF && addr < ENDOFF + objsize) {
f9e6f1f3
ML
436 wp = &p->word[cachehash(addr)];
437 if (addr == 0 || wp->addr != addr) {
82d3cd01 438 w = ptrace(DREAD, p->pid, addr, 0);
f9e6f1f3
ML
439 wp->addr = addr;
440 wp->val = w;
441 } else {
442 w = wp->val;
443 }
82d3cd01 444 } else {
f9e6f1f3 445 w = ptrace(DREAD, p->pid, addr, 0);
82d3cd01 446 }
f9e6f1f3
ML
447 break;
448
449 default:
450 panic("fetch: bad seg %d", seg);
451 /* NOTREACHED */
452 }
453 return(w);
7838db54
ML
454}
455
456/*
457 * Put a word into the process' address space at the given address.
458 * The address is assumed to be on a word boundary.
459 */
460
461LOCAL store(p, seg, addr, data)
462PROCESS *p;
463PIO_SEG seg;
464int addr;
465WORD data;
466{
f9e6f1f3
ML
467 register CACHEWORD *wp;
468
469 switch (seg) {
470 case TEXTSEG:
471 wp = &p->word[cachehash(addr)];
472 wp->addr = addr;
473 wp->val = data;
474 ptrace(IWRITE, p->pid, addr, data);
475 break;
476
477 case DATASEG:
82d3cd01
KM
478 if (addr >= ENDOFF && addr < ENDOFF + objsize) {
479 wp = &p->word[cachehash(addr)];
480 wp->addr = addr;
481 wp->val = data;
482 }
f9e6f1f3
ML
483 ptrace(DWRITE, p->pid, addr, data);
484 break;
485
486 default:
487 panic("store: bad seg %d", seg);
488 /*NOTREACHED*/
489 }
490}
7838db54 491
f9e6f1f3
ML
492/*
493 * Initialize the instruction cache for a process.
494 * This is particularly necessary after the program has been remade.
495 */
496
497initcache(process)
498PROCESS *process;
499{
500 register int i;
501
502 for (i = 0; i < CSIZE; i++) {
503 process->word[i].addr = 0;
504 }
7838db54
ML
505}
506
507/*
508 * Swap file numbers so as to redirect standard input and output.
509 */
510
511LOCAL fswap(oldfd, newfd)
512int oldfd;
513int newfd;
514{
f9e6f1f3
ML
515 if (oldfd != newfd) {
516 close(oldfd);
517 dup(newfd);
518 close(newfd);
519 }
7838db54 520}
82d3cd01
KM
521
522#ifdef tahoe
523BOOLEAN didret;
524
525void
526chkret(p, status)
527PROCESS *p;
528int status;
529{
530 if (((status == (SIGILL << 8) | STOPPED) ||
531 (status == (SIGTRAP << 8) | STOPPED))) {
532 didret = FALSE;
533 } else {
534 didret = TRUE;
535 }
536}
537
538void
539doret(p)
540PROCESS *p;
541{
542 register count = 0;
543
544 if (!didret) {
545 do {
546 if (++count > 5) {
547 panic("px would not return to interpreter");
548 }
549 p->pc = RETLOC;
550 pstep(p);
551 } while(INTFP && p->fp != INTFP);
552 didret = TRUE;
553 }
554}
555#endif