C H A P T E R  4

Viewing and Navigating To Code

Each time the program you are debugging stops, dbx prints the source line associated with the stop location. At each program stop, dbx resets the value of the current function to the function in which the program is stopped. Before the program starts running and when it is stopped, you can move to, or navigate through, functions and files elsewhere in the program.

This chapter describes how dbx navigates to code and locates functions and symbols. It also covers how to use commands to navigate to code or look up declarations for identifiers, types, and classes.

This chapter is organized into the following sections


Navigating To Code

When a program is stopped, you can navigate to code elsewhere in the program. You can navigate to any function or file that is part of the program. Navigating sets the current scope (see Program Scope). It is useful for determining when and at what source line you want to set a stop at breakpoint.

Navigating To a File

You can navigate to any file dbx recognizes as part of the program (even if a module or file was not compiled with the -g option). To navigate to a file:


(dbx) file filename

Using the file command without arguments echoes the file name of the file you are currently navigating.


(dbx) file

dbx displays the file from its first line unless you specify a line number.


(dbx) file filename ; list line_number

For information on setting a stop at breakpoint at a line of source code, see Setting a stop Breakpoint at a Line of Source Code.

Navigating To Functions

You can use the func command to navigate to a function. To navigate to a function, type the command func followed by the function name. For example:


(dbx) func adjust_speed

The func command by itself echoes the current function.

For more information, see func Command

Selecting From a List of C++ Ambiguous Function Names

If you try to navigate to a C++ member function with an ambiguous name or an overloaded function name, a list is displayed, showing all functions with the overloaded name. Type the number of the function you want to navigate. If you know which specific class a function belongs to, you can type the class name and function name. For example:


(dbx) func block::block

Choosing Among Multiple Occurrences

If multiple symbols are accessible from the same scope level, dbx prints a message reporting the ambiguity.


(dbx) func main
(dbx) which C::foo
More than one identifier 'foo'.
Select one of the following:
 0) Cancel
 1) `a.out`t.cc`C::foo(int)
 2) `a.out`t.cc`C::foo()
>1
`a.out`t.cc`C::foo(int)

In the context of the which command, choosing from the list of occurrences does not affect the state of dbx or the program. Whichever occurrence you choose, dbx echoes the name.

Printing a Source Listing

Use the list command to print the source listing for a file or function. Once you navigate through a file, list prints number lines from the top. Once you navigate through a function, list prints its lines.

For detailed information on the list command, see list Command.

Walking the Call Stack to Navigate To Code

Another way to navigate to code when a live process exists is to "walk the call stack," using the stack commands to view functions currently on the call stack, which represent all currently active routines. Walking the stack causes the current function and file to change each time you display a stack function. The stop location is considered to be at the "bottom" of the stack, so to move away from it, use the up command, that is, move toward the main or begin function. Use the down command to move toward the current frame.

For more information on walking the call stack, see Walking the Stack and Returning Home.


Types of Program Locations

dbx uses three global locations to track the parts of the program you are inspecting:


Program Scope

A scope is a subset of the program defined in terms of the visibility of a variable or function. A symbol is said to be "in scope" if its name is visible at a given point of execution. In C, functions may have global or file-static scope; variables may have global, file-static, function, or block scope.

Variables That Reflect the Current Scope

The following variables always reflect the current program counter of the current thread or LWP, and are not affected by the various commands that change the visiting scope:


$scope

Scope of the current program counter

$lineno

Current line number

$func

Current function

$class

Class to which $func belongs

$file

Current source file

$loadobj

Current loadobject


Visiting Scope

When you inspect various elements of your program with dbx, you modify the visiting scope. dbx uses the visiting scope during expression evaluation for purposes such as resolving ambiguous symbols. For example, if you type the following command, dbx uses the visiting scope to determine which i to print.


(dbx) print i

Each thread or LWP has its own visiting scope. When you switch between threads, each thread remembers its visiting scope.

Components of the Visiting Scope

Some of the components of the visiting scope are visible in the following predefined ksh variables:


$vscope

Language scope

$vloadobj

Current visiting loadobject

$vfile

Current visiting source file

$vfunc

Current visiting function

$vlineno

Current visiting line number

$vclass

C++ class


All of the components of the current visiting scope stay compatible with one another. For example, if you visit a file that contains no functions, the current visiting source file is updated to the new file name and the current visiting function is updated to NULL.

Changing the Visiting Scope

The following commands are the most common ways of changing the visiting scope:

The debug command and the attach command set the initial visiting scope.

When you hit a breakpoint, dbx sets the visiting scope to the current location. If the stack_find_source environment variable (see Setting dbx Environment Variables) is set to ON, dbx attempts to find and make active a stack frame that has source code.

When you use the up command (see up Command), the down command (down Command), the frame number command (see frame Command), or the pop command (see pop Command)to change the current stack frame, dbx sets the visiting scope according to the program counter from the new stack frame.

The line number location used by the list command (see list Command) changes the visiting scope only if you use the list function or list file command. When the visiting scope is set, the line number location for the list command is set to the first line number of the visiting scope. When you subsequently use the list command, the current line number location for the list command is updated, but as long as you are listing lines in the current file, the visiting scope does not change. For example, if you type the following, dbx lists the start of the source for my_func, and changes the visiting scope to my_func.


(dbx) list my_func

If you type the following, dbx lists line 127 in the current source file, and does not change the visiting scope.


(dbx) list 127

When you use the file command or the func command to change the current file or the current function, the visiting scope is updated accordingly.


Qualifying Symbols With Scope Resolution Operators

When using the func or file command, you might need to use scope resolution operators to qualify the names of the functions that you give as targets.

dbx provides three scope resolution operators with which to qualify symbols: the backquote operator (`), the C++ double colon operator (::), and the block local operator (:lineno). You use them separately or, in some cases, together.

