![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
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
- Name Resolution
- Retrieving Information from Call Stack Frames
- Specifying Objects in Debugging Commands
- Debugging Language Units (Ada only) (Ada only)
- Core File Debugging
Context of Debugger CommandsMany debugger commands operate within a context. By default, the debugger'scurrent context consists of:
- The task that the debugger has stopped.
- The location in the program where the program stopped.
From a machine-language point of view, the current location in a program is determined by the current value of the program counter. However, if the debugger can calculate the corresponding source-code location, the debugger regards that as the current location.
- The stack frame within the task's stack that was current when the program stopped.
For more about stack frames, see Retrieving Information from Call Stack Frames.
Name ResolutionName 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 FramesWhen 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 CommandsIn 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:
- You can select (highlight) the name of the object on the window.
- You can open a dialog box associated with a debugger command and then enter a debugger pathname as a parameter to be passed to the commands. You can then execute the command associated with the dialog box you have opened by clicking OK or Apply.
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
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:
Seat_Services.Seat_Controls.Light_On
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.
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:
Seat_Services::Status
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:
::Global_Control
Relative Debugger Pathnames
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:
Seat_Services.Seat_Controls
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.
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:
Seat_Services.Seat_Controls.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:
- Program unit: A subprogram, task unit, or generic unit.
- Compilation unit: A specification or body of a program unit presented for compilation as an independent text file.
- Subprogram: A function or procedure.
- Subunit: A separately compiled proper body of a program unit declared within another compilation unit.
- Library unit: A program unit that has been compiled into a top-level program library.
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
seat_services.ss/rev1.wrk panel_controls.1.ada panel_controls.2.ada seat_controls.1.ada seat_controls.2.ada
- Seat_services.ss is the name of a subsystem.
- Rev1.wrk is the name of a working view.
- Panel_controls.1.ada and seat_controls.1.ada are package specifications.
- Panel_controls.2.ada and seat_controls.2.ada are package bodies.
These files shown in the following list contain the previously listed units.
package Panel_Controls is procedure Call_Button is ... package Seat_Controls is procedure Lights is ...
The two units shown above have the following fully qualified pathnames:
Panel_Controls.Call_Button Seat_Controls.Lights.Light_On
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.
=> add is overloaded. Use 'n to select one: add'1 (integer, integer) add'2 (integer, integer) add'3 (integer, integer) add'4 (integer) add'5 (float)
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).
Statement Numbers in Task Constructs
For Ada, In task constructs, the debugger assigns numbers to tasks, as in the example illustrated in Table 8.
Statement-Numbering Rules
For Ada, Table 9 summarizes the statement-numbering rules.
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:
- The debugger resolves the name of the variable or parameter lexically in the current context, using visibility rules.
- The debugger resolves the activation of the variable or parameter by starting at the current context and checking each frame to see if it contains an activation of the subprogram (Ada) or function (C/C++) in which the entity is declared. If no match is found, the search moves down the execution stack to the next frame, that is, to the calling subprogram (Ada) or function (C/C++). Current context is defined in Context of Debugger Commands.
When you specify an object by selecting it with the mouse, the debugger performs only the action in the second bullet above.
Core File DebuggingDescription
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:
[using memory image in "core" file from program "a.out"] process received signal "Segmentation fault" [11] --> Segmentation Violation (SIGSEGV) code: 0 stopped 2 instructions after "/usr/vc/sbq/tst3/dcore.c":17 in dohicky 17 *x = 3;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:
- Warning: program name from "core" file does not match executable name
The program executable's path name stored in the core file does not match the program executable's path name given on the debugger's invocation line. This does not necessarily indicate that the core file was not produced by the given program. Therefore, this is just a warning.
- => "core" file ignored .. it is older than the executable
The file modification time of the program executable is more recent than the file modification time of the core file. Therefore, the core file was not produced by the executable.
=== memory address is out-of-bounds: 02a520 => cannot read opcode at PC 02a520 - (was the "core" file produced by this program?) => "core" file ignored
The value of the PC register recorded in the core file does not correspond to a valid text address in the program executable (nor does it correspond to a data address in the core file memory image). Therefore, the core file was not produced by the executable.
Rational Software Corporation http://www.rational.com support@rational.com techpubs@rational.com Copyright © 1993-2002, Rational Software Corporation. All rights reserved. |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |