Sun Studio 12: Debugging a Program With dbx

Chapter 2 Starting dbx

This chapter explains how to start, execute, save, restore, and quit a dbx debugging session. It contains the following sections:

Starting a Debugging Session

How you start dbx depends on what you are debugging, where you are, what you need dbx to do, how familiar you are with dbx, and whether or not you have set up any dbx environment variables.

The simplest way to start a dbx session is to type the dbx command at a shell prompt.


$ dbx

To start dbx from a shell and load a program to be debugged, type:


$ dbx program_name

To start dbx and load a program that is a mixture of Java code and C JNI code or C++ JNI code:


$ dbx program_name{.class | .jar}

Sun Studio software includes two dbx binaries, a 32-bit dbx that can debug 32-bit programs only, and a 64-bit dbx that can debug both 32-bit and 64-bit programs. When you start dbx, it determines which of its binaries to execute. On 64-bit operating systems, the 64-bit dbx is the default. To start the 32-bit dbx on a 64-bit OS, use the dbx command with the -x exec32 option (see Options) or set the _DBX_EXEC_32 environment variable).


Note –

On the Linux OS, the 64-bit dbx cannot debug 32-bit programs. To debug a 32-bit program on the Linux OS, you must start the 32-bit dbx.



Note –

When using the 32-bit dbx on a 64-bit Linux OS, do not use the debug command or set the follow_fork_mode environment variable to child if the result will be execution of a 64-bit program. Exit dbx and start the 64-bit dbx to debug a 64-bit program.


For more information on the dbx command and start-up options, see dbx Command and the dbx(1) man page.

Debugging a Core File

If the program that dumped core was dynamically linked with any shared libraries, it is best to debug the core file in the same operating environment in which it was created. dbx has limited support for the debugging of “mismatched” core files (for example, core files produced on a system running a different version or patch level of the Solaris Operating System.


Note –

dbx cannot tell you the state of a Java application from a core file as it can with native code.


Debugging a Core File in the Same Operating Environment

To debug a core file, type:


$ dbx program_name core

You can also debug a core file using the debug command when dbx is already running:


(dbx) debug -c core program_name

You can substitute - for the program name and dbx will attempt to extract the program name from the core file. dbx might not find the executable if its full path name is not available in the core file. If dbx does not find the executable, specify the complete path name of the binary when you tell dbx to load the core file.

If the core file is not in the current directory, you can specify its path name (for example, /tmp/core).

Use the where command (see where Command) to determine where the program was executing when it dumped core.

When you debug a core file, you can also evaluate variables and expressions to see the values they had at the time the program crashed, but you cannot evaluate expressions that make function calls. You cannot single step or set breakpoints.

If Your Core File Is Truncated

If you have problems loading a core file, check whether you have a truncated core file. If you have the maximum allowable size of core files set too low when the core file is created, then dbx cannot read the resulting truncated core file. In the C shell, you can set the maximum allowable core file size using the limit command (see the limit(1) man page). In the Bourne shell and Korn shell, use the ulimit command (see the limit(1) man page). You can change the limit on core file size in your shell start-up file, re-source the start-up file, and then rerun the program that produced the core file to produce a complete core file.

If the core file is incomplete, and the stack segment is missing, then stack trace information is not available. If the runtime linker information is missing, then the list of loadobjects is not available. In this case, you get an error message about librtld_db.so not being initialized. If the list of LWPs is missing, then no thread information, lwp information, or stack trace information is available.If you run the where command, you get an error saying the program was not “active.”

Debugging a Mismatched Core File

Sometimes a core file is created on one system (the core-host) and you want to load the core file on another machine (the dbx-host) to debug it. However, two problems with libraries might arise when you do so:

The user libraries and system libraries can change in patches as well as major Solaris operating environment upgrades, so this problem can even occur on the same host, if, for example, a patch was installed after the core file was collected, but before running dbx on the core file.

dbx might display one or more of the following error messages when you load a “mismatched” core file:


dbx: core file read error: address 0xff3dd1bc not available
dbx: warning: could not initialize librtld_db.so.1 -- trying libDP_rtld_db.so
dbx: cannot get thread info for 1 -- generic libthread_db.so error
dbx: attempt to fetch registers failed - stack corrupted
dbx: read of registers from (0xff363430) failed -- debugger service failed

Eliminating Shared Library Problems

ProcedureTo Eliminate the Library Problems and Debug a “mismatched” Core File

  1. Set the dbx environment variable core_lo_pathmap to on.

  2. Use the pathmap command to tell dbx where the correct libraries for the core file are located.

  3. Use the debug command to load the program and the core file.

    For example, assuming that the root partition of the core-host has been exported over NFS and can be accessed via /net/core-host/ on the dbx-host machine, you would use the following commands to load the program prog and the core file prog.core for debugging:


    (dbx) dbxenv core_lo_pathmap on
    (dbx) pathmap /usr /net/core-host/usr
    (dbx) pathmap /appstuff /net/core-host/appstuff
    (dbx) debug prog prog.core
    

    If you are not exporting the root partition of the core-host, you must copy the libraries by hand. You need not re-create the symbolic links. (For example, you need not make a link from libc.so to libc.so.1; just make sure libc.so.1 is available.)

Things to Remember

Keep the following things in mind when debugging a mismatched core file:

Using the Process ID

You can attach a running process to dbx using the process ID as an argument to the dbx command.


$ dbx program_name process_id

To attach dbx to a running process that includes JavaTM code and C JNI (Java Native Interface) code or C++ JNI code:


$ dbx program_name{.class | .jar} process_id

You can also attach to a process using its process ID without knowing the name of the program.


$ dbx - process_id

Because the program name remains unknown to dbx, you cannot pass arguments to the process in a run command.

For more information, see Attaching dbx to a Running Process.

The dbx Startup Sequence

When you start dbx,if you do not specify the -S option, dbx looks for the installed startup file, dbxrc, in the directory /installation_directory/lib. (The default installation_directory is /opt/SUNWspro on Solaris platforms and /opt/sun/sunstudio10u1 on Linux platforms.) If your Sun Studio software is not installed in the default directory, dbx derives the path to the dbxrc file from the path to the dbx executable.

Then dbx searches for a .dbxrc file in the current directory, then in $HOME. You can specify a different startup file than .dbxrc explicitly by specifying the file path using the -s option. For more information, see Using the dbx Initialization File.

A startup file may contain any dbx command, and commonly contains the alias command, dbxenv command, pathmap command, and Korn shell function definitions. However, certain commands require that a program has been loaded or a process has been attached to. All startup files are loaded before the program or process is loaded. The startup file may also source other files using the source or .(period) command. You can also use the startup file to set other dbx options.

As dbx loads program information, it prints a series of messages, such as Reading filename.

Once the program is finished loading, dbx is in a ready state, visiting the “main” block of the program (for C or C++: main(); for Fortran 95: MAIN()). Typically, you set a breakpoint (for example, stop in main) and then issue a run command for a C program.

Setting Startup Properties

You can use the pathmap command, dbxenv command, and alias command to set startup properties for your dbx sessions.

Mapping the Compile-time Directory to the Debug-time Directory

By default, dbx looks in the directory in which the program was compiled for the source files associated with the program being debugged. If the source or object files are not there or the machine you are using does not use the same path name, you must inform dbx of their location.

If you move the source or 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.

Add common pathmaps to your .dbxrc file.

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


(dbx) pathmap [ -c ] from to

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

The pathmap command is 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.

Setting dbx Environment Variables

You can use the dbxenv command to either list or set dbx customization variables. You can place dbxenv commands in your .dbxrc file. To list variables, type:


$ dbxenv

You can also set dbx environment variables. See Saving and Restoring Using replay for more information about the .dbxrc file and about setting these variables.

For more information, see Setting dbx Environment Variables and dbxenv Command.

Creating Your Own dbx Commands

You can create your own dbx commands using the kalias or dalias commands. For more information, see dalias Command.

Compiling a Program for Debugging

You must prepare your program for debugging with dbx by compiling it with the- g or -g0 option.

Compiling with the -g Option

The -g option instructs the compiler to generate debugging information during compilation.

For example, to compile using C++, type:


% CC -g example_source.cc

In C++, the -g option turns on debugging and turns off inlining of functions. The- g0 (zero) option turns on debugging and does not affect inlining of functions. You cannot debug inline functions with the -g0 option. The -g0 option can significantly decrease link time and dbx start-up time (depending on the use of inlined functions by the program).

To compile optimized code for use with dbx, compile the source code with both the -O (uppercase letter O) and the -g options.

Using a Separate Debug File

dbx lets you use options in the objcopy command on Linux platforms and the gobjcopy command on Solaris platforms to copy the debugging information from an executable to a separate debug file, strip that information from the executable, and create a link between these two files.

dbx searches for the separate debug file in the following order and reads the debugging information from the first file it finds:

For example, to create a separate debug file for executable a.out, you would do the following.

ProcedureCreating a Separate Debug File

  1. Create a separate debug file named a.out.debug containing the debugging information.

  2. Strip the debugging information from a.out.

  3. Establish the link between the two files. On Solaris platforms, use the gobjcopy command. On Linux platforms, use the objcopy command.

    On a Linux platform, you can use the command objcopy -help to find out whether or not the -add-gnu-debuglink option is supported on the platform. You can replace the -only-keep-debug option of the objcopy command with the command cp a.out a.out.debug to make a.out.debug a fully executable file.

Debugging Optimized Code

dbx provides partial debugging support for optimized code. The extent of the support depends largely upon how you compiled the program.

When analyzing optimized code, you can:

When programs are compiled with optimization and debugging enabled at the same time (using the -O -g options), dbx operates in a restricted mode.

The details about which compilers emit which kind of symbolic information under what circumstances is considered an unstable interface and is likely to change from release to release.

Source line information is available, but the code for one source line might appear in several different places for an optimized program, so stepping through a program by source line results in the “current line” jumping around in the source file, depending on how the code was scheduled by the optimizer.

Tail call optimization can result in missing stack frames when the last effective operation in a function is a call to another function.

Generally, symbolic information for parameters, local variables, and global variables is available for optimized programs. Type information about structs, unions, C++ classes, and the types and names of local variables, global variables, and parameters should be available. The C++ compiler does not provide symbolic type information about local variables; the C compiler does.

Information about the location of parameters and local variables is often missing in optimized code. If the compiler generates Dwarf location lists, dbx uses this information to print the values of local variables and parameters. If you stop at the first instruction of an optimized function, dbx can print the values of parameters, because the values will be in ABI-conforming registers or stack locations. To see the values of local variables, you might also need to set the dbx environment variable optim_local_vars.

A simple location in the debug information describes one register or stack location. A location list describes different locations for a variable at different points in the code, which makes location lists better for describing optimized code.

Sun Studio compilers, starting with the Sun Studio 12 release, do not emit location lists, but they do produce simple locations for some optimized code. Newer versions of GNU compilers use location lists for describing the frame pointer, and for describing some parameters and local variables. How this information is recorded by the compiler can change from release to release.

You can print global variables and assign values to them, although they might have inaccurate values if the final register-to-memory store has not happened yet.

Chapter 8 of the Sun Studio 11 Performance Analyzer manual includes information on compiler optimizations that might be helpful when debugging an optimized program.

For OpenMP programs, compiling with the -xopenmp=noopt option instructs the compiler not to apply any optimizations. However, the optimizer still processes the code in order to implement the OpenMP directives, so some of the problems described might occur in programs compiled with -xopenmp=noopt.

Code Compiled Without the -g Option

While most debugging support requires that a program be compiled with -g, dbx still provides the following level of support for code compiled without -g:

Note, however, that dbx cannot display source code unless the code was compiled with the -g option. This restriction also applies to code that has had strip -x applied to it.

Shared Libraries Require the -g Option for Full dbx Support

For full support, a shared library must also be compiled with the -g option. If you build a program with shared library modules that were not compiled with the -g option, you can still debug the program. However, full dbx support is not possible because the information was not generated for those library modules.

Completely Stripped Programs

The dbx tool can debug programs that have been completely stripped. These programs contain some information that can be used to debug your program, but only externally visible functions are available. Some runtime checking works on stripped programs or load objects: memory use checking works, and access checking works with code stripped with strip -x but not with code stripped with strip.

Quitting Debugging

A dbx session runs from the time you start dbx until you quit dbx; you can debug any number of programs in succession during a dbx session.

To quit a dbx session, type quit at the dbx prompt.


(dbx) quit

When you start dbx and attach it to a running process using the process_id option, the process survives and continues when you quit the debugging session. dbx performs an implicit detach before quitting the session.

Stopping a Process Execution

You can stop execution of a process at any time by pressing Ctrl+C without leaving dbx.

Detaching a Process From dbx

If you have attached dbx to a process, you can detach the process from dbx without killing it or the dbx session by using the detach command.

To detach a process from dbx without killing the process, type:


(dbx) detach

You can detach a process and leave it in a stopped state while you temporarily apply other /proc-based debugging tools that might be blocked when dbx has exclusive access. For more information, see Detaching dbx From a Process.

For more information on the detach command, see detach Command.

Killing a Program Without Terminating the Session

The dbx kill command terminates debugging of the current process as well as killing the process. However, the kill command preserves the dbx session itself leaving dbx ready to debug another program.

Killing a program is a good way of eliminating the remains of a program you were debugging without exiting dbx.

To kill a program executing in dbx, type:


(dbx) kill

For more information, see kill Command.

Saving and Restoring a Debugging Run

The dbx tool provides three commands for saving all or part of a debugging run and replaying it later:

Using the save Command

The save command saves to a file all debugging commands issued from the last run command, rerun command, or debug command up to the save command. This segment of a debugging session is called a debugging run.

The save command saves more than the list of debugging commands issued. It saves debugging information associated with the state of the program at the start of the run—breakpoints, display lists, and the like. When you restore a saved run, dbx uses the information in the save-file.

You can save part of a debugging run; that is, the whole run minus a specified number of commands from the last one entered.

 

debug

 

debug

 

stop at line

 

stop at line

 

run

 

run

 

next

 

next

 

next

 

next

Saving a complete run 

stop at line

Saving a run minus  

stop at line

 

continue

the last two steps 

continue

 

next

 

next

 

next

 

next

 

step

 

step

 

next

 

next

 

save

 

save-2

       

If you are not sure where you want to end the run you are saving, use the history command to see a list of the debugging commands issued since the beginning of the session.


Note –

By default, the save command writes information to a special save-file. If you want to save a debugging run to a file you can restore later, you can specify a file name with the save command. See Saving a Series of Debugging Runs as Checkpoints .


To save an entire debugging run up to the save command, type:


(dbx) save

To save part of a debugging run, use the save number command, where number is the number of commands back from the save command that you do not want saved.


(dbx) save -number

Saving a Series of Debugging Runs as Checkpoints

If you save a debugging run without specifying a file name, dbx writes the information to a special save-file. Each time you save, dbx overwrites this save-file. However, by giving the save command a filename argument, you can save a debugging run to a file that you can restore later, even if you have saved other debugging runs since the one saved to filename.

Saving a series of runs gives you a set of checkpoints, each one starting farther back in the session. You can restore any one of these saved runs, continue, then reset dbx to the program location and state saved in an earlier run.

To save a debugging run to a file other than the default save-file:


(dbx) save filename

Restoring a Saved Run

After saving a run, you can restore the run using the restore command. dbx uses the information in the save-file. When you restore a run, dbx first resets the internal state to what it was at the start of the run, then reissues each of the debugging commands in the saved run.


Note –

The source command also reissues a set of commands stored in a file, but it does not reset the state of dbx; it only reissues the list of commands from the current program location.


Prerequisites for an Exact Restoration of a Saved Run

For exact restoration of a saved debugging run, all the inputs to the run must be exactly the same: arguments to a run-type command, manual inputs, and file inputs.


Note –

If you save a segment and then issue a run, rerun, or debug command before you do a restore, restore uses the arguments to the second, post-save run, rerun, or debug command. If those arguments are different, you might not get an exact restoration.


To restore a saved debugging run, type:


(dbx) restore

To restore a debugging run saved to a file other than the default save-file, type:


(dbx) restore filename

Saving and Restoring Using replay

The replay command is a combination command, equivalent to issuing a save -1 followed immediately by a restore. The replay command takes a negative number argument, which it passes to the save portion of the command. By default, the value of -number is -1, so the replay command works as an undo command, restoring the last run up until, but not including, the last command issued.

To replay the current debugging run, minus the last debugging command issued, type:


(dbx) replay

To replay the current debugging run and stop the run before a specific command, use the dbx replay command, where number is the number of commands back from the last debugging command.


(dbx) replay -number