The Ada mode of the debugger supports a fairly large subset of Ada expression syntax, with some extensions. The philosophy behind the design of this subset is:
That the debugger should provide basic literals and access to operations for arithmetic, dereferencing, field selection, indexing, and subprogram calls.
That type safety and strict adherence to Ada language restrictions are not particularly important to the the debugger user.
That brevity is important to the the debugger user.
Thus, for brevity, the debugger acts as if there were implicit with and use clauses in effect for all user-written packages, making it unnecessary to fully qualify most names with their packages, regardless of context. Where this causes ambiguity, the debugger asks the user's intent.
The debugger will start in Ada mode if it detects an Ada main program. As for other languages, it will enter Ada mode when stopped in a program that was translated from an Ada source file.
While in Ada mode, you may use -- for comments. This is useful mostly for documenting command files. The standard debugger comment (#) still works at the beginning of a line in Ada mode, but not in the middle (to allow based literals).
The debugger supports limited overloading. Given a subprogram call in which the function symbol has multiple definitions, it will use the number of actual parameters and some information about their types to attempt to narrow the set of definitions. It also makes very limited use of context, preferring procedures to functions in the context of the call command, and functions to procedures elsewhere.
Here are the notable omissions from the subset:
Only a subset of the attributes are supported:
'First, 'Last, and 'Length on array objects (not on types and subtypes).
'Min and 'Max.
'Pos and 'Val.
'Tag.
'Range on array objects (not subtypes), but only as the right operand of the membership (in) operator.
'Access, 'Unchecked_Access, and 'Unrestricted_Access (an extension).
'Address.
The names in Characters.Latin_1 are not available and concatenation is not implemented. Thus, escape characters in strings are not currently available.
The component-by-component array operations (and, or, xor, not, and relational and equality tests) are not implemented.
There are no record or array aggregates.
Dispatching subprogram calls are not implemented.
The overloading algorithm is much more limited (that is less selective) than that of real Ada. It makes only limited use of the context in which a subexpression appears to resolve its meaning, and it is much looser in its rules for allowing type matches. As a result, some function calls will be ambiguous, and the user will be asked to choose the proper resolution.
The operator new is not implemented.
Entry calls are not implemented.
Aside from printing, arithmetic operations on the native VAX floating-point formats are not supported.
As it does for other languages, the debugger makes certain generic extensions to Ada: the operators "@", "::", and {type} addr convenience variables and machine registers.
In addition, it provides a few other shortcuts and outright additions specific to Ada:
The assignment statement is allowed as an expression, returning its right-hand operand as its value. Thus, you may enter
The semicolon is allowed as an "operator", returning as its value the value of its right-hand operand. This allows, for example, complex conditional breaks:
Rather than use catenation and symbolic character names to introduce special characters into strings, one may instead use a special bracket notation, which is also used to print strings. A sequence of characters of the form "["XX"]" within a string or character literal denotes the (single) character whose numeric encoding is XX in hexadecimal. The sequence of characters "["""]" also denotes a single quotation mark in strings. For example,
Contains an ASCII newline character (Ada.Characters.Latin_1.LF) after each period.
The subtype used as a prefix for the attributes 'Pos, 'Min, and 'Max is optional (and is ignored in any case). For example, it is legal to write
When printing arrays, the debugger uses positional notation when the array has a lower bound of 1, and uses a modified named notation otherwise. For example, a one-dimensional array of three integers with a lower bound of 3 might print as
That is, in contrast to valid Ada, only the first component has a => clause.
You may abbreviate attributes in expressions with any unique, multi-character subsequence of their names (an exact match gets preference). For example, you may use a'len, a'gth, or a'lh in place of a'length.
Since Ada is case-insensitive, the debugger normally maps identifiers you type to lower case. The compiler uses upper-case characters for some of its internal identifiers, which are normally of no interest to users. For the rare occasions when you actually have to look at them, enclose them in angle brackets to avoid the lower-case mapping. For example,
The main procedure in Ada has no fixed name, and attempts to break on main will position you before elaboration. Therefore, Ada mode provides a convenient way to begin execution of the program and to stop at the beginning.
Does the equivalent of setting a temporary breakpoint at the beginning of the main procedure and then performing run. Since in general there is package elaboration code that runs before the main procedure begins, it is possible that the program will stop before reaching the main procedure. However, the temporary breakpoint will remain to halt execution.
In Ada mode, you can set breakpoints that trip when your program raises selected exceptions.
The info exceptions command permits the user to examine all defined exceptions within Ada programs. With a regular expression, regexp, as argument, prints out only those exceptions whose name matches regexp.
Support for Ada tasks is analogous to that for threads. When in Ada mode (that is, when the "current language" is Ada), the debugger allows the following task-related commands:
This command shows a list of current Ada tasks, as in the following example:
Example 9-1. Output from info tasks
(gdb) info tasks TCB Task Task Base Actv On Ready Wakeup Time Deadline Address Id State Prio Prio Hold Count (seconds) (seconds) +--------+----+-----------+----+----+----+---------+------------+------------ *00010778 1 Running 0 0 0 3 0.000000 0.000000 000107ba 2 At_Barrier 1 1 0 1 0.000000 0.000000 00010ffa 3 Delayed 10 10 0 24 2.402170 0.000000 0001183a 4 Delayed 10 10 0 24 2.402170 0.000000 +--------+----+-----------+----+----+----+---------+------------+------------
In this listing, the asterisk before the first task indicates it to be the currently running task.
This command lists any tasks that are queued in the ready queue, the delay queue and the deadline queue.
The compiler always uses code expansion for generic instantiation. This means that each time an instantiation occurs, a complete copy of the original code is made with appropriate substitutions.
It is not possible to refer to the original generic entities themselves in the debugger (there is no code to refer to), but it is certainly possible to debug a particular instance of a generic, simply by using the appropriate expanded names. For example, suppose that Gen is a generic package:
-- In file gen.ads: generic package Gen is function F (v1 : in out INTEGER) return INTEGER; end Gen; -- In file gen.adb: package body Gen is function F (v1 : in out INTEGER) return INTEGER is begin v1 := v1 + 1; return v1; -- Line 5 end F; end Gen;
and we have the following expansions:
procedure G is package Gen1 is new Gen; package Gen2 is new Gen; begin Gen1.F; Gen2.F; Gen1.F; Gen2.F; end;
Then to break on a call to procedure F in the Gen2 instance, simply use the command:
To break at a particular line in a particular generic instance, say the return statement in G.Gen2, append the line specification to the file and function name:
To break on this line line in all instances of Gen, use `*' as the function name:
This will set individual breakpoints at all instances; they are independent of each other and you may remove, conditionalize, or otherwise modify them individually.
When a breakpoint occurs, you can step through the code of the generic instance in the normal manner. You can also examine values of data in the normal manner, providing the appropriate generic package qualification to refer to non-local entities.
Ada introduces one new set command.
Limit the size of the types of objects when those sizes are computed from run-time quantities to size bytes. When this is set to 0, there is no limit. By default, it is about 65K.
Show the limit on types whose size is determined by run-time quantities.
Besides the omissions listed previously (see Section 9.4.1), we know of several problems with and limitations of Ada mode in the debugger, some of which will be fixed with planned future releases of the debugger and the Ada compiler.
Currently, the debugger has insufficient information to determine whether certain pointers represent pointers to objects or the objects themselves. Thus, the user may have to tack an extra .all after an expression to get it printed properly.
Static constants that the compiler chooses not to materialize as objects in storage are invisible to the debugger.
Renaming declarations are invisible to the debugger.
Named parameter associations in function argument lists are ignored (the argument lists are treated as positional).
Many useful library packages are currently invisible to the debugger.
Fixed-point arithmetic, conversions, input, and output is carried out using floating-point arithmetic, and may give results that only approximate those on the host machine.
The type of the 'Address attribute may not be System.Address.
When stopped in a particular subprogram, you can access variables defined in other, lexically enclosing subprograms by their simple names. At the moment, however, this may not always work; it depends on whether the compiler happens to have made the necessary information (the "static link") available at execution time, which it can sometimes avoid. Of course, even in those cases where the compiler does not provide the information, you can still look at such variables by issuing the appropriate number of up commands to get to frame containing the variable you wish to see. Access to non-local variables does not, at the moment, work in the test expressions for conditional breakpoints unless you happen to specify these while stopped in the subprogram in which they are to be applied.