xref: /original-bsd/old/dbx/runtime.c (revision 8251a00e)
1 
2 /* Copyright (c) 1982 Regents of the University of California */
3 
4 static char sccsid[] = "@(#)runtime.c 1.8 08/10/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 	    if (not isinline(f)) {
363 		printparams(f, frp);
364 	    }
365 	    line = srcline(frp->save_pc - 1);
366 	    if (line != 0) {
367 		printf(", line %d", line);
368 		printf(" in \"%s\"\n", srcfilename(frp->save_pc - 1));
369 	    } else {
370 		printf(" at 0x%x\n", frp->save_pc);
371 	    }
372 	    if (dumpvariables) {
373 		dumpvars(f, frp);
374 		putchar('\n');
375 	    }
376 	    if (isinline(f)) {
377 		f = container(f);
378 	    } else {
379 		frp = nextframe(frp);
380 		if (frp != nil) {
381 		    f = whatblock(frp->save_pc);
382 		}
383 	    }
384 	} while (frp != nil and f != program);
385 	if (dumpvariables) {
386 	    printf("in \"%s\":\n", symname(program));
387 	    dumpvars(program, nil);
388 	    putchar('\n');
389 	}
390 	walkingstack = save;
391     }
392 }
393 
394 /*
395  * Find the entry point of a procedure or function.
396  */
397 
398 public findbeginning(f)
399 Symbol f;
400 {
401     f->symvalue.funcv.beginaddr += 2;
402 }
403 
404 /*
405  * Return the address corresponding to the first line in a function.
406  */
407 
408 public Address firstline(f)
409 Symbol f;
410 {
411     Address addr;
412 
413     addr = codeloc(f);
414     while (linelookup(addr) == 0 and addr < objsize) {
415 	++addr;
416     }
417     if (addr == objsize) {
418 	addr = -1;
419     }
420     return addr;
421 }
422 
423 /*
424  * Catcher drops strike three ...
425  */
426 
427 public runtofirst()
428 {
429     Address addr;
430 
431     addr = pc;
432     while (linelookup(addr) == 0 and addr < objsize) {
433 	++addr;
434     }
435     if (addr < objsize) {
436 	stepto(addr);
437     }
438 }
439 
440 /*
441  * Return the address corresponding to the end of the program.
442  *
443  * We look for the entry to "exit".
444  */
445 
446 public Address lastaddr()
447 {
448     register Symbol s;
449 
450     s = lookup(identname("exit", true));
451     if (s == nil) {
452 	panic("can't find exit");
453     }
454     return codeloc(s);
455 }
456 
457 /*
458  * Decide if the given function is currently active.
459  *
460  * We avoid calls to "findframe" during a stack trace for efficiency.
461  * Presumably information evaluated while walking the stack is active.
462  */
463 
464 public Boolean isactive(f)
465 Symbol f;
466 {
467     register Boolean b;
468 
469     if (isfinished(process)) {
470 	b = false;
471     } else {
472 	if (walkingstack or f == program or
473 	  (ismodule(f) and isactive(container(f)))) {
474 	    b = true;
475 	} else {
476 	    b = (Boolean) (findframe(f) != nil);
477 	}
478     }
479     return b;
480 }
481 
482 /*
483  * Evaluate a call to a procedure.
484  */
485 
486 public callproc(procnode, arglist)
487 Node procnode;
488 Node arglist;
489 {
490     Symbol proc;
491     Integer argc;
492 
493     if (procnode->op != O_SYM) {
494 	beginerrmsg();
495 	fprintf(stderr, "can't call \"");
496 	prtree(stderr, procnode);
497 	fprintf(stderr, "\"");
498 	enderrmsg();
499     }
500     assert(procnode->op == O_SYM);
501     proc = procnode->value.sym;
502     if (not isblock(proc)) {
503 	error("\"%s\" is not a procedure or function", symname(proc));
504     }
505     pushenv();
506     pc = codeloc(proc);
507     argc = pushargs(proc, arglist);
508     beginproc(proc, argc);
509     isstopped = true;
510     event_once(build(O_EQ, build(O_SYM, pcsym), build(O_SYM, retaddrsym)),
511 	buildcmdlist(build(O_PROCRTN, proc)));
512     cont();
513     /* NOTREACHED */
514 }
515 
516 /*
517  * Push the arguments on the process' stack.  We do this by first
518  * evaluating them on the "eval" stack, then copying into the process'
519  * space.
520  */
521 
522 private Integer pushargs(proc, arglist)
523 Symbol proc;
524 Node arglist;
525 {
526     Stack *savesp;
527     int argc, args_size;
528 
529     savesp = sp;
530     argc = evalargs(proc, arglist);
531     args_size = sp - savesp;
532     setreg(STKP, reg(STKP) - args_size);
533     dwrite(savesp, reg(STKP), args_size);
534     sp = savesp;
535     return argc;
536 }
537 
538 /*
539  * Evaluate arguments left-to-right.
540  */
541 
542 private Integer evalargs(proc, arglist)
543 Symbol proc;
544 Node arglist;
545 {
546     Node p, exp;
547     Symbol arg;
548     Stack *savesp;
549     Address addr;
550     Integer count;
551 
552     savesp = sp;
553     count = 0;
554     arg = proc->chain;
555     for (p = arglist; p != nil; p = p->value.arg[1]) {
556 	if (p->op != O_COMMA) {
557 	    panic("evalargs: arglist missing comma");
558 	}
559 	if (arg == nil) {
560 	    sp = savesp;
561 	    error("too many parameters to %s", symname(proc));
562 	}
563 	exp = p->value.arg[0];
564 	if (not compatible(arg->type, exp->nodetype)) {
565 	    sp = savesp;
566 	    error("expression for parameter %s is of wrong type", symname(arg));
567 	}
568 	if (arg->class == REF) {
569 	    if (exp->op != O_RVAL) {
570 		sp = savesp;
571 		error("variable expected for parameter \"%s\"", symname(arg));
572 	    }
573 	    addr = lval(exp->value.arg[0]);
574 	    push(Address, addr);
575 	} else {
576 	    eval(exp);
577 	}
578 	arg = arg->chain;
579 	++count;
580     }
581     if (arg != nil) {
582 	sp = savesp;
583 	error("not enough parameters to %s", symname(proc));
584     }
585     return count;
586 }
587 
588 public procreturn(f)
589 Symbol f;
590 {
591     flushoutput();
592     putchar('\n');
593     printname(stdout, f);
594     printf(" returns successfully\n", symname(f));
595     popenv();
596     erecover();
597 }
598 
599 /*
600  * Push the current environment.
601  */
602 
603 private pushenv()
604 {
605     push(Address, pc);
606     push(Lineno, curline);
607     push(String, cursource);
608     push(Boolean, isstopped);
609     push(Symbol, curfunc);
610     push(Word, reg(PROGCTR));
611     push(Word, reg(STKP));
612 }
613 
614 /*
615  * Pop back to the real world.
616  */
617 
618 public popenv()
619 {
620     register String filename;
621 
622     setreg(STKP, pop(Word));
623     setreg(PROGCTR, pop(Word));
624     curfunc = pop(Symbol);
625     isstopped = pop(Boolean);
626     filename = pop(String);
627     curline = pop(Lineno);
628     pc = pop(Address);
629     setsource(filename);
630 }
631 
632 /*
633  * Flush the debuggee's standard output.
634  *
635  * This is VERY dependent on the use of stdio.
636  */
637 
638 public flushoutput()
639 {
640     register Symbol p, iob;
641     register Stack *savesp;
642 
643     p = lookup(identname("fflush", true));
644     while (p != nil and not isblock(p)) {
645 	p = p->next_sym;
646     }
647     if (p != nil) {
648 	iob = lookup(identname("_iob", true));
649 	if (iob != nil) {
650 	    pushenv();
651 	    pc = codeloc(p);
652 	    savesp = sp;
653 	    push(long, address(iob, nil) + sizeof(struct _iobuf));
654 	    setreg(STKP, reg(STKP) - sizeof(long));
655 	    dwrite(savesp, reg(STKP), sizeof(long));
656 	    sp = savesp;
657 	    beginproc(p, 1);
658 	    stepto(return_addr());
659 	    popenv();
660 	}
661     }
662 }
663