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