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