The ModulaTor logo 7KB

The ModulaTor

Oberon-2 and Modula-2 Technical Publication

Ubaye's First Independent Modula-2 & Oberon-2 Journal! Nr. 6..7, Jul..Aug 1996

The AlphaOberon Run-Time Debugger

by Günter Dotzel and Hartmut Goebel, ModulaWare

Besides the conventional symbolic back-trace and post-mortem dump of the Oberon System, AlphaOberon now features an integrated run-time debugger tool called Debug.

Debug, originally developed by Markus Hof at University Linz/Austria, fits well into the Oberon System. Debug is a tool which runs as an ordinary Oberon application and does not impose additional load on the system or compiler. Programs executed under debugger control run at full speed.

When an application is started under debugger control (Debug.Trace module), Debug loads the module and all indirectly imported modules and executes their module bodies in order to initialise the modules. When any command of the module is activated (module.procedure), the procedure stops at the first executable statement.

Debug then displays the module source code in a conventional Oberon text window. If there is already a text window with the source, it is re-used, even if it was opened with a text-editor. Debug shows the program counter position as PC- and break-points as BP-markers in the source window. You can directly make modifications in the source window, recompile and execute the module under the control of the debugger. This process is very fast, because no linking is required in the Oberon System.

The debugger executes the statements of a single source line with the commands Debug.StepInto or Debug.Step or runs until a break-point is discovered (Debug.Run).

To set a break-point, move the caret to the desired source line and execute the command Debug.BreakPoint. Pop-up menus are provided for all Debug commands for your convenience.

Data Inspection and Display Concept

Debug uses the Oberon hyper-text element Folds for displaying data. The folds concept, which is also suitable for literate programming, allows to control the debugger operations without introducing a debugger command language. To display the data, the debugger opens a text window. The contents of all simple variables are listed for the scope selected.

Fold elements indicate structured types. Its content is expanded or re-folded by clicking at the fold marker (triangle). This allows to examine variables of structured type selectively by simple mouse clicks: arrays, multi-dimensional open and dynamic arrays, records with their proper dynamic type (type name of record extension) as well as pointers and linked lists. This makes inspection of heterogeneous lists very easy.

AlphaOberon specific implementation

In contrast to the Linz implementation of Debug, where the Oberon compiler is called automatically during debugging to produce the symbol table for each module being debugged, AlphaOberon puts the debugger symbol table into the Oberon load files. The additional time and space required is marginal.

Furthermore, coroutines are used for the task switch, i.e.: to transfer from Debug to the debugged application, while Linz uses a low-level code with register juggling to save and restore the contexts. The exception handler of Debug is completely written in Oberon using the OpenVMS exception handling facility.

The garbage collector of AlphaOberon was extended to work with multiple stack-frames to support coroutines.

The general control structure of Debug

Debug.Trace loads the module(s) to be debugged with the standard Oberon loader. Debug reads the symbol table and source line/instruction correspondance table from the Oberon load file. Then the code of this module is patched so the beginning of the first executable statement of each procedure will raise a break-point exception when executed. The contents of the patched locations is saved. Control returns to the general command dispatcher of the system Oberon.Loop.

When the debugger is activated, it installs a global exception handler and creates a second process. How a trap handler is installed and how traps are processed in Oberon under OpenVMS is shown in the module Unwind below.

When a command of a debugged module is invoked, the procedure's entry code is executed and the break-point raises an exception. This exception is fetched by the debugger's trap handler. The trap handler scans the stack-frames using OpenVMS system services in order to locate Oberon procedures in the call chain. Control is transfered to the second process, where Oberon.Loop is called. This means, that all commands will be run in this second process until the command under debug terminates. So to speak, there is no debugging mode, because you can still activate any Oberon command or edit any text and later continue debugging.

When you activate Debug.StepInto, Debug patches the actual routine of the debugged module, so that a break-point is set at the beginning of each source line. Control is transfered back to the main process, where the trap handler is still active. The original instruction of the patched location is restored. The trap handler terminates and returns to the break-point location. Execution continues until it reaches the next break-point or an exception is risen.

Debug.Close removes the trap-handler and the second process.

Exception handler illustration

