Add copyright
[unix-history] / usr / src / usr.bin / 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
DF
7#ifndef lint
8static char sccsid[] = "@(#)ptrace.c 5.1 (Berkeley) %G%";
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
ML
26# if (isvaxpx)
27# include "pxinfo.h"
28# endif
7838db54 29
fa1f8751
ML
30#ifndef vax
31# define U_PAGE 0x2400
32# define U_AR0 (14*sizeof(int))
33 LOCAL int ar0val = -1;
34#endif
35
7838db54
ML
36/*
37 * This magic macro enables us to look at the process' registers
38 * in its user structure. Very gross.
39 */
40
fa1f8751
ML
41#ifdef vax
42# define regloc(reg) (ctob(UPAGES) + ( sizeof(int) * (reg) ))
43#else
44# define regloc(reg) (ar0val + ( sizeof(int) * (reg) ))
45#endif
7838db54 46
f9e6f1f3
ML
47#define WMASK (~(sizeof(WORD) - 1))
48#define cachehash(addr) ((unsigned) ((addr >> 2) % CSIZE))
7838db54 49
f9e6f1f3
ML
50#define FIRSTSIG SIGINT
51#define LASTSIG SIGQUIT
52#define ischild(pid) ((pid) == 0)
53#define traceme() ptrace(0, 0, 0, 0)
54#define setrep(n) (1 << ((n)-1))
55#define istraced(p) (p->sigset&setrep(p->signo))
7838db54
ML
56
57/*
58 * ptrace options (specified in first argument)
59 */
60
f9e6f1f3
ML
61#define UREAD 3 /* read from process's user structure */
62#define UWRITE 6 /* write to process's user structure */
63#define IREAD 1 /* read from process's instruction space */
64#define IWRITE 4 /* write to process's instruction space */
65#define DREAD 2 /* read from process's data space */
66#define DWRITE 5 /* write to process's data space */
67#define CONT 7 /* continue stopped process */
68#define SSTEP 9 /* continue for approximately one instruction */
69#define PKILL 8 /* terminate the process */
7838db54
ML
70
71/*
72 * Start up a new process by forking and exec-ing the
73 * given argument list, returning when the process is loaded
74 * and ready to execute. The PROCESS information (pointed to
75 * by the first argument) is appropriately filled.
76 *
77 * If the given PROCESS structure is associated with an already running
78 * process, we terminate it.
79 */
80
81/* VARARGS2 */
33ece7d5 82pstart(p, cmd, argv, infile, outfile)
7838db54 83PROCESS *p;
33ece7d5 84char *cmd;
7838db54
ML
85char **argv;
86char *infile;
87char *outfile;
88{
f9e6f1f3
ML
89 int status;
90 FILE *in, *out;
91
92 if (p->pid != 0) { /* child already running? */
93 ptrace(PKILL, p->pid, 0, 0); /* ... kill it! */
94 }
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
200typedef int INTFUNC();
201
202LOCAL INTFUNC *sigfunc[NSIG];
203
204LOCAL sigs_off()
205{
f9e6f1f3 206 register int i;
7838db54 207
f9e6f1f3
ML
208 for (i = FIRSTSIG; i < LASTSIG; i++) {
209 if (i != SIGKILL) {
210 sigfunc[i] = signal(i, SIG_IGN);
7838db54 211 }
f9e6f1f3 212 }
7838db54
ML
213}
214
215/*
216 * turn back on attention to signals
217 */
218
219LOCAL sigs_on()
220{
f9e6f1f3 221 register int i;
7838db54 222
f9e6f1f3
ML
223 for (i = FIRSTSIG; i < LASTSIG; i++) {
224 if (i != SIGKILL) {
225 signal(i, sigfunc[i]);
7838db54 226 }
f9e6f1f3 227 }
7838db54
ML
228}
229
230/*
231 * get PROCESS information from process's user area
232 */
233
fa1f8751
ML
234#if vax
235 LOCAL int rloc[] ={
236 R0, R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11,
237 };
238#else
239 LOCAL int rloc[] ={
240 R0, R1, R2, R3, R4, R5, R6, R7, AR0, AR1, AR2, AR3, AR4, AR5,
241 };
242#endif
7838db54
ML
243
244LOCAL getinfo(p, status)
245register PROCESS *p;
246register int status;
247{
f9e6f1f3
ML
248 register int i;
249
250 p->signo = (status&0177);
251 p->exitval = ((status >> 8)&0377);
252 if (p->signo == STOPPED) {
253 p->status = p->signo;
254 p->signo = p->exitval;
255 p->exitval = 0;
256 } else {
257 p->status = FINISHED;
258 return;
259 }
fa1f8751
ML
260#ifndef vax
261 if (ar0val < 0){
262 ar0val = ptrace(UREAD, p->pid, U_AR0, 0);
263 ar0val -= U_PAGE;
264 }
265#endif
f9e6f1f3
ML
266 for (i = 0; i < NREG; i++) {
267 p->reg[i] = ptrace(UREAD, p->pid, regloc(rloc[i]), 0);
268 p->oreg[i] = p->reg[i];
269 }
fa1f8751 270#ifdef vax
f9e6f1f3
ML
271 p->fp = p->ofp = ptrace(UREAD, p->pid, regloc(FP), 0);
272 p->ap = p->oap = ptrace(UREAD, p->pid, regloc(AP), 0);
273 p->sp = p->osp = ptrace(UREAD, p->pid, regloc(SP), 0);
274 p->pc = p->opc = ptrace(UREAD, p->pid, regloc(PC), 0);
fa1f8751
ML
275#else
276 p->fp = p->ofp = ptrace(UREAD, p->pid, regloc(AR6), 0);
277 p->ap = p->oap = p->fp;
278 p->sp = p->osp = ptrace(UREAD, p->pid, regloc(SP), 0);
279 p->pc = p->opc = ptrace(UREAD, p->pid, regloc(PC), 0);
280#endif
7838db54
ML
281}
282
283/*
284 * set process's user area information from given PROCESS structure
285 */
286
287LOCAL setinfo(p)
288register PROCESS *p;
289{
f9e6f1f3
ML
290 register int i;
291 register int r;
292
293 if (istraced(p)) {
294 p->signo = 0;
295 }
296 for (i = 0; i < NREG; i++) {
297 if ((r = p->reg[i]) != p->oreg[i]) {
298 ptrace(UWRITE, p->pid, regloc(rloc[i]), r);
7838db54 299 }
f9e6f1f3 300 }
fa1f8751 301#if vax
f9e6f1f3
ML
302 if ((r = p->fp) != p->ofp) {
303 ptrace(UWRITE, p->pid, regloc(FP), r);
304 }
305 if ((r = p->sp) != p->osp) {
306 ptrace(UWRITE, p->pid, regloc(SP), r);
307 }
308 if ((r = p->ap) != p->oap) {
309 ptrace(UWRITE, p->pid, regloc(AP), r);
310 }
fa1f8751
ML
311#else
312 if ((r = p->fp) != p->ofp) {
313 ptrace(UWRITE, p->pid, regloc(AR6), r);
314 }
315 if ((r = p->sp) != p->osp) {
316 ptrace(UWRITE, p->pid, regloc(SP), r);
317 }
318#endif
f9e6f1f3
ML
319 if ((r = p->pc) != p->opc) {
320 ptrace(UWRITE, p->pid, regloc(PC), r);
321 }
7838db54
ML
322}
323
324/*
325 * Structure for reading and writing by words, but dealing with bytes.
326 */
327
328typedef union {
f9e6f1f3
ML
329 WORD pword;
330 BYTE pbyte[sizeof(WORD)];
7838db54
ML
331} PWORD;
332
333/*
334 * Read (write) from (to) the process' address space.
335 * We must deal with ptrace's inability to look anywhere other
336 * than at a word boundary.
337 */
338
339LOCAL WORD fetch();
340LOCAL store();
341
342pio(p, op, seg, buff, addr, nbytes)
343PROCESS *p;
344PIO_OP op;
345PIO_SEG seg;
346char *buff;
347ADDRESS addr;
348int nbytes;
349{
f9e6f1f3
ML
350 register int i;
351 register ADDRESS newaddr;
352 register char *cp;
353 char *bufend;
354 PWORD w;
355 ADDRESS wordaddr;
356 int byteoff;
357
358 if (p->status != STOPPED) {
359 error("program is not active");
360 }
361 cp = buff;
362 newaddr = addr;
363 wordaddr = (newaddr&WMASK);
364 if (wordaddr != newaddr) {
365 w.pword = fetch(p, seg, wordaddr);
366 for (i = newaddr - wordaddr; i<sizeof(WORD) && nbytes>0; i++) {
367 if (op == PREAD) {
368 *cp++ = w.pbyte[i];
369 } else {
370 w.pbyte[i] = *cp++;
371 }
372 nbytes--;
7838db54 373 }
f9e6f1f3
ML
374 if (op == PWRITE) {
375 store(p, seg, wordaddr, w.pword);
7838db54 376 }
f9e6f1f3
ML
377 newaddr = wordaddr + sizeof(WORD);
378 }
379 byteoff = (nbytes&(~WMASK));
380 nbytes -= byteoff;
381 bufend = cp + nbytes;
382 while (cp < bufend) {
383 if (op == PREAD) {
384 *((WORD *) cp) = fetch(p, seg, newaddr);
385 } else {
386 store(p, seg, newaddr, *((WORD *) cp));
7838db54 387 }
f9e6f1f3
ML
388 cp += sizeof(WORD);
389 newaddr += sizeof(WORD);
390 }
391 if (byteoff > 0) {
392 w.pword = fetch(p, seg, newaddr);
393 for (i = 0; i < byteoff; i++) {
394 if (op == PREAD) {
395 *cp++ = w.pbyte[i];
396 } else {
397 w.pbyte[i] = *cp++;
398 }
7838db54 399 }
f9e6f1f3
ML
400 if (op == PWRITE) {
401 store(p, seg, newaddr, w.pword);
402 }
403 }
7838db54
ML
404}
405
406/*
407 * Get a word from a process at the given address.
408 * The address is assumed to be on a word boundary.
409 *
410 * We use a simple cache scheme to avoid redundant references to
411 * the instruction space (which is assumed to be pure). In the
412 * case of px, the "instruction" space lies between ENDOFF and
413 * ENDOFF + objsize.
414 *
415 * It is necessary to use a write-through scheme so that
416 * breakpoints right next to each other don't interfere.
417 */
418
419LOCAL WORD fetch(p, seg, addr)
420PROCESS *p;
421PIO_SEG seg;
422register int addr;
423{
f9e6f1f3
ML
424 register CACHEWORD *wp;
425 register WORD w;
426
427 switch (seg) {
428 case TEXTSEG:
429# if (isvaxpx)
430 panic("tried to fetch from px i-space");
431 /* NOTREACHED */
432# else
433 wp = &p->word[cachehash(addr)];
434 if (addr == 0 || wp->addr != addr) {
435 w = ptrace(IREAD, p->pid, addr, 0);
436 wp->addr = addr;
437 wp->val = w;
438 } else {
439 w = wp->val;
440 }
441 break;
442# endif
443
444 case DATASEG:
445# if (isvaxpx)
446 if (addr >= ENDOFF && addr < ENDOFF + objsize) {
447 wp = &p->word[cachehash(addr)];
448 if (addr == 0 || wp->addr != addr) {
449 w = ptrace(DREAD, p->pid, addr, 0);
450 wp->addr = addr;
451 wp->val = w;
452 } else {
453 w = wp->val;
454 }
455 } else {
456 w = ptrace(DREAD, p->pid, addr, 0);
457 }
458# else
459 w = ptrace(DREAD, p->pid, addr, 0);
460# endif
461 break;
462
463 default:
464 panic("fetch: bad seg %d", seg);
465 /* NOTREACHED */
466 }
467 return(w);
7838db54
ML
468}
469
470/*
471 * Put a word into the process' address space at the given address.
472 * The address is assumed to be on a word boundary.
473 */
474
475LOCAL store(p, seg, addr, data)
476PROCESS *p;
477PIO_SEG seg;
478int addr;
479WORD data;
480{
f9e6f1f3
ML
481 register CACHEWORD *wp;
482
483 switch (seg) {
484 case TEXTSEG:
485 wp = &p->word[cachehash(addr)];
486 wp->addr = addr;
487 wp->val = data;
488 ptrace(IWRITE, p->pid, addr, data);
489 break;
490
491 case DATASEG:
492# if (isvaxpx)
493 if (addr >= ENDOFF && addr < ENDOFF + objsize) {
494 wp = &p->word[cachehash(addr)];
495 wp->addr = addr;
496 wp->val = data;
497 }
498# endif
499 ptrace(DWRITE, p->pid, addr, data);
500 break;
501
502 default:
503 panic("store: bad seg %d", seg);
504 /*NOTREACHED*/
505 }
506}
7838db54 507
f9e6f1f3
ML
508/*
509 * Initialize the instruction cache for a process.
510 * This is particularly necessary after the program has been remade.
511 */
512
513initcache(process)
514PROCESS *process;
515{
516 register int i;
517
518 for (i = 0; i < CSIZE; i++) {
519 process->word[i].addr = 0;
520 }
7838db54
ML
521}
522
523/*
524 * Swap file numbers so as to redirect standard input and output.
525 */
526
527LOCAL fswap(oldfd, newfd)
528int oldfd;
529int newfd;
530{
f9e6f1f3
ML
531 if (oldfd != newfd) {
532 close(oldfd);
533 dup(newfd);
534 close(newfd);
535 }
7838db54 536}