MAINDEBUG User's Guide, Chapter 2

previous   next   top   complete contents   complete index   framed top   this page unframed


2. General Operation

A program may be composed of both debuggable and nondebuggable MODULEs. A debuggable MODULE is slightly larger and slower, since the generated code is affected by the debug option.

2.1. Compilation

All MODULEs that are to take part in debugging must be compiled with the DEBUG compiler option; refer to Section 4.9 of the MAINSAIL Compiler User's Guide for details.

The DEBUG compiler option causes an intmod (intermediate MODULE) to be generated, in addition to the objmod (object MODULE). The intmod includes debugging information such as symbol tables and tables that relate code offsets with source file positions for each MAINSAIL statement. An intmod can be stored in a separate file, or in an intlib (intmod library). Intmods are described in the Chapter 13 of the MAINSAIL Language Manual. Intlibs are manipulated with the utility MODULE INTLIB as described in Chapter 18 of the MAINSAIL Utilities User's Guide.

2.2. Invocation

The debugger can be invoked explicitly either from the MAINSAIL executive or in response to the standard MAINSAIL error routine prompt. It can also be invoked automatically if you set the environment variable STARTMSLINDEBUGGER on operating systems that support environment variables; see Section 2.2.3.

To invoke the debugger from the MAINSAIL executive, type DEBUG in response to the MAINSAIL * prompt (i.e., run the MODULE DEBUG). To invoke the debugger after an error has occurred, type DEBUG (or enough to make this response unique) to the Error Response: prompt. You can enter the debugger this way even when a fatal error occurs, although continuing execution from a fatal error is not allowed.

Invoking the debugger as the MODULE DEBUG before executing the program to be debugged allows you to prepare for the debug session, e.g., to set breakpoints, and then invoke the program from the debugger (it is said to “run under the debugger”). Invoking the debugger in response to an error message gives you control at the point of the error.

2.2.1. Setting Debugger Options in the Parameter File

You can set some debugger options that affect the way the debugger behaves in your parameter file. This facility is described in detail in Section 4.32.

2.2.2. Typical Ways to Start a Debugging Session

There are five common ways to begin a debugging session:

2.2.3. Forcing a MAINSAIL Bootstrap to Start in the Debugger

It is often inconvenient to debug a MAINSAIL bootstrap whose COMMANDSTRING you have specified, since such a bootstrap starts executing your program immediately, without displaying the MAINEX * prompt. Of course, your program may have a command to invoke the MAINSAIL debugger; but if not, you may have to add such a command or hardwire a call to $debugExec at the start of program execution in order to run the MAINSAIL debugger on your program.

As an easier way to start your program under the debugger, on operating systems that support environment variables (e.g., UNIX and Windows), MAINSAIL examines the environment variable STARTMSLINDEBUGGER at the start of its execution. If the variable has the value 1 (i.e., the string consisting of the digit one), MAINSAIL invokes $debugExec after processing the SUBCOMMANDS specified in the bootstrap and in any v1630.prm files it finds, but before invoking the MODULE specified by COMMANDSTRING, if any. This allows you to set breakpoints in your program at the beginning of its execution. You can then resume execution of your program by issuing the debugger's C command or by quitting the debugger.

The STARTMSLINDEBUGGER environment variable invokes $debugExec even in MAINSAIL bootstraps that do not have COMMANDSTRING set, but is most useful in conjunction with such bootstraps.

2.3. Finding Intmods

The debugger must be able to find the intmod for each MODULE that is to take part in a debug session. When the debugger needs to find an intmod for some MODULE M, it first searches all open intlibs, then tries to open the file named xxx-int:m, where xxx is the “file abbreviation” for the operating system (typically the same as the standard MAINSAIL abbreviation for the operating system, or the first three characters of that abbreviation; see Section 14.1 of the MAINSAIL Language Manual). For example, the file abbreviation for PA-RISC UNIX is upa; the file the debugger tries to open on that operating system is upa-int:m.