In addition to qualifying file and function names when navigating through code, symbol name qualifying is also necessary for printing and displaying out-of-scope variables and expressions, and for displaying type and class declarations (using the whatis command). The symbol qualifying rules are the same in all cases; this section covers the rules for all types of symbol name qualifying.

Backquote Operator

Use the backquote character (`) to find a variable or function of global scope:


(dbx) print `item

A program can use the same function name in two different files (or compilation modules). In this case, you must also qualify the function name to dbx so that it registers which function you will navigate. To qualify a function name with respect to its file name, use the general purpose backquote (`) scope resolution operator.


(dbx) func `file_name`function_name

C++ Double Colon Scope Resolution Operator

Use the double colon operator (::) to qualify a C++ member function, a top level function, or a variable with global scope with:

You might want to qualify an overloaded function name. If you do not qualify it, dbx displays an overload list so you can choose which function you will navigate. If you know the function class name, you can use it with the double colon scope resolution operator to qualify the name.


(dbx) func class::function_name (args)

For example, if hand is the class name and draw is the function name, type:


(dbx) func hand::draw

Block Local Operator

The block local operator (:line_number) allows you to refer specifically to a variable in a nested block. You might want to do so if you have a local variable shadowing a parameter or member name, or if you have several blocks, each with its own version of a local variable. The line_number is the number of the first line of code within the block for the variable of interest. When dbx qualifies a local variable with the block local operator, dbx uses the line number of the first block of code, but you can use any line number within the scope in dbx expressions.

In the following example, the block local operator (:230) is combined with the backquote operator.


(dbx) stop in `animate.o`change_glyph:230`item

The following example shows how dbx evaluates a variable name qualified with the block local operator when there are multiple occurrences in a function.


(dbx) list 1,$
    1   #include <stddef.h>
    2   
    3   int main(int argc, char** argv) {
    4   
    5   int i=1;
    6   
    7       {
    8            int i=2;
    9            {
   10                   int j=4;
   11                   int i=3;
   12                   printf("hello");
   13            }
   14            printf("world\n");
   15       }
   16       printf("hi\n");
   17   }
   18 
(dbx) whereis i
variable:	`a.out`t.c`main`i
variable:	`a.out`t.c`main:8`i
variable:	`a.out`t.c`main:10`i
(dbx) stop at 12 ; run
...
(dbx) print i
i = 3
(dbx) which i
`a.out`t.c`main:10`i
(dbx) print `main:7`i 
`a.out`t.c`main`i = 1
(dbx) print `main:8`i
`a.out`t.c`main:8`i = 2
(dbx) print `main:10`i
`a.out`t.c`main:10`i = 3
(dbx) print `main:14`i 
`a.out`t.c`main:8`i = 2
(dbx) print `main:15`i 
`a.out`t.c`main`i = 1

Linker Names

dbx provides a special syntax for looking up symbols by their linker names (mangled names in C++). Prefix the symbol name with a # (pound sign) character (use the ksh escape character \ (backslash) before any $ (dollar sign) characters), as in these examples:


(dbx) stop in #.mul
(dbx) whatis #\$FEcopyPc
(dbx) print `foo.c`#staticvar


Locating Symbols

In a program, the same name might refer to different types of program entities and occur in many scopes. The dbx whereis command lists the fully qualified name--hence, the location--of all symbols of that name. The dbx which command tells you which occurrence of a symbol dbx would use if you give that name in an expression (see which Command).

Printing a List of Occurrences of a Symbol

To print a list of all the occurrences of a specified symbol, use whereis symbol, where symbol can be any user-defined identifier. For example:


(dbx) whereis table
forward: `Blocks`block_draw.cc`table
function: `Blocks`block.cc`table::table(char*, int, int, const point&)
class: `Blocks`block.cc`table
class: `Blocks`main.cc`table
variable:       `libc.so.1`hsearch.c`table

The output includes the name of the loadable object(s) where the program defines symbol, as well as the kind of entity each object is: class, function, or variable.

Because information from the dbx symbol table is read in as it is needed, the whereis command registers only occurrences of a symbol that are already loaded. As a debugging session gets longer, the list of occurrences can grow (see Debugging Information in Object files and Executables).

For more information, see whereis Command.

Determining Which Symbol dbx Uses

The which command tells you which symbol with a given name dbx uses if you specify that name (without fully qualifying it) in an expression. For example:


(dbx) func
wedge::wedge(char*, int, int, const point&, load_bearing_block*)
(dbx) which draw
`block_draw.cc`wedge::draw(unsigned long)

If a specified symbol name is not in a local scope, the which command searches for the first occurrence of the symbol along the scope resolution search path. If which finds the name, it reports the fully qualified name.

If at any place along the search path, the search finds multiple occurrences of symbol at the same scope level, dbx prints a message in the command pane reporting the ambiguity.


(dbx) which fid
More than one identifier 'fid'.
Select one of the following:
 0) Cancel
 1) `example`file1.c`fid
 2) `example`file2.c`fid

dbx shows the overload display, listing the ambiguous symbols names. In the context of the which command, choosing from the list of occurrences does not affect the state of dbx or the program. Whichever occurrence you choose, dbx echoes the name.

The which command gives you a preview of what happens if you make symbol (in this example, block) an argument of a command that must operate on symbol (for example, a print command). In the case of ambiguous names, the overload display list indicates that dbx does not yet register which occurrence of two or more names it uses. dbx lists the possibilities and waits for you to choose one. For more information on the which command, see which Command.

Scope Resolution Search Path

When you issue a debugging command that contains an expression, the symbols in the expression are looked up in the following order. dbx resolves the symbols as the compiler would at the current visiting scope.

1. Within the scope of the current function using the current visiting scope (see Visiting Scope). If the program is stopped in a nested block, dbx searches within that block, then in the scope of all enclosing blocks.

2. For C++ only: class members of the current function`s class and its base class.

3. For C++ only: the current name space.

4. The parameters of the current function.

5. The immediately enclosing module, generally, the file containing the current function.

6. Symbols that were made private to this shared library or executable. These symbols can be created using linker scoping.

7. Global symbols for the main program, and then for shared libraries.

8. If none of the above searches are successful, dbx assumes you are referencing a private, or file static, variable or function in another file. dbx optionally searches for a file static symbol in every compilation unit depending on the value of the dbxenv setting scope_look_aside.

dbx uses whichever occurrence of the symbol it first finds along this search path. If dbx cannot find the symbol, it reports an error.

Relaxing the Scope Lookup Rules

To relax the scope lookup rules for static symbols and C++ member functions, set the dbx environment variable scope_look_aside to on:

dbxenv scope_look_aside on

or use the "double backquote" prefix:

stop in ``func4			func4 may be static and not in scope

If the dbx environment variable scope_look_aside is set to on, dbx looks for:

The which command tells you which symbol dbx would choose. In the case of ambiguous names, the overload display list indicates that dbx has not yet determined which occurrence of two or more names it would use. dbx lists the possibilities and waits for you to choose one.

For more information, see func Command.


Viewing Variables, Members, Types, and Classes

The whatis command prints the declarations or definitions of identifiers, structs, types and C++ classes, or the type of an expression. The identifiers you can look up include variables, functions, fields, arrays, and enumeration constants.

For more information, see whatis Command.

Looking Up Definitions of Variables, Members, and Functions

To print out the declaration of an identifier, type:


(dbx) whatis identifier

Qualify the identifier name with file and function information as needed.

For C++ programs, whatis identifier lists function template instantiations. Template definitions are displayed with whatis -t identifier. See Looking Up Definitions of Types and Classes.

For Java programs, whatis identifier, lists the declaration of a class, a method in the current class, a local variable in the current frame, or a field in the current class

To print out the member function, type:


(dbx) whatis block::draw
void block::draw(unsigned long pw);
(dbx) whatis table::draw
void table::draw(unsigned long pw);
(dbx) whatis block::pos 
class point *block::pos();
(dbx) whatis table::pos
class point *block::pos();

To print out the data member, type:


(dbx) whatis block::movable
int movable;

On a variable, whatis tells you the variable`s type.


(dbx) whatis the_table
class table *the_table;

On a field, whatis gives the field`s type.


(dbx) whatis the_table->draw
void table::draw(unsigned long pw);

When you are stopped in a member function, you can look up the this pointer.


(dbx) stop in brick::draw
(dbx) cont
(dbx) where 1
brick::draw(this = 0x48870, pw = 374752), line 124 in
     "block_draw.cc"
(dbx) whatis this
class brick *this;

Looking Up Definitions of Types and Classes

The -t option of the whatis command displays the definition of a type. For C++, the list displayed by whatis -t includes template definitions and class template instantiations.

To print the declaration of a type or C++ class, type:


(dbx) whatis -t type_or_class_name

To see inherited members, the whatis command takes an -r option (for recursive) that displays the declaration of a specified class together with the members it inherits from base classes.


(dbx) whatis -t -r  class_name

The output from a whatis -r query may be long, depending on the class hierarchy and the size of the classes. The output begins with the list of members inherited from the most ancestral class. The inserted comment lines separate the list of members into their respective parent classes.

Here are two examples, using the class table, a child class of the parent class load_bearing_block, which is, in turn, a child class of block.

Without -r, whatis reports the members declared in class table:


(dbx) whatis -t class table
class table : public load_bearing_block {
public:
    table::table(char *name, int w, int h, const class point &pos);
    virtual char *table::type();
    virtual void table::draw(unsigned long pw);
};

Here are results when whatis -r is used on a child class to see members it inherits:


(dbx) whatis -t -r class table
class table : public load_bearing_block {
public:
  /* from base class table::load_bearing_block::block */
  block::block();
  block::block(char *name, int w, int h, const class point &pos, class load_bearing_block *blk);
    virtual char *block::type();
    char *block::name();
    int block::is_movable();
//  deleted several members from example protected:
    char *nm;
    int movable;
    int width;
    int height;
    class point  position;
    class load_bearing_block *supported_by;
    Panel_item panel_item;
    /* from base class table::load_bearing_block */
public:
    load_bearing_block::load_bearing_block();
    load_bearing_block::load_bearing_block(char *name, int w, int h,const class point &pos, class load_bearing_block *blk);
    virtual int load_bearing_block::is_load_bearing();
    virtual class list *load_bearing_block::supported_blocks();
    void load_bearing_block::add_supported_block(class block &b);
    void load_bearing_block::remove_supported_block(class block &b);
    virtual void load_bearing_block::print_supported_blocks();
    virtual void load_bearing_block::clear_top();
    virtual void load_bearing_block::put_on(class block &object);
    class point load_bearing_block::get_space(class block &object);
    class point load_bearing_block::find_space(class block &object);
    class point load_bearing_block::make_space(class block &object);
protected:
    class list *support_for;
    /* from class table */
public:
    table::table(char *name, int w, int h, const class point &pos);
    virtual char *table::type();
    virtual void table::draw(unsigned long pw);
};


Debugging Information in Object files and Executables

Generally, you want to compile your source files with the -g option to make your program more debuggable. The -g option causes the compilers to record debugging information (in stabs or Dwarf format) into the object files along with the code and data for the program.

dbx parses and loads debugging information for each object file (module) on demand, when the information is needed. You can ask dbx to load debug information for any specific module, or for all modules, by using the module command. See also Finding Source and Object Files.

Object File Loading

When the object (.o) files are linked together, the linker can optionally store only summary information into the resulting loadobject. This summary information can be used by dbx at runtime to load the rest of the debug information from the object files themselves instead of from the executable file. The resulting executable has a smaller disk-footprint, but requires that the object files be available when dbx runs.

You can override this requirement by compiling object files with the -xs option to cause all the debugging information for those object files to be put into the executable at link time.

If you create archive libraries (.a files) with your object files, and use the archive libraries in your program, then dbx extracts the object files from the archive library as needed. The original object files are not needed at that point.

The only drawback to putting all the debugging information into the executable file is using additional disk space. The program does not run more slowly, because the debugging information is not loaded into the process image at run time.

The default behavior when using stabs (the default format for debugging information) is for the compiler to put only summary information into the executable.

The DWARF format doesn't yet support object file loading.



Note - The DWARF format is significantly more compact than recording the same information in stabs format. However, because all the information is copied into the executable, DWARF information can appear to be larger than stabs information.



Listing Debugging Information for Modules

The module command and its options help you to keep track of program modules during the course of a debugging session. Use the module command to read in debugging information for one or all modules. Normally, dbx automatically and "lazily" reads in debugging information for modules as needed.

To read in debugging information for a module name, type:


(dbx) module [-f] [-q] name

To read in debugging information for all modules, type:


(dbx) module [-f] [-q] -a

where:


-a

Specifies all modules.

-f

Forces reading of debugging information, even if the file is newer than the executable.

-q

Specifies quiet mode.

-v

Specifies verbose mode, which prints language, file names, and so on. This is the default.


To print the name of the current module, type:


(dbx) module

Listing Modules

The modules command helps you keep track of modules by listing module names.

To list the names of modules containing debugging information that have already been read into dbx, type:


(dbx) modules [-v] -read

To list names of all program modules (whether or not they contain debugging information), type:


(dbx) modules [-v]

To list all program modules that contain debugging information, type:


(dbx) modules [-v] -debug

where:


-v

Specifies verbose mode, which prints language, file names, and so on.



Finding Source and Object Files

dbx must know the location of the source and object code files associated with a program. The default directory for the object files is the directory the files were in when the program was last linked. The default directory for the source files is the one they were in when last compiled. If you move the source or object files, or copy them to a new location, you must either relink the program, change to the new location before debugging, or use the pathmap command.

dbx sometimes uses object files to load additional debugging information. Source files are used when dbx displays source code.

If you have moved the source files or object files since you compiled and linked the program, you can add their new location to the search path. The pathmap command creates a mapping from your current view of the file system to the name in the executable image. The mapping is applied to source paths and object file paths.

To establish a new mapping from the directory from to the directory to:


(dbx) pathmap [-c] from to

If -c is used, the mapping is applied to the current working directory as well.

The pathmap command is also useful for dealing with automounted and explicit NFS mounted file systems with different base paths on differing hosts. Use -c when you try to correct problems due to the automounter because current working directories are inaccurate on automounted file systems.

The mapping of /tmp_mnt to / exists by default.

For more information, see pathmap Command.