TOC PREV NEXT INDEX DOC LIST MASTER INDEX



Debugger Concepts

There are a number of concepts which are important to understand when using the Apex debugger. These are discussed in this chapter. The following are included:

To understand how the Apex debugger works, it helps to have an understanding of the following topics related to debugging:


Context of Debugger Commands

Many debugger commands operate within a context. By default, the debugger'scurrent context consists of:


Name Resolution

Name resolution is the process of mapping a context-dependent relative pathname to a fully qualified pathname that references a unique object. The debugger uses the source location corresponding to the current context to resolve relative pathnames used in commands.

The location determined by the current context is used for name resolution of pathnames in all debugger commands that take expressions or pathnames as arguments. For example, if a program stops at a statement named .xyz.proc, any pathnames in an expression typed in the Show Data dialog box are resolved in the context of .xyz.proc. For more information on pathnames in the debugger, see Debugger Pathnames.

Context dependent name resolution does not apply to fully qualified names.


Retrieving Information from Call Stack Frames

When functions and procedures are called in block-structured procedural languages (such as Ada and C/C++) local variable definitions and other kinds of important information are placed in a construct called a call stack frame (or simply a frame). When you debug a program using the Apex debugger, the debugger can retrieve and display information stored in call stack frames. For more information see Call Stack.

When a task/thread calls a subprogram (Ada) or function (C/C++), a call stack frame is pushed onto the call stack of the task/thread executing the call. This stack frame contains the values of local variables and parameters of the subprogram (Ada) or function (C/C++).

For Ada, when the Apex debugger stops a task, the debugger can retrieve various kinds of information from the current task's call stack frame. (Tasks are described in Managing Tasks.)


Specifying Objects in Debugging Commands

In many cases, a debugger command requires you to specify the object that will be the target of the command. An example of such a command is the Debug > Show command, which displays the current value of an object or expression. When you execute the Debug > Show command, the Show Data dialog box opens. You must then specify the expression whose value is to be displayed.

Other Debug commands require you to specify debugger pathnames. For example, when you execute the Breakpoints > New command, the Breakpoints window opens. You must then specify the debugger pathname of a program unit (Ada) or file (C/C++), a statement, or a declaration. For more information about debugger pathnames, see Debugger Pathnames.

When you issue a debugging command, you can specify the object that is the target of the command by using one or both of the following methods:

Debugger Pathnames

You can explicitly reference objects in a program using strings called debugger pathnames. A debugger pathname can be especially useful when you want to access an object that is not currently displayed on the window and therefore is not accessible using the mouse.

The first component in a debugger pathname is some part of a program, such as a package, task, or subprogram (Ada) or function (C/C++). A debugger pathname can also include data values.

A debugger pathname can refer to a specific field of a record, a component of an array, or an object that is pointed to by an access value. Adebugger pathname can also refer to a variable either a simple variable or an element in a data structure.

A debugger pathname can specify the static definition of a variable or its runtime value. Variables can appear in debugger commands that expect a location as well as in those that expect a variable.

Categories of Debugger Pathnames

Debugger pathnames can be divided into two general categories: fully qualified debugger pathnames and relative debugger pathnames.

A fully qualified debugger pathname contains all the information necessary to specify an object uniquely.

A relative debugger pathname is an incomplete pathname that can be expanded into a fully qualified pathname by the context of a debugger command (see Context of Debugger Commands). When the debugger encounters a relative pathname, it maps the pathname to a unique object by resolving the pathname relative to the current context. Relative debugger pathnames typically are shorter and more convenient to use than fully qualified debugger pathnames.

Fully Qualified Debugger Pathnames

For Ada:

When you debug a program, every object in the program has a fully qualified debugger pathname that uniquely identifies the object within all the source units in the closure of the program. An example of a fully qualified debugger pathname is:

where Seat_Services is a package, Seat_Controls is a procedure within Seat_Services, and Light_On is a function within the Seat_Services procedure.

For C/C++:

The :: operator can be used in a debugger pathname to associate an identifier with a specific class or struct. This is sometimes necessary to reference a class or struct member that is hidden by a locally defined identifier. An example of this kind of fully qualified pathname is:

