1 The Debugger

When a run-time error occurs, the symbolic debugger uses the information in the various modules' reference files to interpret the data structures in memory. The debugger shows the names of all linked modules, the dynamic procedure-call stack, the modules' global variables and the active procedures' local variables, and also a module's source text (if available) in various windows. In yet another window you can directly inspect memory. The data windows are also used to change the value of variables.

The debugger is activated in the following cases:

1.1 Installing The Debugger In a Program

If you want to use the Modula-2 source-level debugger, you have to replace "libm2lib.a" by "libm2dlib.a" for tools. For applications (libm2libforeigen.a) there is actually no debugger available.

1.2 Data For The Debugger

The debugger needs access not only to main memory but also to some additional information on how to interpret this data. It gets this information from various files which are nearly all generated by the compiler.

The reference file:
In order to analyze the data fully, reference files must exist for each of the modules to be examined. A reference file for a module is generated by the compiler if the option "-REFERENCE" is specified (see "Compiler", section 1.2).
The debugger expects this file to have the name "<module name>.MREF".

The symbol file:
Since the format of a symbol file is the same as that of a reference file, the debugger can use a module's symbol file instead its reference file if no reference file is available. In such cases however, only entry routines and exported variables can be interpreted. This feature is particularly handy if an error occurs in a module for which you do not have a reference file. You can access information specific to a procedure-call in a library module in this way for example, so as to see if there is an error in the actual parameters.

The source file:
The debugger can associate the program counter with a line of source code. It therefore also opens the IMPLEMENTATION MODULE's source file. If generic modules are refined additionally all generic sources are opened, more than once if more refinements of the same generic module is done. All source files are displayed as one large source file (alike the listing file). Note that only the actual original source can be worked on in this way—the listing file cannot be used.
The debugger expects this file to have the name "<module name>.MOD" respectively "<module name>.GMO".

The path file:
The path file is a text file created by the programmer. It contains the names of all the folders in which the debugger should look for the above files. Each line of this file starts with an identifying letter. Immediately after this letter follows the name of exactly one folder. The initial letter indicates what kind of file should be looked for in the folder specified: "R" means reference and symbol files, and "M" means source files (with the extension ".MOD" / ".GMO"). The folder names must not be enclosed in quotation marks even if they contain special characters. Furthermore each folder name must end in a slash. The debugger looks for the path file under the name "Debug.DAT" in the current working folder. It does this when it is first activated.
A path in the path file may be specified in two ways. If the path starts with a slash, it is treated as an absolute path. Otherwise it is interpreted as a path relative to the location where the path file resides. This is useful especially for applications where the current working directory is "/".

Example:

M/System/User/Wiedemann/Entwicklung/M2/Library/RTS/
M/System/User/Wiedemann/Entwicklung/M2/Library/StdLib/
R/System/User/Wiedemann/Entwicklung/M2/Library/REFs/
R./Compiler/Ref/
M./Compiler/Common/
M./Compiler/Pass1/
M./Compiler/Pass2/
M./Compiler/Pass3/
M./Compiler/Pass4/
M./Compiler/Pass4a/
M./Compiler/Pass4x/
M./Compiler/Pass4C/
M./Compiler/Listing/
M./Compiler/Symfiles/
Notes:
1. The debugger looks through the paths specified in reverse order of their appearance in the file, meaning that for example in the above example "./Compiler/Ref/" is inspected before "/System/User/Wiedemann/Entwicklung/M2/Library/REFs/".
2. In addition to the given paths, also the paths extended by a subfolder denoted by the specific architecture (a64, x86) are looked for reference files. Thus it is possible to use one path file for multiple architectures.

1.3 Activation

Though the reasons for activating the debugger are equal for tools and applications, the mechanism for entering the debugger is slightly different.

1.3.1 Activation for Applications

ATTENTION: In 10.0 the debugger is not available for applictions.

1.3.2 Activation for Command Line Tools