A searchpath is customarily specified during MAINSAIL initialization for file names of the form xxx-int:*, where xxx is the first three letters of the current operating system abbreviation. The searchpath distributed by XIDAK for UPA is:

SEARCHPATH upa-int:* *-upa.int

Thus, on PA-RISC UNIX the debugger tries to obtain the intmod for M from the file m-upa.int if it cannot find it in an open intlib.

The debugger provides the OI (Open Intlib) command for opening intlibs (OI is described in Section 4.30). Also, the MAINEX subcommand OPENINTLIB can be used to open an intlib (MAINEX subcommands can be given at any time by running MODULE SUBCMD). Each intlib that contains an intmod needed during the debug session should be open before the debugger tries to find the intmod. The MAINEX subcommands INTFILE, INTLIB, and INTDEFAULT also provide control over intmod searches, as described in Chapter 14 of the MAINSAIL Language Manual and Table 21–5 of the MAINSAIL Utilities User's Guide.

If you put a needed intmod in an intlib and forget to open the intlib before the debugger needs to access it, the debugger fails to find the intmod in the default file, so it issues an error message and prompts for a new file name or library in which to look.

2.4. Context

In the debugger, context can include the following things:

The debugger keeps track of three kinds of context: command context, breakpoint context, and compiler context.

The command context (sometimes also called the debugger context or even just “context” when the meaning is clear) is the context in which most debugger commands are given; for example, commands to examine variables show the values resulting from evaluating the variables in the current command context.

The breakpoint context exists only if the debugger has hit a breakpoint (or has been invoked from a program); it is the context in which the breakpoint was hit (or from which the debugger was invoked). The breakpoint context can be changed only by continuing execution and hitting a new breakpoint.

