Debugging a Program With dbx

Chapter 3 Viewing and Visiting Code

Each time the program 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 visit, functions and files elsewhere in the program.

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

This chapter is organized into the following sections

Mapping to the Location of the Code

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 one they 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 or change to the new location before debugging.

If you move the source/object files, 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 directory from to directory to type:


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.

Visiting Code

You can visit code elsewhere in the program any time the program is not running. You can visit any function or file that is part of the program.

Visiting a File

You can visit any file dbx recognizes as part of the program (even if a module or file was not compiled with the -g option.) Visiting a file does not change the current function. To visit a file:


pathmap [-c] filename

Using the file command by itself echoes the file you are currently visiting:


file

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


file filename ; list line_number

Visiting Functions

You can use the func command to visit a function. To visit a function, type the command func followed by the function name. The func command by itself echoes the currently visited function. For example:


(dbx) func adjust_speed

Selecting from a List of C++ Ambiguous Function Names

If you try to visit a C++ member function with an ambiguous name or an overloaded function name, a list is displayed, showing all functions with the overloaded name.

If the specified function is ambiguous, type the number of the function you want to visit. If you know which specific class a function belong to, you can type:


(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 block::block
Class block has more than one function member named block.

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 merely echoes the name.

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

Printing a Source Listing

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


list [-i | -instr] [+] [-] number
 [ function | filename
 ]

Walking the Call Stack to Visit Code

Another way to visit code when a live process exists is to "walk the call stack," using the stack commands to view functions currently on the stack.

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.

Qualifying Symbols with Scope Resolution Operators

When using func or file, you may 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 visiting code, symbol name qualifying is also necessary for printing and displaying out-of-scope variables and expressions, and for displaying type and class declarations (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

The backquote character(`) can be used to find a variable of global scope:


(dbx) print `item

A program may 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 knows which function you mean to visit. To qualify a function name with respect to its filename, 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 or top level function with:

You may want to qualify an overloaded function name. If you do not qualify it, dbx pops up an overload display list for you to choose which function you mean to visit. 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:


(dbx) func hand::draw

Block Local Operator

The block local operator (:lineno) is used in conjunction with the backquote operator. It identifies the line number of an expression that references the instance you're interested in.

In the following example, :230 is the block local operator:


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

Linker Names

dbx provides a special syntax for looking up symbols by their linker names (mangled names in C++). You 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

Scope Resolution Search Path

When you issue a debugging command with a symbol target name, the search order is as follows:

  1. dbx first searches within the scope of the current function. 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. The immediately enclosing "compilation unit," generally, the file containing the current function.

  4. The LoadObject scope.

  5. The global scope.

  6. If none of the above searches are successful, dbx assumes you are referencing a private, or file static, variable or function. 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 a variable, it reports an error.

Locating Symbols

In a program, the same name may 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 uses if you give that name as the target of a debugging command.

Printing a List of Occurrences of a Symbol

To print a list of all the occurrences of a specified symbol, use the 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 knows only about occurrences of a symbol that are already loaded. As a debugging session gets longer, the list of occurrences may grow.

Determining Which Symbol dbx Uses

The which command tells you which symbol with a given name it uses if you specify that name (without fully qualifying it) as the target of a debugging command. 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
`example`file1.c`fid
`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 merely echoes the name.

Remember that 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 know which occurrence of two or more names it uses. dbx lists the possibilities and waits for you to choose one.

Viewing Variables, Members, Types, and Classes

dbx's 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.

Looking Up Definitions of Variables, Members, and Functions

To print out the declaration of an identifier:


(dbx) whatis identifier

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

To print out the member function


(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();
Notice that table::pos is inherited from block:
(dbx) whatis table::pos
class point *block::pos();

:

To print out the data member:


(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 tells you 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 lookup the this pointer. In this example, the output from the whatis shows that the compiler automatically allocated this variable to a register.


(dbx) stop in brick::draw
(dbx) cont
// expose the blocks window (if exposed, hide then expose) to force program to hit the breakpoint.
(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

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


(dbx) whatis -t type_or_class_name

To see inherited members the whatis command takes an option -r (for recursive) that displays the declaration of a specified class together with the members it inherits from parent 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. Note the inserted comment lines separating 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 exampleprotected:
    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);
};

Using the Auto-Read Facility

In general, you should compile the entire program you want to debug using the -g option. Depending on how the program was compiled, the debugging information generated for each program and shared library module is stored in either the object code file (.o file) for each program and shared library module, and/or the program executable file.

When you compile with the -g -c compiler option, debugging information for each module remains stored in its .o file. dbx then reads in debugging information for each module automatically, as it is needed, during a session. This read-on-demand facility is called Auto-Read. Auto-Read is the default for dbx.

Auto-Read saves considerable time when loading a large program into dbx. Auto-Read depends on the continued presence of the program .o files in a location known to dbx.


Note -

If you archive .o files into .a files, and then link using the archive libraries, you can then remove the associated .o files, but you must keep the .a files.


By default, dbx looks for files in the directory where they were when the program was compiled and the .o files in the location from which they were linked. If the files are not there, use the pathmap command to set the search path.

If no object file is produced, debugging information is stored in the executable. That is, for a compilation that does not produce .o files, the compiler stores all the debugging information in the executable. The debugging information is read the same way as for applications compiled with the -xs option. See "Debugging Without the Presence of .o Files"."

Debugging Without the Presence of .o Files

Programs compiled with -g -c store debugging information for each module in the module's .o file. Auto-Read requires the continued presence of the program and shared library .o files.

In circumstances where it is not feasible to keep program .o files or shared library .o files for modules that you want to debug, compile the program using the compiler -xs option (use this option in addition to -g). You can have some modules compiled with -xs and some without. The -xs option instructs the compiler to have the linker place all of the debugging information in the program executable; therefore the .o files do not have to be present in order to debug those modules.

In dbx 4.0, the debugging information for modules compiled with the -xs option is loaded during dbx startup. For a large program compiled with -xs, this may cause dbx to start slowly.

In dbx 5.0, the loading of debugging information for modules compiled with -xs is also delayed in the same way as the debugging information stored in .o files. However, you can instruct dbx to load the debugging information for modules compiled with -xs during startup. The dbx environment variable delay_xs lets you turn off the delayed loading of debugging information for modules compiled with -xs. To set the environment variable, add this line to your .dbxrc file:

dbxenv delay_xs off

Listing Modules

The dbx modules 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:


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

To read in debugging information for all modules:


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

-f

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

-q

Quiet mode 

Command Reference

modules

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


modules -read

To list names of all program modules (with or without debugging info):


modules

To list all program modules with debugging info:


modules -debug

To print the name of the current module:


module

whatis

To print out non-type identifiers:


whatis [-n] [-r]

To print out type identifiers:


whatis -t [-r]

To print out expressions:


whatis -e [-r]

list

The default number of lines listed when no number is specified is controlled by the dbxenv variable output_list_size. Where appropriate, the line number may be $ (dollar sign) which denotes the last line of the file. A comma is optional.

To print a specific line number:


list number

To print the next number lines, or the previous number lines, use the plus or minus sign:


list [ + | - ] number

To list lines from one number to another:


list number1 number2

To list the start of the file filename:


list filename

To list the file filename from line number:


list filename:number

To list the start of the source for function:


list function

This command changes the current scope.

To intermix source lines and assembly code:


list -i

To list number lines, a window, around a line or function:


list -w number

This option is not allowed in combination with the + or - syntax or when two line numbers are specified.