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