where Seat_Services is a class and status is a data member of the class. If the local context was within the body of a member function of Seat_Services with a local variable "status", then a fully qualified pathname would be needed to reference Seat_Services::Status.

The other use of the :: operator is to reference names from the global scope. An example of this find of fully qualified pathname is:

Relative Debugger Pathnames

For Ada:

Although you can always refer to an object using its fully qualified debugger pathname, it is often more convenient to use a relative debugger pathname. A relative pathname is resolved in the context determined by the current focus in the program being debugged.

A relative pathname always starts with a name component. The first name component in a relative pathname is resolved using the programming language lexical name-resolution rules in the current context. That means that any object name that can legally appear in a source file at a particular location can be used in a debugger expression. For example, when the current context is:

a valid relative name is Light_On the name of the function specified in the preceding example.

In this new context, Light_On refers to the same object as the fully qualified name (Seat_Services.Seat_Controls.Light_On) specified in the preceding example. Therefore, you can enter Light_On in the Expression field in the Show Data dialog box.

If the first name component of a relative pathname cannot be resolved in a given context using the language's name-resolution rules, the debugger looks for the name among all library-unit names, filenames, and global symbols. In the preceding example, Seat_Services.Seat_Controls.Light_On is a valid reference to the Light_On function anywhere in the program containing the Light_On function, unless another object with the name Seat_Services is visible within the scope of the Light_On procedure. If another object named Seat_Services is visible within the scope of the Light_On procedure, the relative pathname Seat_Services.Seat_Controls.Light_On is not valid because it is an ambiguous pathname.

For C/C++:

In a normal debugging scenario, relative debugger pathnames are usually preferred because they are shorter. Since a relative debugger pathname does not use the :: operator, the debugger resolves the first simple identifier in the pathname in the context determined by the current focus in the program being debugged. Within a member function, the member names can be referenced by their simple identifiers. For example, within a member function Call_Attendant or class Seat_Services, the member Light_Status is directly visible without qualification.

If the first identifier of a relative pathname cannot be resolved in a given local context using the C/C++ name resolution rules, the debugger looks for the name among the global names.

Simple Pathnames (Ada only)

The rightmost name component of a pathname is the object's simple name. For the fully qualified pathname of:

the simple name is Light_On.

Specifications and Bodies of Program Units (Ada only)

You can distinguish between the specification and body of a program unit by using the 'Spec or 'Body attributes. For example, Seat_Services'Spec.3d and Seat_Services'Body.3d refer to different declarations.

Resolving Pathnames to Locations and Objects

A relative or fully qualified pathname resolves to an entity in a program. An entity is a numbered statement, a declaration, a line, a variable, or a constant, and so on.

A pathname is bound to a source entity when the pathname is resolved. If the pathname resolves to a location, no further resolution is needed because the location is uniquely identified. If a pathname resolves to an object, there may be multiple activations of the entity in the program. In this case, you may need to navigate to a particular activation.

Selecting Locations

To set a breakpoint at a particular location in a source file, or to start or stop program execution at a given location during debugging, you can specify the location by highlighting it with the mouse.

Locations in the source can be selected by designating a line of the source.

Selecting a line of source resolves to the statement or declaration displayed on that line. Selecting a subprogram or package header line resolves to the subprogram or package.

Remember that if you want to debug a C/C++-language source file, you must compile it with debugging enabled (the -g flag set).

For Ada, for more information on interfacing Ada units with source code written in C and other non-Ada languages, see the Ada Compiler Reference Manual.


Debugging Language Units (Ada only)

When you debug a program, the Apex debugger is aware of all the library units that make up the program. The kinds of units that can make up a program are:

In Apex, a compilation-unit file can contain only one program unit. Apex also requires all library units within a configuration to have unique names.

Debugger pathnames can reference library units, and can also reference nested program units in library units.

To reference nested program units in a program, you append the name of the nested program unit to the debugger pathname of the unit or subunit that immediately encloses it. The period serves as a name-component separator.

As an example, suppose the directory-viewer window displays the following files each of which is a compilation unit