For command line tools Modula-2's symbolic debugger is activated on each exception or on a call to BREAK. First, the debugger's seven windows appear as virtual parts of the terminal's window. The window is always fully used, i.e. if this window is as large as the screen, the maximum of information can be displayed. You should not change the size of this window after the debugger is started. First the debugger tries to read the file "Debug.DAT" in the current working directory and interprets it as path file. If the file "Debug.DAT" does not exist in this place, a question is displayed on the screen either to give the path and name of the default file or to abort search. In this case, the debugger is only able to look in the current directory for necessary files.

If a file (either source or reference file) cannot be found even with the help of the path file, a message appears in the terminal window and allows either to specify path and name for this file or to abort.

1.4 The Various Windows

The debugger displays information in seven different windows.
For tools the terminal window ist virtually split into seven parts that show all windows simultaneously. If questions need to be displayed, the upper part of the terminal window is used for this purpose. After the answer ist confirmed or cancelled, the window contents is restored.

By selecting an item in one of the various windows and then choosing an appropriate menu command or by double-clicking a selectable item (applications) resp. pressing the right arrow key (tools), information corresponding to that item is displayed in the other windows. Columns in the windows adapt in width automatically according to the size of the window. If a window is too narrow to display all the data in full, names are truncated where necessary, the missing parts being indicated by ellipses.

The up and down arrow keys may be used for scrolling in all windows (if applicable).
For tools, the active window is highlighted (background color yellow, foreground color blue). The selected line is displayed with cyan background and red foreground. Besides using the menu, the active window may be changed by using the <TAB> key.

In what follows the function of the windows and their interdependencies are described.

An example debugger-window arrangement:

screen layout for tools

1.4.1 The Modules Window

The names of all the modules linked into the program are listed in this window. At the right of each module name the starting address of the global data is shown in hexadecimal.

For tools, each line can be selected by pressing up or down arrow. For the active line, the commands "Open Source for <module name>", "Open Data_1 for <module name>" and "Open Data_2 for <module name>" appear in the "Windows" menu. Selecting one of these commands will display the source code or variables of the chosen module in the relevant window accordingly. The other windows remain unchanged. Pressing the right arrow key fills the two windows "Source" and "Data_2" with applicable information for the module in the active line.
The command "Display at $<address>" can be used to display the corresponding part of memory displayed in the Memory window.

The modules list ist sorted alphabetically. Typing a key selects the first module with the given starting character.

1.4.2 The Procedures Window

This window shows the dynamic procedure-call chain at the time of the interrupt. Initialization parts or modules are signified by "(init)" instead of a procedure name. The top entry in the list is where the interrupt occurred. To the right of each procedure name is shown the address (relative to the start of the procedure) of the next processor instruction to be executed in this procedure. Further right the base address of the local variables and the base address of the parameter area are shown.

For tools, each line can be selected by up and down arrow. For the active line, the commands "Open Source for <module name>", "Open Data_1 for <procedure name>" and "Open Data_2 for <procedure name>" appear in the "Windows" menu analogous to when a module name is selected. After choosing one of these menu items the source code or variables of this procedure are displayed in the respective window accordingly. Pressing the right arrow key fills in the two windows "Source" and "Data_1" (not "Data_2"!) with the relevant information for the procedure in the active line.

Opening a variable of type "COROUTINE" (by double click resp. right arrow key) switches the display to showing the procedure-call chain associated with the coroutine referred to by this variable.

1.4.3 The Two Data Windows

For displaying variables two windows of identical functionality are available: "Data_1" and "Data_2".

Activating a procedure name will display the procedure's local variables in "Data_1"; activating a module name will display the module's global variables in "Data_2".

To the right of each variable (or field) name is shown the following:

An asterisk ("*") between the type and (in such a case) size displays indicates that the variable is either structured or a pointer. Pressing the right arrow key if the line of a such a variable is selected displays the individual fields or elements of the variable, or in the case of a pointer variable dereferences it.

Opening a variable of type "COROUTINE" (by double click resp. right arrow key) switches the procedure window's display to showing the procedure-call chain associated with the coroutine referred to by this variable.