Module Unwind listed below together with the result of execution, imports VMS$Exceptions which itself needs VMSExceptions. The former is a foreign interface module (as indicated by the module's name and the latter is a regular module so that the record types get a type descriptor. Both modules contain declarations only and are listed below. Other modules imported are foreign interface modules for the OpenVMS standard run-time library, system services and status codes.

MODULE Unwind; (* Copyright (1996-1997) Guenter Dotzel, Email: [email deleted due to spam] This module illustrates the system service $GOTO_UNWIND which is used in the AlphaOberon run-time debugger Debug. The specification of the OpenVMS condition handling facility can be found in the "OpenVMS Programming Documents". This module is OpenVMS Alpha specific, because the system service $GOTO_UNWIND does not exist in OpenVMS VAX. 1) Module Unwind installs a skeleton trap handler in trap handling vector 0. 2) An exception (division by 0) is raised to invoke the handler 3) The handler performs some required tasks and calls a dummy code segment via SYS$GOTO_UNWIND. The dummy code segment is filled with only one instruction: HALT (= 0). The code segment is passed to $GOTO_UNWIND in order to execute it. The program then immediately traps again with the error %SYSTEM-F-OPCDEC. This exception is fetched by the same trap handler. The trap recursion is stopped with level 2 and the exception is resignaled to the next handler. This is either the trace back handler, the last chance handler or the debuggers exception handler. *) IMPORT SYS := SYSTEM, EX := VMS$Exceptions, VMS := VMS$, lib:=Lib$; TYPE (* OpenVMS types *) Invo_Context = EX.InvoContext; Invo_Handle = EX.InvoHandle; SigArgs = EX.SigArgs; MechArgs = EX.MechArgs; (* own types *) ADDRESS64 = SYS.SIGNED_64; ExceptionHandler = EX.ExceptionHandler; OneInstruction = ARRAY 1 OF LONGINT; PROCEDURE Do*; VAR currDbgHandle: Invo_Handle; oldTrap: ExceptionHandler; codeseg: OneInstruction; i, trapLevel: INTEGER; PROCEDURE Trap (VAR sigArgs: SigArgs; mechArgs: MechArgs): LONGINT; (* TRAP handler *) VAR currDbgCntxt: Invo_Context; (* current context of debugged program.*) BEGIN (* OpenVMS AXP calling conventions: %IMMED mechArgs is in R17 *) SYS.GETREG(17, mechArgs); (* first output our signal *) IF lib.LIB$PUT_OUTPUT("handler called") = 0 THEN END; IF VMS.SYS$PUTMSG(SYS.ADR(sigArgs), 0, NIL,0) = 0 THEN END; INC(trapLevel); IF trapLevel >= 2 THEN IF lib.LIB$PUT_OUTPUT("resignal") = 0 THEN END; RETURN EX.SS$_RESIGNAL; (* stop recursion and resignal *) END; EX.LIB$GET_CURRENT_INVO_CONTEXT(currDbgCntxt); (* skip trap handler context and all procedure contexts ** belonging to the condition handling facility (CHF) *) WHILE ODD(EX.LIB$GET_PREV_INVO_CONTEXT(currDbgCntxt)) & (EX.LIBICB$V_EXCEPTION_FRAME IN currDbgCntxt.flags) DO END; (* now we have the context of the failed procedure = module body *) currDbgHandle := EX.LIB$GET_INVO_HANDLE(currDbgCntxt); IF lib.LIB$PUT_OUTPUT("okay, starting to skip") = 0 THEN END; (* now we unwind to the context of the failed proc and jump to the ** position behind the trap through the dummy codesegment ** -> in this demo, this should raise a %SYSTEM-F-OPCDEC exception *) RETURN EX.SYS$GOTO_UNWIND(currDbgHandle, SYS.SHORT(SYS.ADR(codeseg[0])), NIL, NIL); END Trap; BEGIN trapLevel := 0; IF ODD( EX.SYS$SETEXV(0, SYS.VAL(ExceptionHandler, Trap), 0, NIL) ) THEN codeseg[0] := 0; (* HALT: enforce illegal instruction *) i := 0; i := 25 DIV i; (* or raise any other exception *) IF lib.LIB$PUT_OUTPUT("this message should never occur") = 0 THEN END; ELSE IF lib.LIB$PUT_OUTPUT("not installed") = 0 THEN END; END; END Do; BEGIN Do; END Unwind.
handler called %DIVMODERR, divisor is zero (or negative in case of DIV or MOD) okay, starting to skip handler called %SYSTEM-F-OPCDEC, opcode reserved to Digital fault at PC=000000007AFB79CC, PS=0000001B resignal %SYSTEM-F-OPCDEC, opcode reserved to Digital fault at PC=000000007AFB79CC, PS=0000001B %TRACE-F-TRACEBACK, symbolic stack dump follows image module routine line rel PC abs PC 0 0000000000000000 000000007AFB79CC UNWIND UNWIND DO ? ? UNWIND UNWIND UNWIND 101 00000000000005C0 00000000000305C0 0 FFFFFFFF81CD70D8 FFFFFFFF81CD70D8
MODULE VMS$Exceptions; (* Copyright (1996-1997) Guenter Dotzel, Email: [email deleted due to spam] A2O version of MaX's foreign definition module VMS_Exceptions GD/18-Aug-1995, hG/22-Aug-1995: corrected for AXP hG/19-Jun-1996: moved record type defs into VMSExceptions.def *) IMPORT SYSTEM, SS := SS$Definitions, PSL := PSL$Definitions, EX := VMSExceptions; TYPE LONGINT = SYSTEM.SIGNED_32; (* PP *) ADDRESS *= LONGINT; ADDRESS64*= SYSTEM.SIGNED_64; INTEGER64*= SYSTEM.SIGNED_64; SigArgs *= ARRAY 257 OF LONGINT; CONST (* NB. the SIGARGS() macro starts at 1 for sigArgs, and 2 for sigName. * But these here are indixes for the array *) sigArgs *= 0; (* n = Additional Longwords *) sigName *= 1; (*sigPC *= n-1*) (*sigPSL*= n *) TYPE MechArgs *= EX.MechArgs; SigArgs64*= EX.SigArgs64; CONST savR0 *=0; savR1 *=1; savR16*=2; savR17*=3; savR18*=4; savR19*=5; savR20*=6; savR21*=7; savR22*=8; savR23*=9; savR24*=10; savR25*=11; savR26*=12; savR27*=13; savR28*=14; savF0 *=0; savF1 *=1; savF10*=2; savF11*=3; savF12*=4; savF13*=5; savF14*=6; savF15*=7; savF16*=8; savF17*=9; savF18*=10; savF19*=11; savF20*=12; savF21*=13; savF22*=14; savF23*=15; savF24*=16; savF25*=17; savF26*=18; savF27*=19; savF28*=20; savF29*=21; savF30*=22; (* todo: add Continue_64 and Resignal_64 *) SS$_RESIGNAL * = SS.SS$_RESIGNAL; SS$_CONTINUE * = SS.SS$_CONTINUE; SS$_UNWIND * = SS.SS$_UNWIND; SS$_SIGNAL64 * = SS.SS$_SIGNAL64; SS$_RESIGNAL_64 * = SS.SS$_RESIGNAL_64; SS$_CONTINUE_64 * = SS.SS$_CONTINUE_64; PSL$C_USER *= PSL.PSL$C_USER; TYPE ExceptionHandler * = PROCEDURE(VAR sigargs: SigArgs; mechArgs$I: MechArgs): LONGINT; (* Oberon does not allow to declare $I parameters in non-foreign procedures *) OberonExceptionHandler * = PROCEDURE(VAR sigargs: SigArgs; mechArgs: MechArgs): LONGINT; (*--- call chain stuff ---*) CONST LIBICB$V_EXCEPTION_FRAME *= 0; LIBICB$V_AST_FRAME *= 1; LIBICB$V_BOTTOM_OF_STACK *= 2; LIBICB$V_BASE_FRAME *= 3; LIBICB$K_INVO_HANDLE_SIZE *= 4; TYPE InvoContext *= EX.InvoContext; Invo_Context *= InvoContext; (* VMS style aliases *) Invo_Context_Blk *= InvoContext; HANDLE *= LONGINT; (* invocation handle, 4 Bytes *) InvoHandle *= HANDLE; Invo_Handle *= HANDLE; CONST LIB$K_INVO_HANDLE_NULL *= 0; PROCEDURE LIB$GET_INVO_CONTEXT *( invoHandle$I: InvoHandle; VAR invoContext$N: InvoContext): BOOLEAN; END LIB$GET_INVO_CONTEXT; PROCEDURE LIB$GET_CURRENT_INVO_CONTEXT *( VAR invoContext$N: InvoContext); END LIB$GET_CURRENT_INVO_CONTEXT; PROCEDURE LIB$GET_PREV_INVO_CONTEXT *( VAR invoContext$N: InvoContext): LONGINT; END LIB$GET_PREV_INVO_CONTEXT; PROCEDURE LIB$GET_INVO_HANDLE *( invoContext: InvoContext): InvoHandle; END LIB$GET_INVO_HANDLE; PROCEDURE LIB$GET_PREV_INVO_HANDLE *( invoHandle$I: InvoHandle): InvoHandle; END LIB$GET_PREV_INVO_HANDLE; PROCEDURE LIB$SIG_TO_RET* ( VAR sigargs: SigArgs; mechArgs: MechArgs): LONGINT; END LIB$SIG_TO_RET; PROCEDURE LIB$SIGNAL* (signal$I: LONGINT); END LIB$SIGNAL; PROCEDURE SYS$UNWIND* ( depadr$O: LONGINT; newpc$I: ADDRESS): LONGINT; END SYS$UNWIND; PROCEDURE SYS$GOTO_UNWIND* ( targetInvo: InvoHandle; targetPC$O: ADDRESS; newR0$O, newR1$O: INTEGER64): LONGINT; END SYS$GOTO_UNWIND; PROCEDURE SYS$PUTMSG* ( msgvec$I: ADDRESS64; actrtn$I: ADDRESS64; facnam$S: ARRAY OF CHAR; (* 32 or 64Bit Stringdescriptor *) actprm$I: LONGINT): LONGINT; END SYS$PUTMSG; PROCEDURE SYS$SETEXV* ( vector$I: LONGINT; newhnd$I: ExceptionHandler; acmode$I: LONGINT; VAR prvhnd$O: ExceptionHandler): LONGINT; END SYS$SETEXV; END VMS$Exceptions.
MODULE VMSExceptions; (* Copyright (1996) Guenter Dotzel, Email: [email deleted due to spam] hG/19-Jun-1996: Record declarations for VMS$Exceptions *) IMPORT SYSTEM; TYPE LONGINT = SYSTEM.SIGNED_32; ADDRESS *= LONGINT; ADDRESS64*= SYSTEM.SIGNED_64; INTEGER64*= SYSTEM.SIGNED_64; MechArgs *= POINTER TO RECORD args *: LONGINT; flags *: SET; frame *: ADDRESS64; depth *: LONGINT; resvdf1 *: LONGINT; dAddr *: ADDRESS64; esfAddr *: ADDRESS64; sigAddr *: ADDRESS64; savR *: ARRAY 15 OF SYSTEM.QUADWORD;(*use indices below for access*) savF *: ARRAY 23 OF SYSTEM.QUADWORD;(*use indices below for access*) sig64Addr *: ADDRESS64; END; SigArgs64* = POINTER TO RECORD noArgs*, ss_signal64* :LONGINT; (* = SS$SIGNAL64 *) args* :ARRAY 257 OF INTEGER64; END; CONST (* LIBICB$K_INVO_CONTEXT_BLK_SIZE: *) LIBICB_K_INVO_CONTEXT_BLK_SIZE = 544; TYPE InnerInvoContext = RECORD length *: LONGINT; flags *: SET; (* upper 8 bits are version byte *) procDesc *: ADDRESS64; progCnt *: ADDRESS64; procStatus*: SYSTEM.QUADWORD; (* 64 bit flag field *) iReg *: ARRAY 31 OF SYSTEM.SIGNED_64; fReg *: ARRAY 31 OF SYSTEM.QUADWORD; END; InvoContext *= RECORD (InnerInvoContext) system: ARRAY LIBICB_K_INVO_CONTEXT_BLK_SIZE - SIZE(InnerInvoContext) OF SYSTEM.BYTE; END; END VMSExceptions.

The ModulaTor Forum

Recommended Reading

Frederic Bastiat: "Selected Essays on Political Economy", The Foundation for Economic Education, Irvington-on-Hudson, NY, 10533 (USA), 1964 (translated from French by Seymour Cain). 352 pages. (Unfortunately, this book does not have an ISBN. Try to get a copy of this book at Laissez Faire Books)
This book contains the essay "The Law", which was already recommended in the ModulaTor Forum, issue Sep-95. Bastiat's essays were first published a hundred-fifty years ago, in 1850! Really fundamental. A quote from Frederic's article "The State": "The state is the great fictitious entity by which everyone seeks to live at the expense of everyone else."

IMPRESSUM: The ModulaTor is an unrefereed journal. Technical papers are to be taken as working papers and personal rather than organizational statements. Items are printed at the discretion of the Editor based upon his judgement on the interest and relevancy to the readership. Letters, announcements, and other items of professional interest are selected on the same basis. Office of publication: The Editor of The ModulaTor is Günter Dotzel; he can be reached by tel/fax: [removed due to abuse] or by mailto:[email deleted due to spam]

Home | Site_index | Legal | OpenVMS_compiler | Alpha_Oberon_System | ModulaTor | Bibliography | Oberon[-2]_links | Modula-2_links | [3KB] [Any browser]

Books Music Video Enter keywords... logo

Webdesign by, 07-Dec-1998