xref: /original-bsd/old/dbx/runtime.c (revision f82e54c4)
1 
2 /* Copyright (c) 1982 Regents of the University of California */
3 
4 static	char sccsid[] = "@(#)runtime.c	1.11 (Berkeley) 06/23/84";
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 Frame curframe = nil;
42 private struct Frame curframerec;
43 private Boolean walkingstack = false;
44 
45 #define frameeq(f1, f2) ((f1)->save_fp == (f2)->save_fp)
46 
47 /*
48  * Set a frame to the current activation record.
49  */
50 
51 private getcurframe(frp)
52 register Frame frp;
53 {
54     register int i;
55 
56     checkref(frp);
57     frp->mask = reg(NREG);
58     frp->save_ap = reg(ARGP);
59     frp->save_fp = reg(FRP);
60     frp->save_pc = reg(PROGCTR) + 1;
61     for (i = 0; i < NSAVEREG; i++) {
62 	frp->save_reg[i] = reg(i);
63     }
64 }
65 
66 /*
67  * Return a pointer to the next activation record up the stack.
68  * Return nil if there is none.
69  * Writes over space pointed to by given argument.
70  */
71 
72 #define bis(b, n) ((b & (1 << (n))) != 0)
73 
74 private Frame nextframe(frp)
75 Frame frp;
76 {
77     register Frame newfrp;
78     struct Frame frame;
79     register Integer i, j, mask;
80     Address prev_frame, callpc;
81     static Integer ntramp = 0;
82 
83     newfrp = frp;
84     prev_frame = frp->save_fp;
85 
86 /*
87  *  The check for interrupt generated frames is taken from adb with only
88  *  partial understanding.  If you're in "sub" and on a sigxxx "sigsub"
89  *  gets control, then the stack does NOT look like <main, sub, sigsub>.
90  *
91  *  As best I can make out it looks like:
92  *
93  *     <main, (machine check exception block + sub), sysframe, sigsub>.
94  *
95  *  When the signal occurs an exception block and a frame for the routine
96  *  in which it occured are pushed on the user stack.  Then another frame
97  *  is pushed corresponding to a call from the kernel to sigsub.
98  *
99  *  The addr in sub at which the exception occured is not in sub.save_pc
100  *  but in the machine check exception block.  It is at the magic address
101  *  fp + 84.
102  *
103  *  The current approach ignores the sys_frame (what adb reports as sigtramp)
104  *  and takes the pc for sub from the exception block.  This allows the
105  *  "where" command to report <main, sub, sigsub>, which seems reasonable.
106  */
107 
108 nextf:
109     dread(&frame, prev_frame, sizeof(struct Frame));
110     if (ntramp == 1) {
111 	dread(&callpc, prev_frame + 84, sizeof(callpc));
112     } else {
113 	callpc = frame.save_pc;
114     }
115     if (frame.save_fp == nil) {
116 	newfrp = nil;
117     } else if (callpc > 0x80000000 - 0x200 * UPAGES ) {
118 	 ntramp++;
119 	 prev_frame = frame.save_fp;
120 	 goto nextf;
121     } else {
122 	frame.save_pc = callpc;
123         ntramp = 0;
124 	mask = ((frame.mask >> 16) & 0x0fff);
125 	j = 0;
126 	for (i = 0; i < NSAVEREG; i++) {
127 	    if (bis(mask, i)) {
128 		newfrp->save_reg[i] = frame.save_reg[j];
129 		++j;
130 	    }
131 	}
132 	newfrp->condition_handler = frame.condition_handler;
133 	newfrp->mask = mask;
134 	newfrp->save_ap = frame.save_ap;
135 	newfrp->save_fp = frame.save_fp;
136 	newfrp->save_pc = frame.save_pc;
137     }
138     return newfrp;
139 }
140 
141 /*
142  * Get the current frame information in the given Frame and store the
143  * associated function in the given value-result parameter.
144  */
145 
146 private getcurfunc (frp, fp)
147 Frame frp;
148 Symbol *fp;
149 {
150     getcurframe(frp);
151     *fp = whatblock(frp->save_pc);
152 }
153 
154 /*
155  * Return the frame associated with the next function up the call stack, or
156  * nil if there is none.  The function is returned in a value-result parameter.
157  * For "inline" functions the statically outer function and same frame
158  * are returned.
159  */
160 
161 private Frame nextfunc (frp, fp)
162 Frame frp;
163 Symbol *fp;
164 {
165     Symbol t;
166     Frame nfrp;
167 
168     t = *fp;
169     checkref(t);
170     if (isinline(t)) {
171 	t = container(t);
172 	nfrp = frp;
173     } else {
174 	nfrp = nextframe(frp);
175 	if (nfrp == nil) {
176 	    t = nil;
177 	} else {
178 	    t = whatblock(nfrp->save_pc);
179 	}
180     }
181     *fp = t;
182     return nfrp;
183 }
184 
185 /*
186  * Return the frame associated with the given function.
187  * If the function is nil, return the most recently activated frame.
188  *
189  * Static allocation for the frame.
190  */
191 
192 public Frame findframe(f)
193 Symbol f;
194 {
195     register Frame frp;
196     static struct Frame frame;
197     Symbol p;
198 
199     frp = &frame;
200     getcurframe(frp);
201     if (f == nil)
202 	return (frp);
203     /*
204      * Starting at the current stack frame,
205      * walk backwards looking for a symbol
206      * match.  Beware of local blocks which
207      * have a back pointer but no stack frame.
208      */
209     p = whatblock(frp->save_pc);
210     while (p != f) {
211 	if (p == program) {
212 	    frp = nil;
213 	    break;
214 	}
215 	if (isinline(p)) {
216 	    p = container(p);
217 	    continue;
218 	}
219 	frp = nextframe(frp);
220 	if (frp == nil)
221 	   break;
222 	p = whatblock(frp->save_pc);
223     }
224     return (frp);
225 }
226 
227 /*
228  * Find the return address of the current procedure/function.
229  */
230 
231 public Address return_addr()
232 {
233     Frame frp;
234     Address addr;
235     struct Frame frame;
236 
237     frp = &frame;
238     getcurframe(frp);
239     frp = nextframe(frp);
240     if (frp == nil) {
241 	addr = 0;
242     } else {
243 	addr = frp->save_pc;
244     }
245     return addr;
246 }
247 
248 /*
249  * Push the value associated with the current function.
250  */
251 
252 public pushretval(len, isindirect)
253 Integer len;
254 Boolean isindirect;
255 {
256     Word r0;
257 
258     r0 = reg(0);
259     if (isindirect) {
260 	rpush((Address) r0, len);
261     } else {
262 	switch (len) {
263 	    case sizeof(char):
264 		push(char, r0);
265 		break;
266 
267 	    case sizeof(short):
268 		push(short, r0);
269 		break;
270 
271 	    default:
272 		if (len == sizeof(Word)) {
273 		    push(Word, r0);
274 		} else if (len == 2*sizeof(Word)) {
275 		    push(Word, r0);
276 		    push(Word, reg(1));
277 		} else {
278 		    panic("not indirect in pushretval?");
279 		}
280 		break;
281 	}
282     }
283 }
284 
285 /*
286  * Return the base address for locals in the given frame.
287  */
288 
289 public Address locals_base(frp)
290 register Frame frp;
291 {
292     return (frp == nil) ? reg(FRP) : frp->save_fp;
293 }
294 
295 /*
296  * Return the base address for arguments in the given frame.
297  */
298 
299 public Address args_base(frp)
300 register Frame frp;
301 {
302     return (frp == nil) ? reg(ARGP) : frp->save_ap;
303 }
304 
305 /*
306  * Return saved register n from the given frame.
307  */
308 
309 public Word savereg(n, frp)
310 register Integer n;
311 register Frame frp;
312 {
313     register Word w;
314 
315     if (frp == nil) {
316 	w = reg(n);
317     } else {
318 	switch (n) {
319 	    case ARGP:
320 		w = frp->save_ap;
321 		break;
322 
323 	    case FRP:
324 		w = frp->save_fp;
325 		break;
326 
327 	    case STKP:
328 		w = reg(STKP);
329 		break;
330 
331 	    case PROGCTR:
332 		w = frp->save_pc;
333 		break;
334 
335 	    default:
336 		assert(n >= 0 and n < NSAVEREG);
337 		w = frp->save_reg[n];
338 		break;
339 	}
340     }
341     return w;
342 }
343 
344 /*
345  * Return the nth argument to the current procedure.
346  */
347 
348 public Word argn(n, frp)
349 Integer n;
350 Frame frp;
351 {
352     Word w;
353 
354     dread(&w, args_base(frp) + (n * sizeof(Word)), sizeof(w));
355     return w;
356 }
357 
358 /*
359  * Calculate the entry address for a procedure or function parameter,
360  * given the address of the descriptor.
361  */
362 
363 public Address fparamaddr(a)
364 Address a;
365 {
366     Address r;
367 
368     dread(&r, a, sizeof(r));
369     return r;
370 }
371 
372 /*
373  * Print a list of currently active blocks starting with most recent.
374  */
375 
376 public wherecmd()
377 {
378     walkstack(false);
379 }
380 
381 /*
382  * Dump the world to the given file.
383  * Like "where", but variables are dumped also.
384  */
385 
386 public dump()
387 {
388     walkstack(true);
389 }
390 
391 /*
392  * Walk the stack of active procedures printing information
393  * about each active procedure.
394  */
395 
396 private walkstack(dumpvariables)
397 Boolean dumpvariables;
398 {
399     register Frame frp;
400     register Boolean save;
401     register Lineno line;
402     Symbol f;
403     struct Frame frame;
404 
405     if (notstarted(process)) {
406 	error("program is not active");
407     } else {
408 	save = walkingstack;
409 	walkingstack = true;
410 	frp = &frame;
411 	getcurfunc(frp, &f);
412 	do {
413 	    printf("%s", symname(f));
414 	    if (not isinline(f)) {
415 		printparams(f, frp);
416 	    }
417 	    line = srcline(frp->save_pc - 1);
418 	    if (line != 0) {
419 		printf(", line %d", line);
420 		printf(" in \"%s\"\n", srcfilename(frp->save_pc - 1));
421 	    } else {
422 		printf(" at 0x%x\n", frp->save_pc);
423 	    }
424 	    if (dumpvariables) {
425 		dumpvars(f, frp);
426 		putchar('\n');
427 	    }
428 	    frp = nextfunc(frp, &f);
429 	} while (frp != nil and f != program);
430 	if (dumpvariables) {
431 	    printf("in \"%s\":\n", symname(program));
432 	    dumpvars(program, nil);
433 	    putchar('\n');
434 	}
435 	walkingstack = save;
436     }
437 }
438 
439 /*
440  * Set the current function to the given symbol.
441  * We must adjust "curframe" so that subsequent operations are
442  * not confused; for simplicity we simply clear it.
443  */
444 
445 public setcurfunc (f)
446 Symbol f;
447 {
448     curfunc = f;
449     curframe = nil;
450 }
451 
452 /*
453  * Set curfunc to be N up/down the stack from its current value.
454  */
455 
456 public up (n)
457 integer n;
458 {
459     integer i;
460     Symbol f;
461     Frame frp;
462     boolean done;
463 
464     if (not isactive(program)) {
465 	error("program is not active");
466     } else if (curfunc == nil) {
467 	error("no current function");
468     } else {
469 	i = 0;
470 	f = curfunc;
471 	if (curframe != nil) {
472 	    frp = curframe;
473 	} else {
474 	    frp = findframe(f);
475 	}
476 	done = false;
477 	do {
478 	    if (frp == nil) {
479 		done = true;
480 		error("not that many levels");
481 	    } else if (i >= n) {
482 		done = true;
483 		curfunc = f;
484 		curframe = &curframerec;
485 		*curframe = *frp;
486 	    } else if (f == program) {
487 		done = true;
488 		error("not that many levels");
489 	    } else {
490 		frp = nextfunc(frp, &f);
491 	    }
492 	    ++i;
493 	} while (not done);
494     }
495 }
496 
497 public down (n)
498 integer n;
499 {
500     integer i, depth;
501     register Frame frp;
502     Symbol f;
503     struct Frame frame;
504 
505     if (not isactive(program)) {
506 	error("program is not active");
507     } else if (curfunc == nil) {
508 	error("no current function");
509     } else {
510 	depth = 0;
511 	frp = &frame;
512 	getcurfunc(frp, &f);
513 	if (curframe == nil) {
514 	    curframe = &curframerec;
515 	    *curframe = *(findframe(curfunc));
516 	}
517 	while ((f != curfunc or !frameeq(frp, curframe)) and f != nil) {
518 	    frp = nextfunc(frp, &f);
519 	    ++depth;
520 	}
521 	if (f == nil or n > depth) {
522 	    error("not that many levels");
523 	} else {
524 	    depth -= n;
525 	    frp = &frame;
526 	    getcurfunc(frp, &f);
527 	    for (i = 0; i < depth; i++) {
528 		frp = nextfunc(frp, &f);
529 		assert(frp != nil);
530 	    }
531 	    curfunc = f;
532 	    *curframe = *frp;
533 	}
534     }
535 }
536 
537 /*
538  * Find the entry point of a procedure or function.
539  */
540 
541 public findbeginning(f)
542 Symbol f;
543 {
544     if (isinternal(f)) {
545 	f->symvalue.funcv.beginaddr += 15;
546     } else {
547 	f->symvalue.funcv.beginaddr += 2;
548     }
549 }
550 
551 /*
552  * Return the address corresponding to the first line in a function.
553  */
554 
555 public Address firstline(f)
556 Symbol f;
557 {
558     Address addr;
559 
560     addr = codeloc(f);
561     while (linelookup(addr) == 0 and addr < objsize) {
562 	++addr;
563     }
564     if (addr == objsize) {
565 	addr = -1;
566     }
567     return addr;
568 }
569 
570 /*
571  * Catcher drops strike three ...
572  */
573 
574 public runtofirst()
575 {
576     Address addr;
577 
578     addr = pc;
579     while (linelookup(addr) == 0 and addr < objsize) {
580 	++addr;
581     }
582     if (addr < objsize) {
583 	stepto(addr);
584     }
585 }
586 
587 /*
588  * Return the address corresponding to the end of the program.
589  *
590  * We look for the entry to "exit".
591  */
592 
593 public Address lastaddr()
594 {
595     register Symbol s;
596 
597     s = lookup(identname("exit", true));
598     if (s == nil) {
599 	panic("can't find exit");
600     }
601     return codeloc(s);
602 }
603 
604 /*
605  * Decide if the given function is currently active.
606  *
607  * We avoid calls to "findframe" during a stack trace for efficiency.
608  * Presumably information evaluated while walking the stack is active.
609  */
610 
611 public Boolean isactive(f)
612 Symbol f;
613 {
614     register Boolean b;
615 
616     if (isfinished(process)) {
617 	b = false;
618     } else {
619 	if (walkingstack or f == program or
620 	  (ismodule(f) and isactive(container(f)))) {
621 	    b = true;
622 	} else {
623 	    b = (Boolean) (findframe(f) != nil);
624 	}
625     }
626     return b;
627 }
628 
629 /*
630  * Evaluate a call to a procedure.
631  */
632 
633 public callproc(procnode, arglist)
634 Node procnode;
635 Node arglist;
636 {
637     Symbol proc;
638     Integer argc;
639 
640     if (procnode->op != O_SYM) {
641 	beginerrmsg();
642 	fprintf(stderr, "can't call \"");
643 	prtree(stderr, procnode);
644 	fprintf(stderr, "\"");
645 	enderrmsg();
646     }
647     assert(procnode->op == O_SYM);
648     proc = procnode->value.sym;
649     if (not isblock(proc)) {
650 	error("\"%s\" is not a procedure or function", symname(proc));
651     }
652     pushenv();
653     pc = codeloc(proc);
654     argc = pushargs(proc, arglist);
655     beginproc(proc, argc);
656     isstopped = true;
657     event_once(build(O_EQ, build(O_SYM, pcsym), build(O_SYM, retaddrsym)),
658 	buildcmdlist(build(O_PROCRTN, proc)));
659     cont(0);
660     /* NOTREACHED */
661 }
662 
663 /*
664  * Push the arguments on the process' stack.  We do this by first
665  * evaluating them on the "eval" stack, then copying into the process'
666  * space.
667  */
668 
669 private Integer pushargs(proc, arglist)
670 Symbol proc;
671 Node arglist;
672 {
673     Stack *savesp;
674     int argc, args_size;
675 
676     savesp = sp;
677     argc = evalargs(proc, arglist);
678     args_size = sp - savesp;
679     setreg(STKP, reg(STKP) - args_size);
680     dwrite(savesp, reg(STKP), args_size);
681     sp = savesp;
682     return argc;
683 }
684 
685 /*
686  * Check to see if an expression is correct for a given parameter.
687  * If the given parameter is false, don't worry about type inconsistencies.
688  *
689  * Return whether or not it is ok.
690  */
691 
692 private boolean chkparam (actual, formal, chk)
693 Node actual;
694 Symbol formal;
695 boolean chk;
696 {
697     boolean b;
698 
699     b = true;
700     if (chk) {
701 	if (formal == nil) {
702 	    beginerrmsg();
703 	    fprintf(stderr, "too many parameters");
704 	    b = false;
705 	} else if (not compatible(formal->type, actual->nodetype)) {
706 	    beginerrmsg();
707 	    fprintf(stderr, "type mismatch for %s", symname(formal));
708 	    b = false;
709 	}
710     }
711     if (b and formal != nil and isvarparam(formal) and
712 	not isopenarray(formal->type) and actual->op != O_RVAL)
713     {
714 	beginerrmsg();
715 	fprintf(stderr, "expected variable, found \"");
716 	prtree(stderr, actual);
717 	fprintf(stderr, "\"");
718 	b = false;
719     }
720     return b;
721 }
722 
723 /*
724  * Pass an expression to a particular parameter.
725  *
726  * Normally we pass either the address or value, but in some cases
727  * (such as C strings) we want to copy the value onto the stack and
728  * pass its address.
729  */
730 
731 private passparam (actual, formal)
732 Node actual;
733 Symbol formal;
734 {
735     boolean b;
736     Address addr;
737     Stack *savesp;
738     integer paramsize;
739 
740     if (isvarparam(formal) and not isopenarray(formal->type)) {
741 	addr = lval(actual->value.arg[0]);
742 	push(Address, addr);
743     } else if (passaddr(formal, actual->nodetype)) {
744 	savesp = sp;
745 	eval(actual);
746 	paramsize = sp - savesp;
747 	setreg(STKP, reg(STKP) - paramsize);
748 	dwrite(savesp, reg(STKP), paramsize);
749 	sp = savesp;
750 	push(Address, reg(STKP));
751 	if (formal != nil and isopenarray(formal->type)) {
752 	    push(integer, paramsize div size(formal->type->type));
753 	}
754     } else {
755 	eval(actual);
756     }
757 }
758 
759 /*
760  * Evaluate an argument list left-to-right.
761  */
762 
763 private Integer evalargs(proc, arglist)
764 Symbol proc;
765 Node arglist;
766 {
767     Node p, actual;
768     Symbol formal;
769     Stack *savesp;
770     Integer count;
771     boolean chk;
772 
773     savesp = sp;
774     count = 0;
775     formal = proc->chain;
776     chk = (boolean) (not nosource(proc));
777     for (p = arglist; p != nil; p = p->value.arg[1]) {
778 	assert(p->op == O_COMMA);
779 	actual = p->value.arg[0];
780 	if (not chkparam(actual, formal, chk)) {
781 	    fprintf(stderr, " in call to %s", symname(proc));
782 	    sp = savesp;
783 	    enderrmsg();
784 	}
785 	passparam(actual, formal);
786 	if (formal != nil) {
787 	    formal = formal->chain;
788 	}
789 	++count;
790     }
791     if (chk) {
792 	if (formal != nil) {
793 	    sp = savesp;
794 	    error("not enough parameters to %s", symname(proc));
795 	}
796     }
797     return count;
798 }
799 
800 public procreturn(f)
801 Symbol f;
802 {
803     flushoutput();
804     putchar('\n');
805     printname(stdout, f);
806     printf(" returns successfully\n", symname(f));
807     popenv();
808     erecover();
809 }
810 
811 /*
812  * Push the current environment.
813  */
814 
815 private pushenv()
816 {
817     push(Address, pc);
818     push(Lineno, curline);
819     push(String, cursource);
820     push(Boolean, isstopped);
821     push(Symbol, curfunc);
822     push(Frame, curframe);
823     push(struct Frame, curframerec);
824     push(Word, reg(PROGCTR));
825     push(Word, reg(STKP));
826 }
827 
828 /*
829  * Pop back to the real world.
830  */
831 
832 public popenv()
833 {
834     register String filename;
835 
836     setreg(STKP, pop(Word));
837     setreg(PROGCTR, pop(Word));
838     curframerec = pop(struct Frame);
839     curframe = pop(Frame);
840     curfunc = pop(Symbol);
841     isstopped = pop(Boolean);
842     filename = pop(String);
843     curline = pop(Lineno);
844     pc = pop(Address);
845     setsource(filename);
846 }
847 
848 /*
849  * Flush the debuggee's standard output.
850  *
851  * This is VERY dependent on the use of stdio.
852  */
853 
854 public flushoutput()
855 {
856     register Symbol p, iob;
857     register Stack *savesp;
858 
859     p = lookup(identname("fflush", true));
860     while (p != nil and not isblock(p)) {
861 	p = p->next_sym;
862     }
863     if (p != nil) {
864 	iob = lookup(identname("_iob", true));
865 	if (iob != nil) {
866 	    pushenv();
867 	    pc = codeloc(p);
868 	    savesp = sp;
869 	    push(long, address(iob, nil) + sizeof(struct _iobuf));
870 	    setreg(STKP, reg(STKP) - sizeof(long));
871 	    dwrite(savesp, reg(STKP), sizeof(long));
872 	    sp = savesp;
873 	    beginproc(p, 1);
874 	    stepto(return_addr());
875 	    popenv();
876 	}
877     }
878 }
879