The value of a non structured variable may be changed by double clicking on it (for applications) or pressing the right arrow key (for tools) (cf. 1.5.4 Options menu).

Address values and values of system types are shown in hexadecimal, the values of all other types according to their types.

In the line of text immediately below each Data window's title bar is shown the name of the procedure/module or variable/field/element to which the variables or fields/elements respectively visible in the rest of the window actually belong. This name can become extremely long, in particular for example when examining a linked list (in which the dereferencing of pointer variables occurs repeatedly).
For tools, the left arrow key is used for stepping back the list.

For tools, the memory area of a variable may be displayed in the Memory window by choosing the menu item "Display at $<address>".

The value of a variable may also be changed by selecting the address of a variable and then choosing the menu item "Set value at <address>…" (Options menu). This is used especially for changing pointer variables, as double clicking / right arrow on a pointer variable will dereference it.

1.4.4 The Source Window

This window displays a section of source code. Which section that actually is depends on which procedure or module name was last double-clicked or selected when the menu item "Open Source for <module name>" was chosen (see the discussions of the Modules and the Procedures windows). The statement last executed in the procedure last selected in this way is highlighted.

1.4.5 The Memory Window

Any section of main memory desired can be displayed in this window. Each line starts with the address (in hexadecimal) of the first memory cell shown on the line and is followed by the contents of several bytes of memory. The number of bytes displayed depends on the size of the window and the display format. The first address shown in the window is always set to a multiple of the number of bytes displayed in a line.

What section of memory is shown can be regulated by double-clicks / selection in the Modules, Procedures, Data_1 and Data_2 windows, as well as by the menu items "Address" and "Display Selection" from the "Memory" menu and of course by the use of the scroll bar. The remaining items in the "Memory" menu can be used to choose how it should be displayed.

In all display formats except "Ascii", "Instruction" and the floating point formats eight consecutive bytes of memory contents can be selected as an address for the "Display Selection" command or for double clicking. This is particularly convenient when dealing with pointers. This selection may also be used to change memory locations by "Set value at <address> …". The selection is always interpreted as an "ADDRESS", the input is required for eight bytes in hexadecimal notation.

It is up to the user to ensure that a reasonable value is specified for the start address of the memory to be displayed. If the address entered falls outside the range of the memory actually available or addresses a memory mapped I/O device then only zeros are displayed. It is therefore recommended that the section of memory to be examined only be selected by double clicking on an address in the Modules or Procedures window or one of the Data windows

1.4.6 The Register Window

This window (for applications normally in background) shows the contents of the processor registers.

1.4.6.1 The Arm Registers

The first two lines show the status registers:
PCProgram counter
CSPRMachine state and control register
TrapNumber of arm exception taken
ErrException syndrome
AddrVirtual fault address
FPSRFloating point state register
FPCRFloating Point control register
They are followed by the 32 integer registers x0 .. r28, FP, LR, SP. According to the programming conventions, the following registers have special meaning:
SPStack pointer
LRLink register, also x30
FPFrame pointer, also x29.
Last part are the 32 fpu registers v0 .. v31. Note: The values in the control registers are meaningful only if the debugger was invoked by an exception situation. This is not true for a call to "BREAK" or "RAISE".

1.4.6.2 The Intel 64-bit Registers

The first two lines show the status registers:
RIPInstruction Pointer
FLAGFlag Register
TrapTrap Number
ErrError Number
AddrFaulting Address
FPCONTFloating Point Control Register
FPSTATUSFloating Point State Register
FPTAGFloating Point Tag Register
They are followed by the 16 integer registers rax .. r15. According to the programming conventions, the following registers have special meaning:
SPStack Pointer, also rsp
FPFrame Pointer, also rbp
r13Static Link, if an inner procedure accesses the variables of surrounding procedures
r14SELF Pointer in Methods and their inner procedures.
r15Global Pointer, if a procedures accesses global variables of its module.
Note: The values in the integer registers (except SP and FP) are meaningful only if the debugger was invoked by an exception situation. This is not true for a call to "BREAK" or "RAISE".