The compiler context is used by the !D, .I, and .+I commands. This context allows the declaration of an identifier to be displayed even in contexts where an expression containing the identifier could not be evaluated (e.g., a local variable that cannot be evaluated because the PROCEDURE where it is declared has not been invoked. The debugger nonetheless has sufficient information to display the variable's declaration).

The command context does not have to include all the possible context elements; for example, there can be a current MODULE but not a current PROCEDURE. In such a case, commands that require a current PROCEDURE (such as commands that examine local variables) are not valid, but commands that depend only on the current MODULE (such as commands that set breakpoints) are valid. Initially, the command context is empty.

The breakpoint context generally contains either all context elements (if a breakpoint has been reached) or none (if no breakpoint has been reached).

Whenever the debugger obtains control, the command context and breakpoint context are set to be the same: if any debuggable PROCEDURE has been invoked in the current coroutine, the context is set to the breakpoint or point from which the debugger was invoked within that PROCEDURE invocation. If no debuggable PROCEDURE has been invoked within the current coroutine, both command context and breakpoint context are set to be empty.

Changing the file and file position parts of the command context (by using editor or debugger commands that position in the source text) does not change the other parts of the command context. The MODULE, data section, coroutine, PROCEDURE, and iUnit parts of the context are affected by such operations as reaching a breakpoint or by the commands that walk the call or exception stack or change the current coroutine.

You may use file positioning commands to move to a place where a breakpoint can be set, but only if the breakpoint position is in a file used by the current MODULE (i.e., the current MODULE part of the command context). Otherwise, when you attempt to set the breakpoint, the debugger informs you that the file is not one of the files used in the current MODULE.

The compiler context's MODULE context is the same as the MODULE part of the command context. The PROCEDURE context (if any) is the PROCEDURE where the debugger cursor is located; there is no PROCEDURE context if the cursor is outside any PROCEDURE for the current MODULE. Thus, you can see a local variable v of a PROCEDURE p in a MODULE M by setting the MODULE context to M with the M command, moving the cursor into p, and saying !D v.

You can always examine the declarations of identifiers that are predefined by MAINSAIL, regardless of whether or not there is a compiler context.

2.5. Positions and iUnits

Context can include a position within a MODULE if the MODULE is currently executing. Such a position within a MODULE is usually specified by positioning the cursor to the appropriate place in the MODULE's source text, but it can also be specified numerically as an offset in the control section for the MODULE. Such an offset is called an iUnit (for “instruction unit”). iUnits are displayed when a stack dump is given, as by the CALLS error response or the CALLS utility MODULE, and by certain debugger commands. Remembering the iUnit of a location of interest in a MODULE can save you the effort of positioning the cursor to that spot in the source text if you wish to issue a command referring to that location, because several debugger commands take an iUnit as an argument. However, iUnits become invalid whenever a MODULE is recompiled, since the offsets of the instructions corresponding to source statements may change.

2.6. Breakpoints and Single Stepping

MAINDEBUG provides four commands for directly controlling and displaying the execution of MAINSAIL statements: the B, T, S, and J commands. Each command causes program execution to “break” under specified conditions. When execution breaks, MAINDEBUG displays a prompt (in the line-oriented interface) or an editor message (in the display-oriented interface) and waits for further commands.

The B command sets a permanent breakpoint (one at which execution halts each time the breakpoint is reached); the T command sets a temporary breakpoint (one that is removed the next time a break occurs). The S command executes the next MAINSAIL statement, without following execution into PROCEDUREs; the J command executes the next MAINSAIL statement, breaking on the first statement of any debuggable PROCEDURE invoked. The S and J commands therefore act identically if the executed statements do not invoke a debuggable PROCEDURE. See Chapter 4 for the descriptions of these commands.

The C and .C commands are used to continue execution from a breakpoint; see Section 4.6.

The +W (watch variables) command can be used to cause the program to break when a variable changes value or acquires a specified value; see Section 4.48.

2.7. Command Interface

The debugger supports two different user interfaces: line-oriented and display-oriented. The display-oriented interface makes available to the user all the features of MAINEDIT during a debugging session. The line-oriented interface runs even on terminal types for which no MAINEDIT display MODULE exists.

2.7.1. Line-Oriented Command Interface

The line-oriented command interface provides debugging commands and a simple set of read-only line editor commands, communicating through the file TTY. It displays short pieces of the current file in response to each debugger command. The debugger's cursor is indicated by a right curly bracket, }, immediately preceding the current character. If any command in a command line changes the cursor position, the new line and cursor position are displayed before the prompt for the next command line.

A command line given by the user in response to the debugger's prompt is a sequence of debugging commands. All commands on a line are executed from left to right unless an error occurs, in which case commands after the one in error are not executed.

The debugger's prompt indicates the current context and has the default format (use the OP command to change the prompt format):

moduleName.procedureName:

The PROCEDURE name is omitted if there is no PROCEDURE context; if there is no MODULE context, the prompt is:

MAINDEBUG:

When a breakpoint is taken, or when the debugger is entered in response to an error message, the debugger displays the appropriate line of text followed by the debugger prompt, described above, and awaits a debugger command. The command line must be terminated with <eol> before any command on the line is executed.

2.7.2. Display-Oriented Command Interface

The display-oriented command interface uses MAINEDIT, the MAINSAIL text editor. This interface provides debugging commands and full access to the command structures offered by the various editor front ends (which include MAINVI, an emulation of the UNIX editor vi, and MAINED, XIDAK's own front end). The output of the debugger and the debugged program is captured in a buffer, so that the user can refer to earlier output at any time. The MAINEDIT User's Guide describes MAINEDIT in detail.

Files are displayed on the screen just as they are by MAINEDIT. The editor cursor is used to indicate the current position in a file, rather than the curly bracket cursor of the line-oriented interface. When a command requiring the debugger to position to a particular location is issued, the file containing the specified MODULE is displayed in a window. If the file was not previously in a buffer, a new window is created and the cursor positioned at the beginning of the file. If there was a buffer for the file, then that buffer is used, and the cursor is positioned where it was when last in that buffer.

The display-oriented interface provides immediate feedback when a command is typed. For example, when the debugger command M is issued, the debugger immediately prompts for the MODULE name on the editor message line. MAINEDIT's <abort> key can be used to abort a debugger command before the user has finished typing it in, or to abort a command that is prompting for further input.

In the display interface, when the debugger is given a character or sequence of characters it cannot interpret as a debugger command, it passes the character(s) to the editor (except for line-oriented debugger commands, which the debugger traps and complains about). The editor executes the appropriate commands and returns to the debugger. In particular, editor macros defined for control keys and keypad keys can be used when interacting with the debugger.

When the debugger reads a file into a MAINEDIT buffer or breaks at a statement in a MAINEDIT buffer, the buffer is made read-only, so that it cannot be accidentally altered, and the mode is set to escape mode, as indicated by the E in the status line. In this mode commands are processed by the debugger rather than the editor. To talk to the editor, type the <ecm> (Enter Command Mode) key. In command mode, all editor commands are available, allowing you to examine files, position the cursor, etc. Use MAINED's E command (or the corresponding commands in the other editor front ends) to enter escape mode and talk to the debugger again. A common mistake is to forget whether you are talking to the debugger or editor, and use a command intended for one mode while in the other. Be careful that you are talking to the editor (as shown by the buffer mode letter) when giving an editor command, and talking to the debugger (E mode) when giving a debugger command.

The file for the current buffer is the debugger's current file. When the user changes the current buffer by means of a MAINEDIT command, the debugger's current file is automatically set to be the file for the new buffer. Thus, the use of editor commands to change buffers is nearly equivalent to using the debugger's F command to change files.

A file is brought into a buffer and displayed when a breakpoint is reached or when the DEBUG command is entered in response to an error message. At this point, the debugger is awaiting a command.

To edit a buffer used by the debugger, remember that read-only mode must first be turned off for that buffer (use MAINED's -.Oreadonly<eol> command to turn off read-only mode for the current buffer). After a buffer has been modified, the debugger positions in the buffer may no longer correspond to the appropriate text, so it is recommended not to continue a debugging session after changing a buffer containing a part of a MODULE being debugged.

2.8. Command Syntax

MAINDEBUG commands are divided into three groups. General and debugging commands are used to control debugger operations and are available from both the line-oriented and display-oriented interfaces. Editing commands are provided with the line-oriented interface to allow files to be examined. The editing commands are not available with the display interface, since the MAINEDIT commands are used in this case.

Each command consists of one or more base characters, possibly preceded by an integer count (represented in the descriptions as n), dot (.), plus (+), or minus (-), and possibly followed by arguments. If the count is omitted, 1 is assumed. If both count and minus or plus are permitted for a command, it does not matter which comes first. Dot must usually occur immediately before the command character, although sometimes .n introduces a subcount n. The debugger ignores upper and lower case distinctions in command letters. Spaces and tabs are usually ignored except where they play a role as delimiters as described below.

Arguments can usually occur immediately after the command base with no intervening spaces, e.g., both V x and Vx display the value of x.

Optional parts of commands are indicated in curly brackets; e.g., {n}D means the D command can be preceded by a count.

2.8.1. Command Arguments

The different kinds of debugger arguments are as follows:

count integer
id MAINSAIL identifier
name text to first space, tab, or semicolon
expr MAINSAIL expression
dcl MAINSAIL declarations and definitions
stmt MAINSAIL statements

An id argument is used for identifiers such as MODULE and field names. A name argument is used for file names and coroutine names, which need not be identifiers. For commands that can take either a MODULE name or a file name argument, the debugger scans a name argument rather than an id argument since it does not know whether the argument is a file name or a MODULE name.

The end of count, id, and name arguments can be determined simply by scanning the corresponding object from the argument text. The ends of expr, dcl, and stmt arguments are determined by a more complicated scan carried out by the MAINSAIL compiler as it compiles the argument text. When it encounters a “terminating token”, i.e., a piece of text that cannot be the continuation of an expression (for an expr argument), the start of a declaration or definition (for a dcl argument), or the start of a statement (for a stmt argument), it discards the token and returns to the debugger.

expr and stmt arguments are restricted as described in Appendix A.

If a command can have more than one argument, the arguments are usually separated from one another by either commas or semicolons as shown in the individual command descriptions.

In order to allow several commands on the same command line, a terminator is usually required to separate (the last argument of) a command from the next command. The rules for terminators are as follows:

A backslash (\) at the end of a command line serves as a continuation character to indicate that another command line is to be read and concatenated to the end of the first one. The backslash and <eol> are discarded, so precede the backslash with a space if you need white space to separate the first line from the second. Any number of continuation lines may be specified in this manner.

2.8.2. Direct Arguments

Most command arguments can be specified either directly or indirectly. In the case of a direct argument, the argument itself appears on the command line; e.g., V i displays the value of i.

A direct argument may be enclosed in brackets to indicate explicitly where it ends. For example, if a file name argument has an embedded space, tab, semicolon, or brackets, it must be enclosed in brackets; use:

F[(my file)]

rather than:

F(my file)

since otherwise (my would be used as the file name. The debugger is “smart” about finding a matching right bracket; i.e., it ignores brackets in STRING constants and immediately after single quotes ('[' and ']' are the MAINSAIL INTEGER constants for the character codes of left and right bracket, respectively). An unmatched bracket may not otherwise occur in a bracketed argument.

2.8.3. Indirect Arguments

An indirect argument is a kind of subcommand that indicates how the text of the actual argument is to be obtained. For example V @w displays the value of the expression given by the current “word”. The following indirect arguments are provided:

Argument Get Argument Text From
@fs contents of file named s
@l rest of current line
@n name (from cursor to space, tab, or semicolon)
@t token (MAINSAIL identifier or constant at cursor)
@w word (from cursor to space or tab)
@'c text (from cursor to first occurrence of character c)
@n n lines

The command letter after the @ can be uppercase or lowercase; e.g., @W is treated the same as @w. s is a file name, c is any character, and n is an integer count. @t allows MAINSAIL identifiers and constants of type BOOLEAN, INTEGER, REAL, and BITS. The identifier or constant starts at the cursor and continues as long as characters are encountered that form a valid identifier or constant. @l takes the remainder of the current line from the current cursor position.

In @fs, s may be a direct or indirect argument; e.g., @f@l uses the contents of the file whose name is given by the remainder of the current line. Enclose s in brackets if it is a direct name that contains a space, tab, or semicolon, e.g., XS@f[foo.msl] to execute statements in a file named foo.msl.

The current line is the line at which the debugger cursor is currently positioned. This line can be in any file, since the F command allows a file to be specified that becomes the current file. In the display interface, the current line is the one on which the editor's cursor is positioned. Thus argument text can be built up using the editor in an arbitrary buffer, then a command issued with an indirect argument that refers to the text. It is often convenient to put the cursor on some output generated by a previous command such as V or .V and use the @w argument to reference that text.

Most arguments can be omitted, in which case an indirect argument is used by default, as shown:

Type of Omitted Argument Default Argument
expression @t (identifier or constant)
declarations or statements @1 (current line)
all others @l (rest of current line)

An indirect argument specifies the argument text, but the actual argument may be just the initial part of the argument text. However, an indirect name argument uses all of the argument text rather than just the part up to a space, tab, or semicolon as for a direct name argument.

An argument is assumed to be omitted if the end of the command line, or a comma or semicolon, appears before any argument text. For example, if the command list consists of just V with no arguments, the effect is the same as V@t, which displays the value of the current identifier or constant. Similarly, V ,j,k is the same as V @t,j,k, which displays the value of the current identifier or constant, then j and k.

2.9. Miscellaneous

(LONG) BITS, ADDRESS, CHARADR, and POINTER values are displayed in most cases in the preferred radix of the processor, as given by the system macro $preferredRadix. The H and .H commands may be used to force these values to be displayed in hexadecimal.

The debugger accepts { in place of [ and } in place of ] for all occurrences of [ and ]. No distinction is made; i.e., { may match ] and [ may match }.


previous   next   top   complete contents   complete index   framed top   this page unframed

MAINDEBUG User's Guide, Chapter 2