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 source window is also used for setting and removing breakpoints, the data windows 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 resp. "libm2libcarbon.a" by "libm2dlibcarbon.a" for applications.
Note: As the Carbon lib is not available for x86_64 architectures, "libm2dlibcarbon.a" supports only for ppc and i386; "libm2dlib.a" support for ppc, i386, and x86_64.

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). The setting and clearing of breakpoints is done in this program-source window. 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/StdLibMods/
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/Pass4Mac/
M./Compiler/Pass4C/
M./Compiler/Pass4PPC/
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 (ppc, i386, 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

At the moment a program is interrupted, a "Modula-2 Runtime Error" alert appears giving information about how and where the interrupt occurred. However only the internal number of the procedure or method (instead of its name) and the address of the next machine-code instruction in the procedure to be executed (relative to the beginning of the procedure and absolute) is displayed. The value of the stack pointer ("r1") for ppc resp. the value of the frame pointer ("ebp") for Intel is displayed too.
 
Error dialog ppc Error dialog Intel

The procedures are numbered in the order of their declaration. First of all the exported procedures declared in the definition module are numbered, starting at 1 (one). After that the procedures defined in the implementation module (which are not exported) are assigned numbers in the order of their appearance in the source code. The initialization part of a module is always assigned the number 0 (zero).

Keyboard equivalents for the buttons are shown in parentheses in what follows.

With the buttons "up" (up arrow) and "down" (down arrow) you can find out about the routine dynamically preceding or succeeding (respectively) the routine shown.

The button "Abort" (A) ends the program immediately, without activating the Modula-2 debugger, by calling "_exit".

The "Resume" button (R) causes the program to resume execution. The button is only enabled if no serious error has occurred.

"Term" (T) initiates or continues the Modula-2 termination process (cf. "Compiler", section 2.6). This button has exactly the same effect as a call to "HALT" at this point in the program.

"Handler" (H) activates the relevant exception handler (cf. "Compiler," section 2.7). If "BREAK" was called or a breakpoint has been encountered, this button is disabled.

Finally "M2 Debugger" (D) brings Modula-2's symbolic debugger into action. This button is only active if the debugger was included when the program was linked. After clicking the button, six of the debugger's windows appear. Then the debugger reads 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 dialog box appears asking "File for default search?" and containing "OK" and "Cancel" buttons. If "OK" is clicked the system's standard "get file" dialog appears enabling the path file to be located and selected. If "Cancel" is clicked or the file-selection dialog is itself cancelled, the debugger is only able to look in the current directory for necessary files.

If a file cannot be found even with the help of the path file, a message appears that can be dismissed either with "OK" or with "Cancel". If dismissed with "OK" the Macintosh "get file" dialog appears so that a file can be selected as source / reference (or symbol) file. In this way files can also be used which do not conform to the usual naming convention that a file's name excluding its extension is also the name of the module itself. Dismissing with "Cancel" means (as far as the debugger is concerned) that the file does not exist.

Holding the command and option keys down during program launch forces a breakpoint at the beginning of program execution. This breakpoint is set at the beginning of the initialization part of the main program, after all imported modules have been initialized. From there, breakpoints can easily be set in other parts of the program.

By pressing "shift", "command", and "option" key simultaneously at program startup, a breakpoint can be forced before the initialization of the first module. This breakpoint may be used to find bugs in the initialization parts. However, as initialization is not yet done, not all modules may be accessible at that point.

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 applications, six of these windows are shown in the foreground, the register window is displayed only on demand. Questions for file names, new values for variables etc. are displayed in separate dialogs.
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 applications, all the windows can be closed, moved and resized. The configuration of the windows is saved between calls to the debugger (within the one program execution).
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 applications
 

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 applications, each of these items can be selected by clicking with the mouse. If you select the module name, 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. Double clicking on a module name fills the two windows "Source" and "Data_2" with applicable information.
If the address is selected, the command "Display at $<address>" can be used to display the corresponding part of memory displayed in the Memory window. Double clicking on the address achieves the same thing faster.

For tools, each line can be selected up and 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 applications, if a procedure name is selected, the commands "Open Source for <module name>", "OpenData_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. A double click on a procedure name correspondingly results in the two windows "Source" and "Data_1" (not "Data_2"!) being filled with the relevant information.
If one of the addresses to the right or a procedure name is selected, the corresponding part of memory can be displayed in the Memory window by choosing the menu item "Display at $<address>" or by double clicking on the address. When clicking the relative program address, the selected part of memory is automatically disassembled and shown in the appropriate instruction format.

For tools, each line can be selected 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. Double clicking on the name of a such a variable 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 applications, the arrows at each end of (the visible part of) the name can be used to scroll it to the left or right. By double clicking on any part of the name the display can be snapped back to showing the data corresponding to the name up to that point.
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>". For applications this may be achieved by either double clicking on the address of a variable or by selecting the address and then 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.

The Source window is also designed for setting and clearing breakpoints. In version 9.0, the use of breakpoints is not available, as this part needs a full redesign of the debugger.

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 four 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 or handles. 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 four 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 Ppc Registers

The first two lines show the status registers:
CTRCount Register
LRLink Register
PCProgram Counter
CRCondition Register
XERInteger Exception Register
MSRMachine State Register
FPSCRFloating Point State- and Control Register
VRSaveVector Save Register
VSCRVector State and Control Register
They are followed by the 32 GPRs ("General Purpose Registers") r0 .. r31. According to the programming conventions, the following registers have special meaning:
SPStack Pointer, also r1
r30Static Link, if an inner procedure accesses the variables of surrounding procedures
r31Global Pointer, if a procedures accesses global variables of its module.
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".

The AltiVec registers are displayed always, there value is meaningful only on computers that support the AltiVec instruction set.

1.4.6.2 The Intel 32-bit Registers

The first two lines show the status registers:
EIPInstruction 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 8 integer registers eax .. esp. According to the programming conventions, the following registers have special meaning:
SPStack Pointer, also esp
FPFrame Pointer, also ebp
esiSELF Pointer in Methods, Static Link, if an inner procedure accesses the variables of surrounding procedures
ediGlobal Pointer, if a procedures accesses global variables of its module.
Note: The values in the integer registers 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 LONGREAL arithmetic.

1.4.6.3 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.

Single Step
The program is continued up to the next line of Modula-2 code.

(ATTENTION: not implemented in V9.3)

Single Step Skip Calls
The program is continued up to the next line of Modula-2 code, subroutine calls are skipped.

(ATTENTION: not implemented in V9.3)

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

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)