These lines are followed by the old FPU registers st0 through st7 used for LONGDOUBLE arithmetic and the xmm-registers xmm0 through xmm7 used for REAL and LONGERAL arithmetic.

1.5 The Menus

For applications while the debugger is active the program's menu bar is replaced by that of the debugger.

For tools a special menu bar mechanism is available. The menu bar is shown in the first line of the terminal window. Each menu may be accessed by holding down the <CTRL>-key and pressing the underlined character. The menu drops down. Navigation ist done by the four arrow keys. Pressing the <ENTER>-key chooses the selected menu item.
Menu shortcuts are activated by holding down <SHIFT> and pressing the shortcut key.
The windows behind the menus are restored after choosing a menu item.
Please note that not all menu items are applicable when debugging a command line tool.

The menu commands made available in this way are described briefly below.

1.5.1 The File Menu

The file menu contains commands for leaving the debugger.

Continue Application/Tool
This command allows resumption of execution after a breakpoint or a minor error.

Terminate
Has the same effect as a call to "HALT" at this point. Modula-2's termination process is started or continued (cf. "Compiler", section 2.6). This is the menu item that should normally be used to quit a program from the debugger.

Exception Handler
Continues the program with the currently active exception handler (cf. "Compiler" section 2.7). This item is only available if the debugger was triggered by a genuine exception condition (which does not include calls to "BREAK", breakpoints or "single steps").

Exit Application
This command can be used to end the program.
This "emergency exit" should only be used if ending the program with "Terminate" no longer seems advisable because of inconsistent data.

1.5.2 The Windows Menu

Tile Windows
Displays the six main windows on the screen, restoring them to their standard tiled arrangement if necessary. The register window is placed behind the memory window.

Stack Windows
Displays all windows, arranging them staggered on the screen in a stack.

The remaining commands are for opening and activating the various windows individually. They can also be used to reassign the contents of the Source window and the Data windows.

1.5.3 The Memory Menu

Address …
This command allows a start address for the memory window to be entered manually.

Display Selection / at $…
A selected address can be made the start address using this command.

The remaining commands are for setting the memory window's display format.

1.5.4 The Options Menu

NOTE: Not all of the options below are available for tools.

Keep Data
If this menu item is marked with a "√" the debugger's internal data (symbol table etc.) is maintained between consecutive activations. This accelerates subsequent debugger calls but needs some extra memory.

Call Options …
This puts up a dialog in which the conditions under which the debugger should be called can be specified.

In the upper part of the window the exception conditions can be selected for which the debugger should be activated before an exception handler is called. Default: all except user-defined.

The lower pair of radio buttons are for specifying whether the debugger should be activated (again) if an exception was not handled by any exception handler at all. The default is that it should not.

If the check box "skip dialog" is marked the debugger is activated at breakpoints directly, without intervening error dialog. This is convenient if the debugger is to be used at every breakpoint. Default: off (i.e. dialog not skipped).

Delete All Breaks
Clears all breakpoints.

Show Next Break
Brings the next breakpoint into view in the Source window. By issuing this command repeatedly all breakpoints set can be displayed one after the other.

Crashed Process
Displays in the debugger the procedure-call chain of the coroutine which caused the debugger activation. This makes it possible when analyzing parallel coroutines to switch back to the active coroutine even if its process descriptor is not stored in any variable.

Main Process
Displays in the debugger the procedure-call chain of the main program.

Set Value at …
This item allows to change the value of a selected none structured variable (data windows) or of a selected memory location (memory window).

A dialog for input of the new value pops up; for enumeration types a list of all possible values is displayed to select from. Input is checked for correctness (e. g. format, range), wrong input is rejected. For subrange types an additional range check is performed. If this check fails, the value may be set nevertheless, but is truncated to the available data space.

For SET variables, short sets (up to four bytes) may be changed by one input, longer sets may be changed byte by byte (as they are displayed in the data windows). Structured variables may be changed element by element.

table of contents (utilities) start page chapter 2 (utilities)