123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486 |
- .\" A simple tutorial
- .\"
- .\" $Id$
- .\"
- .bp
- .DS
- APPENDIX B
- .DE
- .SH
- How to use the interpreter
- .PP
- The interpreter is not normally used for the debugging of programs under
- construction. Its primary application is as a verification tool for almost
- completed programs. Although the proper operation of the interpreter is
- obviously a black art, this chapter tries to provide some guidelines.
- .LP
- For the sake of the argument, the source language is assumed to be C, but most
- hints apply equally well to other languages supported by ACK.
- .sp
- .LP
- .I "Initial measures"
- .PP
- Start with a test case of trivial size; to be on the safe side, reckon with a
- time dilatation factor of about 500, i.e., a second grows into 10 minutes.
- (The interpreter takes 0.5 msec to do one EM instruction on a Sun 3/50).
- Fortunately many trivial test cases are much shorter than one second.
- .PP
- Compile the program into an \fIe.out\fP, the EM machine version of a
- \fIa.out\fP, by calling \fIem22\fP (for 2-byte integers and 2-byte pointers),
- \fIem24\fP (for 2 and 4) or \fIem44\fP (for 4 and 4) as seems appropriate;
- if in doubt, use \fIem44\fP. These compilers can be found in the ACK
- \fIbin\fP directory, and should be used instead of \fIacc\fP (or normal
- .UX
- \fIcc\fP). Alternatively, \fIacc \-memNN\fP can be used instead of
- \fIemNN\fP.
- .LP
- If a C program consists of more than one file, as it usually does, there is
- a small problem. The \fIacc\fP and \fIcc\fP compilers generate .o files,
- whereas the \fIemNN\fP compilers generate .m files as object files.
- A simple technique to avoid the problem is to call
- .DS
- em44 *.c
- .DE
- if possible. If not, the following hack on the \fIMakefile\fP generally works.
- .IP \-
- Make sure the \fIMakefile\fP is reasonably clean and complete: all calls to
- the compiler are through \fI$(CC)\fP, \fICFLAGS\fP is used properly and all
- dependencies are specified.
- .IP \-
- Add the following lines to the \fIMakefile\fP (possibly permanently):
- .DS
- \&.SUFFIXES: .o
- \&.c.o:
- \& $(CC) \-c $(CFLAGS) $<
- .DE
- .IP \-
- Set CC to \fIem44 \-.c\fP (for example). Make sure CFLAGS includes
- the \-O option; this yields a speed-up of about 15 %.
- .IP \-
- Change all .o to .m (or .k if the \-O option is not used).
- .IP \-
- If necessary, change \fIa.out\fP to \fIe.out\fP.
- .PP
- With these changes, \fImake\fP will produce an EM object;
- \fIesize\fP can be used to verify that it is indeed an EM object and obtain some
- statistics. Then call the interpreter:
- .DS
- int <EM-object-file> [ parameters ]
- .DE
- where the parameters are the normal parameters of the program. This should
- work exactly like the original program, though slower. It reads from the
- terminal if the original does, it opens and closes files like the original and
- it accepts interrupts.
- .sp
- .LP
- .I "Interpreting the results"
- .PP
- Now there are several possibilities.
- .PP
- It does all this. Great! This means the program
- does not do very uncouth things. Now
- read the file \fIint.mess\fP to see if any messages were generated. If there
- are none, the program did not really run (perhaps the original cc \fIa.out\fP
- got called instead?) Normally there is at least a termination message like
- .DS
- (Message): program exits with status 0 at "awa.p", line 64, INR = 4124
- .DE
- This says that the program terminated through an exit(0) on line 64 of the
- file \fIawa.p\fP after 4124 EM instructions.
- If this is the only message it is time to move to a bigger test case.
- .PP
- On the other hand, the program may come to a grinding halt with an error
- message.
- All messages (errors and warnings) have a format in which the sequence
- .DS
- "<file name>", line <ln#>
- .DE
- occurs, which is the same sequence many compilers produce for their error
- messages. Consequently, the \fIint.mess\fP file can be processed as any
- compiler message output.
- .PP
- One such message can be
- .DS
- (Fatal error) a.em: trap "Addressing non existent memory" not caught at "a.c", line 2, INR = 16
- .DE
- produced by the abysmal program
- .DS
- main() {
- *(int*)200000 = 1;
- }
- .DE
- .LP
- Often the effects are more subtle, however. The program
- .DS
- main() {
- int *a, b = 777;
- b = *a;
- }
- .DE
- produces the following five warnings (in far less than a second):
- .DS
- (Warning 47, #1): Local data pointer expected at "t.c", line 4, INR = 17
- (Warning 61, cont.): Actual memory is undefined at "t.c", line 4, INR = 17
- (Warning 102, #1): Returned function result too small at "<unknown>", line 0, INR = 21
- (Warning 43, #1): Local integer expected at "exit.c", line 11, INR = 34
- (Warning 61, cont.): Actual memory is undefined at "exit.c", line 11, INR = 34
- .DE
- The one about the function result looks the most frightening,
- but is the most easily solved:
- \fImain\fP is a function returning an int, so the start-up routine expects a
- (four-byte) integer but gets an empty (zero-byte) return area.
- .LP
- \fINote\fP: The experts are divided about this. The traditional school holds
- that \fImain\fP is an int function and its result is the return code; this
- leaves them with two ways of supplying a return code: one as the parameter
- of \fIexit()\fP and one as the result
- of \fImain\fP. The modern school (Berkeley 4.2 etc.) claims that
- return codes are supplied exclusively
- by \fIexit()\fP, and they have an \fIexit(0)\fP in
- the start-up routine, just after the call to \fImain()\fP; leaving \fImain()\fP
- through the bottom implies successful termination.
- .LP
- We shall satisfy both groups by
- .DS
- main() {
- int *a, b = 777;
- b = *a;
- exit(0);
- }
- .DE
- This results in
- .DS
- (Warning 47, #1): Local data pointer expected at "t.c", line 4, INR = 17
- (Warning 61, cont.): Actual memory is undefined at "t.c", line 4, INR = 17
- (Message): program exits with status 0 at "exit.c", line 11, INR = 33
- .DE
- which is pretty clear as it stands.
- .sp
- .LP
- .I "Using stack dumps"
- .PP
- Let's, for the sake of argument
- and to avoid the fierce realism of 10000-line programs, assume that the above
- still does not give enough information.
- Since the error occurred in EM instruction number 17, we should like to see
- more information around that moment. Call the interpreter again, now with the
- shell variable AT set at 17:
- .DS
- int AT=17 t.em
- .DE
- (The interpreter has a number of internal variables that can be set by
- assignments on the command line, like with \fImake\fP.)
- This gives a file called \fIint.log\fP containing the
- stack dump of 150 lines presented at the end of this chapter.
- .PP
- Since dumping is a subfacility of logging in the interpreter, the formats of
- the lines are
- the same. If a line starts with an @, it will contain a file-name/line-number
- indication; the next two characters are the subject and the log
- level. Then comes the information, preceded by a space. The text contains
- three stack dumps, one before the offending instruction, one at it, and one
- after it; then the interpreter stops. All kinds of other dumps can be
- obtained, but this is default.
- .PP
- For each instruction we have, in order:
- .IP \-
- an @x9 line, giving the position in the program,
- .IP \-
- the messages, warnings and errors from the instruction as it is being executed,
- .IP \-
- dump(s), as requested.
- .PP
- The first two lines mean that at line 4 in file \fIt.c\fP the interpreter
- performed its 16-th instruction, with the Program Counter at 30 pointing at
- opcode 180 in the text segment; the instruction was an LOL (LOad Local)
- with the operand \-4 derived from the opcode. It copies the local at offset
- \-4 to the top of the stack. The effect can be seen from the subsequent stack
- dump, where the undefined word at addresses 2147483568 to ...571 (the variable
- \fIa\fP) has been copied to the top of the stack at 2147483560 (copying
- undefined values does not generate a warning).
- Since we used the \fIem44\fP compiler, all pointers and ints in our dump are
- 4 bytes long.
- So a variable at address X in reality extends from address X to X+3.
- .br
- Note that this is not the offending instruction; this stack dump represents
- the situation just before the error.
- .PP
- The stack consists of a sequence of frames, each containing data followed by
- a Return Status Block resulting from a call; the last frame ends in
- top-of-stack. The first frame represents the stack when the program starts,
- through a call to the start-up routine. This routine prepares the second
- stack frame with the actual parameters to \fImain()\fP:
- \fIargc\fP at 2147483596, \fIargv\fP at 2147483600 and \fIenviron\fP at
- 2147483604.
- .LP
- The RSB line shows that the call to \fImain()\fP was made from procedure 0
- which has 0 locals, with PC at
- 16, an LB of 2147483608 and file name and line number still unknown.
- The \fIcode\fP in the RSB tells how this RSB was made; possible values are STP
- (start-up), CAL, RTT (returnable trap) and NRT (non-returnable trap).
- .PP
- The next frame shows the local variable(s) of \fImain()\fP; there are two of
- them, the pointer \fIa\fP at 2147483568, which is undefined, and variable
- \fIb\fP at 2147483564, which has the value 777. Then comes a copy of \fIa\fP,
- just made by the LOL instruction, at 2147483560. The following line shows that
- the Function Return Area (which does not reside at the end of the stack, but
- just happens to be printed here) has size 0 and is presently undefined.
- The stack dump ends
- by showing that the Actuals Base is at 2147483596 (pointing at \fIargc\fP), the
- Locals Base at 2147483572 (pointing just above the local \fIa\fP), the Stack
- Pointer at 2147483560 (pointing at the undefined pointer), the line count is 4
- and the file name is "t.c".
- .LP
- (Notice that there is one more stack frame than one would probably expect, the
- one above the start-up routine.)
- .LP
- The Function Return Area
- could have a size larger than 0 and still be undefined, for
- example when an instruction that does not preserve the contents of the FRA has
- just been executed; likewise the FRA could have size 0 and be defined
- nevertheless, for example just after a RET 0 instruction.
- .PP
- All this has set the scene for the distaster which is about to strike in the
- next instruction. This is indeed a LOI (LOad Indirect) of size 4, opcode 169;
- it causes the message
- .DS
- warning: Local data pointer expected [stack.c: 242]
- .DE
- and its continuation
- .DS
- warning cont.: Actual memory is undefined
- .DE
- (detected in the interpreter file \fIstack.c\fP at line 242; this can be
- useful for sorting out dubious semantics). We see that the effect, as shown in
- the third frame of this stack dump (at instruction number 17) is somewhat
- unexpected: the LOI has fetched the value 4 and stacked it. The reason is
- that, unfortunately, undefinedness is not transitive in the interpreter. When
- an undefined value is used in an operation (other than copying) a warning is
- given, but thereafter the value is treated as if it were zero. So, after the
- warning a normal null pointer remains, which is then used to pick up the value
- at location 0. This is the place where the EM machine stores its current line
- number, which is presently 4.
- .PP
- The third stack dump shows the final effect: the value 4 has been unstacked
- and copied to variable \fIb\fP at 2147483564 through an STL (STore Local)
- instruction.
- .PP
- Since this form of logging dumps the stack only, the log file is relatively
- small as dumps go.
- Nevertheless, a useful excerpt can be obtained with the command
- .DS
- grep 'd1' int.log
- .DE
- This extracts the Return Status Block lines from the log, thus producing three
- traces of calls, one for each instruction in the log:
- .DS
- d1 >> RSB: code = STP, PI = uninit, PC = 0, LB = 2147483644, LIN = 0, FIL = NULL
- d1 >> RSB: code = CAL, PI = (0,0), PC = 16, LB = 2147483608, LIN = 0, FIL = NULL
- d1 >> AB = 2147483596, LB = 2147483572, SP = 2147483560, HP = 848, LIN = 4, FIL = "t.c"
- d1 >> RSB: code = STP, PI = uninit, PC = 0, LB = 2147483644, LIN = 0, FIL = NULL
- d1 >> RSB: code = CAL, PI = (0,0), PC = 16, LB = 2147483608, LIN = 0, FIL = NULL
- d1 >> AB = 2147483596, LB = 2147483572, SP = 2147483560, HP = 848, LIN = 4, FIL = "t.c"
- d1 >> RSB: code = STP, PI = uninit, PC = 0, LB = 2147483644, LIN = 0, FIL = NULL
- d1 >> RSB: code = CAL, PI = (0,0), PC = 16, LB = 2147483608, LIN = 0, FIL = NULL
- d1 >> AB = 2147483596, LB = 2147483572, SP = 2147483564, HP = 848, LIN = 4, FIL = "t.c"
- .DE
- Theoretically, the pertinent trace is the middle one, but in practice all three
- are equal. In the present case there isn't much to trace, but in real programs
- the trace can be useful.
- .sp
- .LP
- .I "Errors in libraries"
- .PP
- Since libraries are generally compiled with suppression of line number and
- file name information, the line number and file name in the interpreter will
- not be updated when it enters a library routine. Consequently, all messages
- generated by interpreting library routines will seem to originate from the
- line of the call. This is especially true for the routine malloc(), which,
- from the nature of its business, often contains dubitable code.
- .PP
- A usual message is:
- .DS
- (Warning 43, #1): Local integer expected at "buff.c", line 18, INR = 266
- (Warning 64, cont.): Actual memory contains a data pointer at "buff.c", line 18, INR = 266
- .DE
- and indeed at line 18 of the file buff.c we find:
- .DS
- buff = malloc(buff_size = BFSIZE);
- .DE
- This problem can be avoided by using a specially compiled version of the
- library that contains the correct LIN and FIL instructions, or, less
- elegantly, by including the source code of the library routines in the
- program; in the latter case, one has to be sure to have them all.
- .sp
- .LP
- .I "Unavoidable messages"
- .br
- Some messages produced by the logging are almost unavoidable; sometimes the
- writer of a library routine is forced to take liberties with the semantics of
- EM.
- .LP
- Examples from C include the memory allocation routines.
- For efficiency reasons, one bit of an pointer in the administration is used as
- a flag; setting, clearing and reading this bit requires bitwise operations on
- pointers, which gives the above messages.
- Realloc causes a problem in that it may have to copy the originally allocated
- area to a different place; this area may contain uninitialised bytes.
- .bp
- .DS
- .ft CW
- @x9 "t.c", line 4, INR = 16, PC = 30 OPCODE = 180
- @L6 "t.c", line 4, INR = 16, DoLOLm(-4)
- d2
- d2 . . STACK_DUMP[4/4] . . INR = 16 . . STACK_DUMP . .
- d2 ----------------------------------------------------------------
- d2 ADDRESS BYTE ITEM VALUE SHADOW
- d2 2147483643 0 (Dp)
- d2 2147483642 0 (Dp)
- d2 2147483641 0 (Dp)
- d2 2147483640 40 [ 40] (Dp)
- d2 2147483639 0 (Dp)
- d2 2147483638 0 (Dp)
- d2 2147483637 3 (Dp)
- d2 2147483636 64 [ 832] (Dp)
- d2 2147483635 0 (In)
- d2 2147483634 0 (In)
- d2 2147483633 0 (In)
- d2 2147483632 1 [ 1] (In)
- d1 >> RSB: code = STP, PI = uninit, PC = 0, LB = 2147483644, LIN = 0, FIL = NULL
- d2
- d2 ADDRESS BYTE ITEM VALUE SHADOW
- d2 2147483607 0 (Dp)
- d2 2147483606 0 (Dp)
- d2 2147483605 0 (Dp)
- d2 2147483604 40 [ 40] (Dp)
- d2 2147483603 0 (Dp)
- d2 2147483602 0 (Dp)
- d2 2147483601 3 (Dp)
- d2 2147483600 64 [ 832] (Dp)
- d2 2147483599 0 (In)
- d2 2147483598 0 (In)
- d2 2147483597 0 (In)
- d2 2147483596 1 [ 1] (In)
- d1 >> RSB: code = CAL, PI = (0,0), PC = 16, LB = 2147483608, LIN = 0, FIL = NULL
- d2
- d2 ADDRESS BYTE ITEM VALUE SHADOW
- d2 2147483571 undef
- d2 | | | | | |
- d2 2147483568 undef (1 word)
- d2 2147483567 0 (In)
- d2 2147483566 0 (In)
- d2 2147483565 3 (In)
- d2 2147483564 9 [ 777] (In)
- d2 2147483563 undef
- d2 | | | | | |
- d2 2147483560 undef (1 word)
- d2 FRA: size = 0, undefined
- d1 >> AB = 2147483596, LB = 2147483572, SP = 2147483560, HP = 848, \e
- LIN = 4, FIL = "t.c"
- d2 ----------------------------------------------------------------
- d2
- @x9 "t.c", line 4, INR = 17, PC = 31 OPCODE = 169
- @w1 "t.c", line 4, INR = 17, warning: Local data pointer expected [stack.c: 242]
- @w1 "t.c", line 4, INR = 17, warning cont.: Actual memory is undefined
- @L6 "t.c", line 4, INR = 17, DoLOIm(4)
- d2
- d2 . . STACK_DUMP[4/4] . . INR = 17 . . STACK_DUMP . .
- d2 ----------------------------------------------------------------
- d2 ADDRESS BYTE ITEM VALUE SHADOW
- d2 2147483643 0 (Dp)
- d2 2147483642 0 (Dp)
- d2 2147483641 0 (Dp)
- d2 2147483640 40 [ 40] (Dp)
- d2 2147483639 0 (Dp)
- d2 2147483638 0 (Dp)
- d2 2147483637 3 (Dp)
- d2 2147483636 64 [ 832] (Dp)
- d2 2147483635 0 (In)
- d2 2147483634 0 (In)
- d2 2147483633 0 (In)
- d2 2147483632 1 [ 1] (In)
- d1 >> RSB: code = STP, PI = uninit, PC = 0, LB = 2147483644, LIN = 0, FIL = NULL
- d2
- d2 ADDRESS BYTE ITEM VALUE SHADOW
- d2 2147483607 0 (Dp)
- d2 2147483606 0 (Dp)
- d2 2147483605 0 (Dp)
- d2 2147483604 40 [ 40] (Dp)
- d2 2147483603 0 (Dp)
- d2 2147483602 0 (Dp)
- d2 2147483601 3 (Dp)
- d2 2147483600 64 [ 832] (Dp)
- d2 2147483599 0 (In)
- d2 2147483598 0 (In)
- d2 2147483597 0 (In)
- d2 2147483596 1 [ 1] (In)
- d1 >> RSB: code = CAL, PI = (0,0), PC = 16, LB = 2147483608, LIN = 0, FIL = NULL
- d2
- d2 ADDRESS BYTE ITEM VALUE SHADOW
- d2 2147483571 undef
- d2 | | | | | |
- d2 2147483568 undef (1 word)
- d2 2147483567 0 (In)
- d2 2147483566 0 (In)
- d2 2147483565 3 (In)
- d2 2147483564 9 [ 777] (In)
- d2 2147483563 0 (In)
- d2 2147483562 0 (In)
- d2 2147483561 0 (In)
- d2 2147483560 4 [ 4] (In)
- d2 FRA: size = 0, undefined
- d1 >> AB = 2147483596, LB = 2147483572, SP = 2147483560, HP = 848, \e
- LIN = 4, FIL = "t.c"
- d2 ----------------------------------------------------------------
- d2
- @x9 "t.c", line 4, INR = 18, PC = 32 OPCODE = 229
- @S6 "t.c", line 4, INR = 18, DoSTLm(-8)
- d2
- d2 . . STACK_DUMP[4/4] . . INR = 18 . . STACK_DUMP . .
- d2 ----------------------------------------------------------------
- d2 ADDRESS BYTE ITEM VALUE SHADOW
- d2 2147483643 0 (Dp)
- d2 2147483642 0 (Dp)
- d2 2147483641 0 (Dp)
- d2 2147483640 40 [ 40] (Dp)
- d2 2147483639 0 (Dp)
- d2 2147483638 0 (Dp)
- d2 2147483637 3 (Dp)
- d2 2147483636 64 [ 832] (Dp)
- d2 2147483635 0 (In)
- d2 2147483634 0 (In)
- d2 2147483633 0 (In)
- d2 2147483632 1 [ 1] (In)
- d1 >> RSB: code = STP, PI = uninit, PC = 0, LB = 2147483644, LIN = 0, FIL = NULL
- d2
- d2 ADDRESS BYTE ITEM VALUE SHADOW
- d2 2147483607 0 (Dp)
- d2 2147483606 0 (Dp)
- d2 2147483605 0 (Dp)
- d2 2147483604 40 [ 40] (Dp)
- d2 2147483603 0 (Dp)
- d2 2147483602 0 (Dp)
- d2 2147483601 3 (Dp)
- d2 2147483600 64 [ 832] (Dp)
- d2 2147483599 0 (In)
- d2 2147483598 0 (In)
- d2 2147483597 0 (In)
- d2 2147483596 1 [ 1] (In)
- d1 >> RSB: code = CAL, PI = (0,0), PC = 16, LB = 2147483608, LIN = 0, FIL = NULL
- d2
- d2 ADDRESS BYTE ITEM VALUE SHADOW
- d2 2147483571 undef
- d2 | | | | | |
- d2 2147483568 undef (1 word)
- d2 2147483567 0 (In)
- d2 2147483566 0 (In)
- d2 2147483565 0 (In)
- d2 2147483564 4 [ 4] (In)
- d2 FRA: size = 0, undefined
- d1 >> AB = 2147483596, LB = 2147483572, SP = 2147483564, HP = 848, \e
- LIN = 4, FIL = "t.c"
- d2 ----------------------------------------------------------------
- d2
- .DE
|