xref: /csrg-svn/old/dbx/runtime.vax.c (revision 13937)
1 
2 /* Copyright (c) 1982 Regents of the University of California */
3 
4 static char sccsid[] = "@(#)runtime.vax.c 1.7 07/15/83";
5 
6 /*
7  * Runtime organization dependent routines, mostly dealing with
8  * activation records.
9  */
10 
11 #include "defs.h"
12 #include "runtime.h"
13 #include "process.h"
14 #include "machine.h"
15 #include "events.h"
16 #include "mappings.h"
17 #include "symbols.h"
18 #include "tree.h"
19 #include "eval.h"
20 #include "operators.h"
21 #include "object.h"
22 #include <sys/param.h>
23 
24 #ifndef public
25 typedef struct Frame *Frame;
26 
27 #include "machine.h"
28 #endif
29 
30 #define NSAVEREG 12
31 
32 struct Frame {
33     Integer condition_handler;
34     Integer mask;
35     Address save_ap;		/* argument pointer */
36     Address save_fp;		/* frame pointer */
37     Address save_pc;		/* program counter */
38     Word save_reg[NSAVEREG];	/* not necessarily there */
39 };
40 
41 private Boolean walkingstack = false;
42 
43 /*
44  * Set a frame to the current activation record.
45  */
46 
47 private getcurframe(frp)
48 register Frame frp;
49 {
50     register int i;
51 
52     checkref(frp);
53     frp->mask = reg(NREG);
54     frp->save_ap = reg(ARGP);
55     frp->save_fp = reg(FRP);
56     frp->save_pc = reg(PROGCTR) + 1;
57     for (i = 0; i < NSAVEREG; i++) {
58 	frp->save_reg[i] = reg(i);
59     }
60 }
61 
62 /*
63  * Return a pointer to the next activation record up the stack.
64  * Return nil if there is none.
65  * Writes over space pointed to by given argument.
66  */
67 
68 #define bis(b, n) ((b & (1 << (n))) != 0)
69 
70 private Frame nextframe(frp)
71 Frame frp;
72 {
73     register Frame newfrp;
74     struct Frame frame;
75     register Integer i, j, mask;
76     Address prev_frame, callpc;
77     static Integer ntramp = 0;
78 
79     newfrp = frp;
80     prev_frame = frp->save_fp;
81 
82 /*
83  *  The check for interrupt generated frames is taken from adb with only
84  *  partial understanding.  If you're in "sub" and on a sigxxx "sigsub"
85  *  gets control, then the stack does NOT look like <main, sub, sigsub>.
86  *
87  *  As best I can make out it looks like:
88  *
89  *     <main, (machine check exception block + sub), sysframe, sigsub>.
90  *
91  *  When the signal occurs an exception block and a frame for the routine
92  *  in which it occured are pushed on the user stack.  Then another frame
93  *  is pushed corresponding to a call from the kernel to sigsub.
94  *
95  *  The addr in sub at which the exception occured is not in sub.save_pc
96  *  but in the machine check exception block.  It is at the magic address
97  *  fp + 76.
98  *
99  *  The current approach ignores the sys_frame (what adb reports as sigtramp)
100  *  and takes the pc for sub from the exception block.  This allows the
101  *  "where" command to report <main, sub, sigsub>, which seems reasonable.
102  */
103 
104 nextf:
105     dread(&frame, prev_frame, sizeof(struct Frame));
106     if (ntramp == 1) {
107 	dread(&callpc, prev_frame + 76, sizeof(callpc));
108     } else {
109 	callpc = frame.save_pc;
110     }
111     if (frame.save_fp == nil) {
112 	newfrp = nil;
113     } else if (callpc > 0x80000000 - 0x200 * UPAGES ) {
114 	 ntramp++;
115 	 prev_frame = frame.save_fp;
116 	 goto nextf;
117     } else {
118 	frame.save_pc = callpc;
119         ntramp = 0;
120 	mask = ((frame.mask >> 16) & 0x0fff);
121 	j = 0;
122 	for (i = 0; i < NSAVEREG; i++) {
123 	    if (bis(mask, i)) {
124 		newfrp->save_reg[i] = frame.save_reg[j];
125 		++j;
126 	    }
127 	}
128 	newfrp->condition_handler = frame.condition_handler;
129 	newfrp->mask = mask;
130 	newfrp->save_ap = frame.save_ap;
131 	newfrp->save_fp = frame.save_fp;
132 	newfrp->save_pc = frame.save_pc;
133     }
134     return newfrp;
135 }
136 
137 /*
138  * Return the frame associated with the given function.
139  * If the function is nil, return the most recently activated frame.
140  *
141  * Static allocation for the frame.
142  */
143 
144 public Frame findframe(f)
145 Symbol f;
146 {
147     register Frame frp;
148     static struct Frame frame;
149     Symbol p;
150     Boolean done;
151 
152     frp = &frame;
153     getcurframe(frp);
154     if (f != nil) {
155 	done = false;
156 	do {
157 	    p = whatblock(frp->save_pc);
158 	    if (p == f) {
159 		done = true;
160 	    } else if (p == program) {
161 		done = true;
162 		frp = nil;
163 	    } else {
164 		frp = nextframe(frp);
165 		if (frp == nil) {
166 		    done = true;
167 		}
168 	    }
169 	} while (not done);
170     }
171     return frp;
172 }
173 
174 /*
175  * Find the return address of the current procedure/function.
176  */
177 
178 public Address return_addr()
179 {
180     Frame frp;
181     Address addr;
182     struct Frame frame;
183 
184     frp = &frame;
185     getcurframe(frp);
186     frp = nextframe(frp);
187     if (frp == nil) {
188 	addr = 0;
189     } else {
190 	addr = frp->save_pc;
191     }
192     return addr;
193 }
194 
195 /*
196  * Push the value associated with the current function.
197  */
198 
199 public pushretval(len, isindirect)
200 Integer len;
201 Boolean isindirect;
202 {
203     Word r0;
204 
205     r0 = reg(0);
206     if (isindirect) {
207 	rpush((Address) r0, len);
208     } else {
209 	switch (len) {
210 	    case sizeof(char):
211 		push(char, r0);
212 		break;
213 
214 	    case sizeof(short):
215 		push(short, r0);
216 		break;
217 
218 	    default:
219 		if (len == sizeof(Word)) {
220 		    push(Word, r0);
221 		} else if (len == 2*sizeof(Word)) {
222 		    push(Word, r0);
223 		    push(Word, reg(1));
224 		} else {
225 		    panic("not indirect in pushretval?");
226 		}
227 		break;
228 	}
229     }
230 }
231 
232 /*
233  * Return the base address for locals in the given frame.
234  */
235 
236 public Address locals_base(frp)
237 register Frame frp;
238 {
239     return (frp == nil) ? reg(FRP) : frp->save_fp;
240 }
241 
242 /*
243  * Return the base address for arguments in the given frame.
244  */
245 
246 public Address args_base(frp)
247 register Frame frp;
248 {
249     return (frp == nil) ? reg(ARGP) : frp->save_ap;
250 }
251 
252 /*
253  * Return saved register n from the given frame.
254  */
255 
256 public Word savereg(n, frp)
257 register Integer n;
258 register Frame frp;
259 {
260     register Word w;
261 
262     if (frp == nil) {
263 	w = reg(n);
264     } else {
265 	switch (n) {
266 	    case ARGP:
267 		w = frp->save_ap;
268 		break;
269 
270 	    case FRP:
271 		w = frp->save_fp;
272 		break;
273 
274 	    case STKP:
275 		w = reg(STKP);
276 		break;
277 
278 	    case PROGCTR:
279 		w = frp->save_pc;
280 		break;
281 
282 	    default:
283 		assert(n >= 0 and n < NSAVEREG);
284 		w = frp->save_reg[n];
285 		break;
286 	}
287     }
288     return w;
289 }
290 
291 /*
292  * Return the nth argument to the current procedure.
293  */
294 
295 public Word argn(n, frp)
296 Integer n;
297 Frame frp;
298 {
299     Word w;
300 
301     dread(&w, args_base(frp) + (n * sizeof(Word)), sizeof(w));
302     return w;
303 }
304 
305 /*
306  * Calculate the entry address for a procedure or function parameter,
307  * given the address of the descriptor.
308  */
309 
310 public Address fparamaddr(a)
311 Address a;
312 {
313     Address r;
314 
315     dread(&r, a, sizeof(r));
316     return r;
317 }
318 
319 /*
320  * Print a list of currently active blocks starting with most recent.
321  */
322 
323 public wherecmd()
324 {
325     walkstack(false);
326 }
327 
328 /*
329  * Dump the world to the given file.
330  * Like "where", but variables are dumped also.
331  */
332 
333 public dump()
334 {
335     walkstack(true);
336 }
337 
338 /*
339  * Walk the stack of active procedures printing information
340  * about each active procedure.
341  */
342 
343 private walkstack(dumpvariables)
344 Boolean dumpvariables;
345 {
346     register Frame frp;
347     register Symbol f;
348     register Boolean save;
349     register Lineno line;
350     struct Frame frame;
351 
352     if (notstarted(process)) {
353 	error("program is not active");
354     } else {
355 	save = walkingstack;
356 	walkingstack = true;
357 	frp = &frame;
358 	getcurframe(frp);
359 	f = whatblock(frp->save_pc);
360 	do {
361 	    printf("%s", symname(f));
362 	    printparams(f, frp);
363 	    line = srcline(frp->save_pc - 1);
364 	    if (line != 0) {
365 		printf(", line %d", line);
366 		printf(" in \"%s\"\n", srcfilename(frp->save_pc - 1));
367 	    } else {
368 		printf(" at 0x%x\n", frp->save_pc);
369 	    }
370 	    if (dumpvariables) {
371 		dumpvars(f, frp);
372 		putchar('\n');
373 	    }
374 	    frp = nextframe(frp);
375 	    if (frp != nil) {
376 		f = whatblock(frp->save_pc);
377 	    }
378 	} while (frp != nil and f != program);
379 	if (dumpvariables) {
380 	    printf("in \"%s\":\n", symname(program));
381 	    dumpvars(program, nil);
382 	    putchar('\n');
383 	}
384 	walkingstack = save;
385     }
386 }
387 
388 /*
389  * Find the entry point of a procedure or function.
390  */
391 
392 public findbeginning(f)
393 Symbol f;
394 {
395     f->symvalue.funcv.beginaddr += 2;
396 }
397 
398 /*
399  * Return the address corresponding to the first line in a function.
400  */
401 
402 public Address firstline(f)
403 Symbol f;
404 {
405     Address addr;
406 
407     addr = codeloc(f);
408     while (linelookup(addr) == 0 and addr < objsize) {
409 	++addr;
410     }
411     if (addr == objsize) {
412 	addr = -1;
413     }
414     return addr;
415 }
416 
417 /*
418  * Catcher drops strike three ...
419  */
420 
421 public runtofirst()
422 {
423     Address addr;
424 
425     addr = pc;
426     while (linelookup(addr) == 0 and addr < objsize) {
427 	++addr;
428     }
429     if (addr < objsize) {
430 	stepto(addr);
431     }
432 }
433 
434 /*
435  * Return the address corresponding to the end of the program.
436  *
437  * We look for the entry to "exit".
438  */
439 
440 public Address lastaddr()
441 {
442     register Symbol s;
443 
444     s = lookup(identname("exit", true));
445     if (s == nil) {
446 	panic("can't find exit");
447     }
448     return codeloc(s);
449 }
450 
451 /*
452  * Decide if the given function is currently active.
453  *
454  * We avoid calls to "findframe" during a stack trace for efficiency.
455  * Presumably information evaluated while walking the stack is active.
456  */
457 
458 public Boolean isactive(f)
459 Symbol f;
460 {
461     register Boolean b;
462 
463     if (isfinished(process)) {
464 	b = false;
465     } else {
466 	if (walkingstack or f == program or
467 	  (ismodule(f) and isactive(container(f)))) {
468 	    b = true;
469 	} else {
470 	    b = (Boolean) (findframe(f) != nil);
471 	}
472     }
473     return b;
474 }
475 
476 /*
477  * Evaluate a call to a procedure.
478  */
479 
480 public callproc(procnode, arglist)
481 Node procnode;
482 Node arglist;
483 {
484     Symbol proc;
485     Integer argc;
486 
487     if (procnode->op != O_SYM) {
488 	beginerrmsg();
489 	fprintf(stderr, "can't call \"");
490 	prtree(stderr, procnode);
491 	fprintf(stderr, "\"");
492 	enderrmsg();
493     }
494     assert(procnode->op == O_SYM);
495     proc = procnode->value.sym;
496     if (not isblock(proc)) {
497 	error("\"%s\" is not a procedure or function", symname(proc));
498     }
499     pushenv();
500     pc = codeloc(proc);
501     argc = pushargs(proc, arglist);
502     beginproc(proc, argc);
503     isstopped = true;
504     event_once(build(O_EQ, build(O_SYM, pcsym), build(O_SYM, retaddrsym)),
505 	buildcmdlist(build(O_PROCRTN, proc)));
506     cont();
507     /* NOTREACHED */
508 }
509 
510 /*
511  * Push the arguments on the process' stack.  We do this by first
512  * evaluating them on the "eval" stack, then copying into the process'
513  * space.
514  */
515 
516 private Integer pushargs(proc, arglist)
517 Symbol proc;
518 Node arglist;
519 {
520     Stack *savesp;
521     int argc, args_size;
522 
523     savesp = sp;
524     argc = evalargs(proc, arglist);
525     args_size = sp - savesp;
526     setreg(STKP, reg(STKP) - args_size);
527     dwrite(savesp, reg(STKP), args_size);
528     sp = savesp;
529     return argc;
530 }
531 
532 /*
533  * Evaluate arguments left-to-right.
534  */
535 
536 private Integer evalargs(proc, arglist)
537 Symbol proc;
538 Node arglist;
539 {
540     Node p, exp;
541     Symbol arg;
542     Stack *savesp;
543     Address addr;
544     Integer count;
545 
546     savesp = sp;
547     count = 0;
548     arg = proc->chain;
549     for (p = arglist; p != nil; p = p->value.arg[1]) {
550 	if (p->op != O_COMMA) {
551 	    panic("evalargs: arglist missing comma");
552 	}
553 	if (arg == nil) {
554 	    sp = savesp;
555 	    error("too many parameters to %s", symname(proc));
556 	}
557 	exp = p->value.arg[0];
558 	if (not compatible(arg->type, exp->nodetype)) {
559 	    sp = savesp;
560 	    error("expression for parameter %s is of wrong type", symname(arg));
561 	}
562 	if (arg->class == REF) {
563 	    if (exp->op != O_RVAL) {
564 		sp = savesp;
565 		error("variable expected for parameter \"%s\"", symname(arg));
566 	    }
567 	    addr = lval(exp->value.arg[0]);
568 	    push(Address, addr);
569 	} else {
570 	    eval(exp);
571 	}
572 	arg = arg->chain;
573 	++count;
574     }
575     if (arg != nil) {
576 	sp = savesp;
577 	error("not enough parameters to %s", symname(proc));
578     }
579     return count;
580 }
581 
582 public procreturn(f)
583 Symbol f;
584 {
585     flushoutput();
586     putchar('\n');
587     printname(stdout, f);
588     printf(" returns successfully\n", symname(f));
589     popenv();
590     erecover();
591 }
592 
593 /*
594  * Push the current environment.
595  */
596 
597 private pushenv()
598 {
599     push(Address, pc);
600     push(Lineno, curline);
601     push(String, cursource);
602     push(Boolean, isstopped);
603     push(Symbol, curfunc);
604     push(Word, reg(PROGCTR));
605     push(Word, reg(STKP));
606 }
607 
608 /*
609  * Pop back to the real world.
610  */
611 
612 public popenv()
613 {
614     register String filename;
615 
616     setreg(STKP, pop(Word));
617     setreg(PROGCTR, pop(Word));
618     curfunc = pop(Symbol);
619     isstopped = pop(Boolean);
620     filename = pop(String);
621     curline = pop(Lineno);
622     pc = pop(Address);
623     setsource(filename);
624 }
625 
626 /*
627  * Flush the debuggee's standard output.
628  *
629  * This is VERY dependent on the use of stdio.
630  */
631 
632 public flushoutput()
633 {
634     register Symbol p, iob;
635     register Stack *savesp;
636 
637     p = lookup(identname("fflush", true));
638     while (p != nil and not isblock(p)) {
639 	p = p->next_sym;
640     }
641     if (p != nil) {
642 	iob = lookup(identname("_iob", true));
643 	if (iob != nil) {
644 	    pushenv();
645 	    pc = codeloc(p);
646 	    savesp = sp;
647 	    push(long, address(iob, nil) + sizeof(struct _iobuf));
648 	    setreg(STKP, reg(STKP) - sizeof(long));
649 	    dwrite(savesp, reg(STKP), sizeof(long));
650 	    sp = savesp;
651 	    beginproc(p, 1);
652 	    stepto(return_addr());
653 	    popenv();
654 	}
655     }
656 }
657