In the example shown above:

These files shown in the following list contain the previously listed units.

The two units shown above have the following fully qualified pathnames:

Thus, in the preceding example, every name component that follows the first name component refers to some object declared in the named unit.

Accessing Overloaded Objects

Overloading of procedure name and the names of objects in your programs is permitted.

If the search finds multiple definitions for the same name, the debugger issues a message in the Debugger window displaying each of the possible alternatives. The notation is simple_name'n, where n is a number (e.g., sin'2). An example of the debugger message follows.

Append the suffix 'n to the subprogram name to indicate precisely the name to reference, where n is one of the numbers listed in the overloading message.

The debugger assigns a number to each occurrence of a simple name, starting with 1. These numbers remain constant throughout the debugging session. When the debugger finds overloading, it lists all of the overloadings.

Understanding Statement and Declaration Numbers

The Apex debugger assigns numbers to statements and declarations. Statement and declaration numbers are not shown in source-code windows.

To construct a number for a statement or a declaration, the debugger appends the number of the statement or declaration to the pathname of the subprogram, package, task, block, or accept statement (Ada) or function, or class (C/C++)containing the numbered object. In other words, the number of a statement or declaration includes the pathname of the containing statement group and the number suffix of the statement or declaration.

The debugger assigns declaration and statement numbers only to statements or declarations that have associated assembly code.

Some declarations and statements are removed during optimization; thus they have no associated assembly code and cannot be referenced by a statement number.

For Ada, Table 7 shows how the debugger numbers statements, declarations, and lines (numbering within each group of declarations and statements is independent).

Table 7 Debugger Statement Numbering
Code
Statement number
package Res is

subtype Passenger is String (1 .. 20);
Res'Spec.1d
type Flight_Info is range 1..3000;
Res'Spec.2d
type Person;
Res'Spec.3d
type Person_Ptr is access Person;
Res'Spec.4d
type Person is record
Res'Spec.5d
Name : Name;

Age : Natural;

Flight : Flight_Info;

And record;

procedure Rsvp (List : in out Person_Ptr);
Res'Spec.6d
Name : Passenger;

Age : Natural;

Flight : Flight_Info);

end Res;

package body Res is

procedure Rsvp (List : in out Person_Ptr);
Res'Body.1d
Name : Passenger;

Age : Natural;

Flight : Flight_Info) is

begin

Check_Name (Name);
Res.Rsvp'Body.1s
Add_To_Flight (List, Name, Flight);
Res.Rsvp'Body.2s
Add_To_List (List, Name, Age, Flight);
Res.Rsvp'Body.3s
declare
Res.Rsvp'Body.4s
Cost : Ticket_Price := 0;
Res.Rsvp'Body.4s.1d
Max_Price: constant Ticket_Price := 1000;
Res.Rsvp'Body.4s.2d
begin

while Cost > Ticket_Price loop
Res.Rsvp'Body.4s.1s
Cost := Compute_Ticket_Price (Flight);
Res.Rsvp'Body.4s.2s
end loop;

Send_Bill (Name, Cost)
Res.Rsvp'Body.4s.3s
exception

when others =>

Cancel_Reservation (Name, List);
Res.Rsvp'Body.4s.4s
end;

Finalize_Transaction;
Res.Rsvp'Body.5s
end Rsvp;

end Res;

Statement Numbers in Task Constructs

For Ada, In task constructs, the debugger assigns numbers to tasks, as in the example illustrated in Table 8.

Table 8 Debugger Statement Numbering in Tasks
Code
Statement number
procedure Tasks is

 task type Worker is
Tasks'Body.1d
  entry Die;

 end Worker;

 task body Worker is
Tasks'Body.2d
 begin

  accept Die;
Tasks.Worker'Body.1s
 end Worker;

 task type Active is
Tasks'Body.3d
  entry Live (Time : Integer);

 end Active;

 task body Active is
Tasks'Body.4d
  Count : Natural;
Tasks.Active'Body.1d
 begin

  accept Live (Time : Integer) do
Tasks.Active'Body.1s
   Count := Time;
Tasks.Active'Body.1s.1s
  end Live;

  while Count > 0 loop
Tasks.Active'Body.2s
   Count := Count - 1;
Tasks.Active'Body.3s
  end loop;

 end Active;



begin

...

end Tasks;

Statement-Numbering Rules

For Ada, Table 9 summarizes the statement-numbering rules.

Table 9 Debugger Statement-Numbering Rules
Rule
no.


Rule


Examples

1
Within each subprogram, package, task, and block, statements and declarations are numbered independently beginning with 1.
Res'Spec.1d
Res'Body.1d
Res.Rsvp.1s
Res.Rsvp.4s.1d

2
Package specs and bodies are numbered separately.
Res'Spec.1d
Res'Body.1d

3
Blocks are numbered separately as if they were subprograms themselves:
Declarations and statements within a block are numbered independently.
The block is referenced as a whole by the statement number of the block in the subprogram that contains it.
If the block is labeled, the label can be used instead of the statement number.



Res'Body.1d
Res.Rsvp'Body.1s

Res.Rsvp'Body.4s.1d

4
Loop statements are numbered sequentially with the surrounding statements.
Res.Rsvp'Body.4s.1s
5
Statements in exception handlers are numbered continuously with preceding statements. The when clause is not a statement, so it is not assigned a number.
Res.Rsvp.4s.3s
6a









6b

Accept-statement bodies are treated similarly to blocks:
Statements within an accept-statement body are numbered independently.
The accept-statement body is referenced as a whole by the statement number of the accept statement in the statement list that contains it.
Select alternatives are numbered sequentially with surrounding statements.

Accept arms of select statements are numbered sequentially with statements at the same level as the enclosing select
statement.


7
The following objects are not numbered:
Use clauses
Generic formal parameters
Representation specifications


Referencing Variables and Parameters in Subprograms or Functions

In Apex, you can perform a debugging action on a variable or a parameter in two ways: by specifying its pathname or by selecting it with the mouse. In a subprogram (Ada) or function (C/C++), when you designate a variable or a parameter by specifying its relative pathname, the name of the variable or parameter and the activation of the variable or parameter are resolved as follows:

When you specify an object by selecting it with the mouse, the debugger performs only the action in the second bullet above.


Core File Debugging

Description

UNIX programs produce core files when certain signals occur that are neither caught nor ignored. The core file contains a complete snapshot of the program's state at the time the signal occurred. See your UNIX signal documentation for the list of signals that cause a core file to be produced.

The debugger automatically reads a core file if it exists in the directory the debugger was invoked from. Alternatively, the path name of a core file can be given explicitly with the -C core_file_name debugger option.

Note: If the open fails for any reason, for example, permission denied, etc., the debugger silently ignores the core file.

After reading the core file the debugger informs you that it is using the core file image and displays a message similar to the following:

The message in brackets, [], tells the user the name of the core file and the name of the executable. If the executable name in the core file doesn't match the executable name the user is trying to debug, the debugger displays a warning, but tries to continue using the core file.

It then announces the signal that caused the core file to be produced. This announcement looks identical to the announcement produced if the signal had occurred while running the program from the debugger. It includes the signal name, the source file and line, and the subprogram name where the signal occurred. For the DEC Alpha the code value can be ignored; it will always be 0.

You can then use any of the debugger's commands to interrogate the state of the program at the point it produced the core file. For example, you can display the call stack, display variable values, examine registers, list task information, or go into disassembly mode to see the exact instruction that caused the signal (or was executing when the signal was delivered), etc.

You cannot continue the program from its core file state. All execution commands (o, oi, i, ii, s, si, g, gw, gs, r) cause the program to be restarted. You can also type set run to reset the program to its normal startup state (for example, you might do this if you were not really interested in the core file state).

In some situations, the debugger produces warning and/or error messages about a core file. If error messages are produced the core file is ignored. Here are some of the more common messages:


Rational Software Corporation 
http://www.rational.com
support@rational.com
techpubs@rational.com
Copyright © 1993-2002, Rational Software Corporation. All rights reserved.
TOC PREV NEXT INDEX DOC LIST MASTER INDEX TECHNOTES APEX TIPS