Chapter 6. Controlling Program Execution

A program typically runs until it exits or encounters an unrecoverable error. You can use dbx, however, to stop a program under various conditions, step through your program line by line, stop execution on receiving a signal, and execute conditional commands based on your program's status.

This chapter covers:

Setting Breakpoints

Breakpoints allow you to stop execution of your program. Breakpoints can be unconditional, in which case they always stop your program, or conditional, in which case they stop your program only if a test condition that you specify is true.


Note: All breakpoints halt program execution before executing the line on which they are set. Therefore, if you want to examine the effects of a line of code, you should set the breakpoint on the line of code following the one whose effects you want to study.

Each breakpoint is assigned a number when you create it. Use this number to reference a breakpoint in the various commands provided for manipulating breakpoints (for example, disable, enable, and delete, all described in "Managing Breakpoints, Traces, and Conditional Commands").

Setting Unconditional Breakpoints

To set an unconditional breakpoint, you simply specify the point at which you want to stop program execution, using one of the following forms of the stop command:

stop at 

Sets a breakpoint at the current source line.

stop at line 

Sets a breakpoint at the specified source line in the current source file.

stop in procedure  


Sets a breakpoint to stop execution upon entering the specified procedure. Execution will stop in all inlined or cloned instances of the procedure.

stop at file:line 


Sets a breakpoint in the specified file at the specified line.


Caution: If your program has multiple source files, be sure to set the breakpoint in the correct file. To do so, you can explicitly set the source file using dbx's file command (see "Changing Source Files") or you can use the func command to go to a source file containing a specified function (see "Moving to a Specified Procedure").


Setting Conditional Breakpoints

An unconditional breakpoint is the simplest type of breakpoint; your program stops every time it reaches a specified place. On the other hand, a conditional breakpoint stops your program only if a condition that you specify is true. The two conditions that you can test are:

  • Has the value of a variable or other memory location changed?

  • Is a test expression true?

Stopping If a Variable or Memory Location Has Changed

By including a variable clause in your stop command, you can cause dbx to stop if the value of a variable or the contents of a memory location has changed.

If you provide only a variable name in your variable clause, the breakpoint stops your program if the value of the variable has changed since the last time dbx checked it. If instead of a variable name, you provide an expression of type pointer, dbx checks the data pointed to. If the data pointed to is a structure, dbx checks that structure. If you provide an expression that's not of type pointer, dbx evaluates the expression and uses the result as an address in memory. The breakpoint stops your program if the contents of the memory location (32 bits) has changed since the last time dbx checked it.

The points at which dbx checks the value of a variable or memory location depend on the command that you use to set the breakpoint:

stop [expression|variable]  


Inspects the value before executing each source line. If the expression is of type pointer, look at the data pointed to and watch until it changes.

If the expression is not of type pointer, look at the 32 bits at that address (assume the expression evaluates to an address). For example, consider the command:

stop (struct s*) 0x12345678

This command checks the contents of the structure located at 0x12345678.

stop [expression|variable] at line  


Inspects the value at the given source line. Stops if the value has changed.

If the expression is of type pointer, look at the data pointed to and watch until it changes. If the expression is not of type pointer, look at the 32 bits at that address (assume the expression evaluates to an address).

stop [expression|variable] in procedure  


Inspects the value at every source line within a given procedure. Stops if the value has changed.

If the expression is of type pointer, look at the data pointed to and watch until it changes.

If the expression is not of type pointer, look at the 32 bits at that address (assume the expression evaluates to an address).

Using Fast Data Breakpoints

You can use fast watchpoints, also known as data breakpoints, with the stop command. A fast watchpoint watches a specified variable or memory address without severely impacting the performance of the program being debugged.

InIRIX 4 and earlier versions of dbx, the debugger had to single-step the process being debugged and check if the value of a variable had changed after each instruction. With fast watchpoints, the debugger uses a hardware virtual memory write protect mechanism to allow the program to run freely until the variable being watched changes. The program being debugged stops only when the virtual memory page containing the variable is written to. If the value of the variable being watched does not changed, dbx continues the execution of the process. If a write modifies a watched variable, dbx notifies you of the change.

Consider a small program that contains a global variable called global:

stop global

This command causes the program to stop if the value of the variable global changes. The program runs virtually at full speed until global gets assigned a new value. Similarly, consider the command:

stop 0x100100

This command stops when the 32- bit integer residing at address 0x100100 is modified, and runs at nearly full speed until the value changes. This form of the stop command is useful for watching the contents of anonymous memory, such as the memory returned by malloc().

dbx still needs to use the single-step approach if the stop command contains an expression to watch, such as in stop if global == 1. The performance of the debugged program can be greatly enhanced by including a variable to watch in the stop command.

For example, the previous stop command can be expressed equivalently as stop global if global == 1. This instructs the debugger to check only the expression global == 1 if the value of global changes. For situations where the expression does not depend upon a particular variable getting modified such as stop if global == x * 3, the single-step approach is the only way to achieve the desired behavior.

Stopping If a Test Expression Is True

By including a test clause in your stop command, you can cause dbx to stop if the value of an expression is true. You can use any valid numerical expression as a test. If the result of the expression is nonzero, the expression is true and the test is successful.

The point at which dbx evaluates the test expression depends on the command that you use to set the breakpoint:

stop if expression 


Evaluates the expression before executing each source line. Note that execution is very slow if you choose this type of conditional breakpoint.

stop at line if expression 


Evaluates the expression at the given line.

stop in procedure if expression 


Evaluates the expression at every source line within a given procedure.

Conditional Breakpoints Combining Variable and Test Clauses

You can create conditional breakpoints that combine both variable and test clauses. In these cases, the overall test evaluates to true only if both clauses are true.

The following forms of the stop command combine both the variable and test clauses:

stop [expression1|variable] if expression2  


Tests both conditions before executing each source line. Stops if both conditions are true.

If expression1 is of type pointer, look at the data pointed to and watch until it changes. If expression1 is not of type pointer, look at the 32 bits at that address (assume the expression evaluates to an address).

stop [expression1|variable] at line if expression2  


Tests both conditions at the given source line. Stops if both conditions are true.

If expression1 is of type pointer, look at the data pointed to and watch until it changes. If expression1 is not of type pointer, look at the 32 bits at that address (assume the expression evaluates to an address).

stop [expression1|variable] in procedure if expression2  


Tests both conditions at every source line within a given procedure. Stops if both conditions are true.

If expression1 is of type pointer, look at the data pointed to and watch until it changes. If expression1 is not of type pointer, look at the 32 bits at that address (assume the expression evaluates to an address).

Continuing Execution After a Breakpoint

The cont command allows you to continue execution after any type of breakpoint. In its simplest form, program execution continues until the end of the program or until another breakpoint is reached. You can also tell dbx to continue your program until it reaches a given line or procedure; this is similar to setting a temporary, "one-shot" breakpoint and then continuing.

The syntax of the cont command is:

cont 

Continues execution with the current line.

cont {at | to} line 


Sets a temporary breakpoint at the specified source line, then resumes execution with the current line. When your program reaches the breakpoint at line, dbx stops your program and deletes the temporary breakpoint. The keywords at and to are equivalent.

cont in procedure 


Sets a temporary breakpoint to stop execution upon entering the specified procedure, then resumes execution with the current line. When your program reaches the breakpoint in procedure, dbx stops your program and deletes the temporary breakpoint.

If your program stopped because dbx caught a signal intended for your program, then dbx will send that signal to your program when you continue execution. You can also explicitly send a signal to your program when you continue execution. Sending signals to your program upon continuation is discussed in "Continuing After Catching a Signal".

When you debug multiprocess programs, the resume command can be more helpful than the cont command. Refer to "Resuming a Suspended Process" for more information about the resume command.

Tracing Program Execution

The trace command allows you to observe the progress of your program as it executes. With it, you can print:

  • values of variables at specific points in your program or whenever variables change value

  • parameters passed to and values returned from functions

Each trace is assigned a number when you create it. Use this number to reference the trace in the various commands provided for manipulating traces (for example, disable, enable, and delete, all described in "Managing Breakpoints, Traces, and Conditional Commands").

The syntax of the trace command is:

trace variable 

Whenever the specified variable changes, dbx prints the old and new values of that variable.

trace procedure 


Prints the values of the parameters passed to the specified procedure whenever your program calls it. Upon return, dbx prints the return value.

trace [expression|variable] at line 


Whenever your program reaches the specified line, dbx prints the value of the variable if its value has changed.

If the expression is of type pointer, look at the data pointed to and watch until it changes. If the expression is not of type pointer, look at the 32 bits at that address (assume the expression evaluates to an address).

trace [expression|variable] in procedure  


Whenever the variable changes within the procedure, dbx prints the old and new values of that variable.

If the expression is of type pointer, look at the data pointed to and watch until it changes. If the expression is not of type pointer, look at the 32 bits at that address (assume the expression evaluates to an address).

trace [expression1|variable] at line if expression2  


Prints the value of the variable (if changed) whenever your program reaches the specified line and the given expression is true.

If expression1 is of type pointer, look at the data pointed to and watch until it changes. If expression1 is not of type pointer, look at the 32 bits at that address (assume the expression evaluates to an address).

trace [expression1|variable] in procedure if expression2  


Whenever the variable changes within the procedure that you specify, dbx prints the old and new values of that variable, if the given expression is true.

If expression1 is of type pointer, look at the data pointed to and watch until it changes. If expression1 is not of type pointer, look at the 32 bits at that address (assume the expression evaluates to an address).

To examine the parameters passed to and values returned from a function, you can trace that function. For example, if the function name is foo, set the trace by entering:

(dbx) trace foo

When you execute your program, dbx prints the values of the parameters passed to foo whenever your program calls it. Upon return from foo, dbx prints the return value:

(dbx) run
[3] calling foo(text = 0x10000484 = "Processing...\n", i = 4) from function main
[4] foo returning -1 from foo

In the example shown above, foo receives two parameters: a character string variable named text containing the value "Processing...\n" and an integer variable named i containing the value 4. The trace also indicates that foo returns a value of -1.

You can also examine a variable as it changes values. For example, you can monitor the value of a string variable named curarg as you use it to process an argument list. To set the trace, enter:

(dbx) trace curarg
Process 2395: [6] trace .test.main.curarg in main

When you set a trace on a variable, examine the confirmation that dbx prints. If you use the same variable name in multiple functions in your program, dbx may not set the trace on the variable that you want. If dbx sets the trace on an incorrect variable, delete the trace and set a new trace using a qualified variable format as described in "Qualifying Names of Program Elements". For more information on deleting traces, see "Deleting Breakpoints, Traces, and Conditional Commands".

So, in this example, if you use the variable curarg in both main and a function called arg_process, and you want to trace the curarg in arg_process, first delete this trace and then set a new trace:

(dbx) delete 6
(dbx) trace arg_process.curarg
Process 2395: [7] trace .test.arg_process.curarg in arg_process

When you execute your program, whenever curarg changes, dbx prints its old and new values:

(dbx) run
[7] curarg changed before [arg_process: line 53]:
                new value = (nil);
[7] curarg changed before [arg_process: line 86]:
                old value = 0;
                new value = 0x7fffc7e5 = "-i";
[7] curarg changed before [arg_process: line 86]:
                old value = 2147469285;
                new value = 0x7fffc7eb = "names.out";
[7] curarg changed before [arg_process: line 86]:
                old value = 2147469291;
                new value = 0x7fffc7f5 = "names.in";

Writing Conditional Commands

A conditional command created with the when command is similar to a breakpoint set with the stop command, except that rather than stopping when certain conditions are met, dbx executes a list of commands. The command list can consist of any dbx commands, separated by semicolons if you include more than one command in the command list. Additionally, you can use the keyword stop in the command list to stop execution, just like a breakpoint.

Each conditional command is assigned a number when you create it. You use this number to reference the conditional command in the various commands provided for manipulating conditional commands (for example, disable, enable, and delete, all described in "Managing Breakpoints, Traces, and Conditional Commands").

The syntax of the when command is:

when [expression|variable] {command-list}  


Inspects the value before executing each source line. If it has changed, executes the command list.

If the expression is of type pointer, look at the data pointed to and watch until it changes. If the expression is not of type pointer, look at the 32 bits at that address (assume the expression evaluates to an address).

when [expression|variable] at line {command-list}  


Inspects the value at the given source line. If it has changed, executes the command list.

If the expression is of type pointer, look at the data pointed to and watch until it changes. If the expression is not of type pointer, look at the 32 bits at that address (assume the expression evaluates to an address).

when [expression|variable] in procedure {command-list}  


Inspects the value at every source line within a given procedure. If it has changed, executes the command list.

If the expression is of type pointer, look at the data pointed to and watch until it changes. If the expression is not of type pointer, look at the 32 bits at that address (assume the expression evaluates to an address).

when if expression {command-list} 


Evaluates the expression before executing each source line. If it is true, executes the command list. Note that execution is slow if you choose this type of conditional command execution.

when at line if expression {command-list} 


Evaluates the expression at the given line. If it is true, executes the command list.

when in procedure if expression {command-list} 


Evaluates the expression at every source line within a given procedure. If it is true, executes the command list.

when [expression1|variable] if expression2 {command-list}  


Checks if the value of the variable has changed. If it has changed and the expression is true, executes the command list.

If expression1 is of type pointer, look at the data pointed to and watch until it changes. If expression1 is not of type pointer, look at the 32 bits at that address (assume the expression evaluates to an address).

when [expression1|variable] at line if expression2 {command-list}  


Checks if the value of the variable has changed each time the line is executed. If the value has changed and the expression is true, executes the command list.

If expression1 is of type pointer, look at the data pointed to and watch until it changes. If expression1 is not of type pointer, look at the 32 bits at that address (assume the expression evaluates to an address).

when [expression1|variable] in procedure if expression2 {command-list}  


Checks if the value of variable has changed at each source line of the given procedure. If the value has changed and the expression is true, executes the command list.

If expression1 is of type pointer, look at the data pointed to and watch until it changes. If expression1 is not of type pointer, look at the 32 bits at that address (assume the expression evaluates to an address).

Managing Breakpoints, Traces, and Conditional Commands

dbx provides commands that allow you to disable, enable, delete, and examine the status of the breakpoints, traces, and conditional commands that you set in your programs.

Each breakpoint, trace, and conditional command is assigned a number when you create it. Use these numbers as identifiers in the various commands provided for manipulating these debugging controls.

Listing Breakpoints, Traces, and Conditional Commands

The status command lists all of the breakpoints, traces, and conditional commands that you have set and indicates whether they are enabled or disabled.

For example, consider executing the following commands while debugging a program called test:

(dbx) stop in foo
Process     0: [3] stop in foo
(dbx) r
Process 22631 (test) started
[3] Process 22631 (test) stopped at [foo:38 ,0x10001050]
  38  r = foo2(i+1);
(dbx) trace total
Process 22631: [4] trace total in foo
(dbx) when at 60 {print i,j }
Process 22631: [5] when at "/usr/var/tmp/dbx_examples/test.c":60 { print i, j }

If you enter status, you see the following:

(dbx) status
Process 22631: [3] stop in foo
Process 22631: [4] trace total in foo
Process 22631: [5] when at "/usr/var/tmp/dbx_examples/test.c":60 { print i, j }

Disabling Breakpoints, Traces, and Conditional Commands

The disable command allows you to temporarily disable a breakpoint, trace, or conditional command so that it is inoperative and has no effect on program execution. dbx remembers all information about a disabled breakpoint, trace, or conditional command, and you may enable it using the enable command described in "Enabling Breakpoints, Traces, and Conditional Commands".

The syntax of the disable command is:

disable item [, item ... ] 


Disables the specified breakpoint(s), trace(s), or conditional command(s). This command has no effect if the item you specify is already disabled.

For example, to disable the conditional command set in "Listing Breakpoints, Traces, and Conditional Commands" enter:

(dbx) disable 4

If you enter status, you see the following:

(dbx) status
Process 22631: [3] stop in foo
Process 22631: [4] (disabled) trace total in foo
Process 22631: [5] when at "/usr/var/tmp/dbx_examples/test.c":60 { print i, j

Enabling Breakpoints, Traces, and Conditional Commands

The enable command reverses the effects of a disable command: The breakpoint, trace, or conditional command that you specify is enabled and once again affects the execution of your program. The syntax of the enable command is:

enable item [, item ...] 


Enables the specified breakpoint(s), trace(s), or conditional command(s).

For example, to enable the conditional command disabled in "Disabling Breakpoints, Traces, and Conditional Commands" enter:

(dbx) enable 4

Executing the status command shows that the condition command is now enabled:

(dbx) status
Process 22631: [3] stop in foo
Process 22631: [4] trace total in foo
Process 22631: [5] when at "/usr/var/tmp/dbx_examples/test.c":60 { print i, j

Deleting Breakpoints, Traces, and Conditional Commands

The delete command allows you to delete breakpoints, traces, and conditional commands:

delete {item [, item ...] | all} 


Deletes the item or items specified. If you use the keyword all instead of listing individual items, dbx deletes all breakpoints, traces, and conditional commands.

For example, to delete the breakpoint and trace set in "Listing Breakpoints, Traces, and Conditional Commands" enter:

(dbx) delete 3, 4

If you enter status, you see the following:

(dbx) status
Process 22631: [5] when at "/usr/var/tmp/dbx_examples/test.c":60 { print i, j }

To delete all breakpoints, traces, and conditional commands, enter:

(dbx) delete all

Using Signal Processing

dbx can detect any signals sent to your program while it is running and, at your option, stop the program.

Catching and Ignoring Signals

With the catch command, you can instruct dbx to stop your program when it receives any specified signal. The ignore command undoes the effects of a catch command.

The catch and ignore commands have the following syntax:

catch {signal | all} 


Instructs dbx to stop your program whenever it receives the specified signal. If you use the keyword all rather than giving a specific signal, dbx catches all signals.

ignore {signal | all} 


Instructs dbx to ignore the specified signal. All ignored signals are passed to your program normally. If you use the keyword all rather than giving a specific signal, dbx ignores all signals.

catch 

Prints a list of all signals caught.

ignore 

Prints a list of all signals ignored.

You can use the signal names and numbers as listed in the signal(2) reference page. You can also abbreviate the signal names by omitting the "SIG" portion of the name. You can use uppercase or lowercase for the signal names.


Note: Because "int" (in lowercase) is a dbx keyword, you cannot use it as an abbreviation for the SIGINT signal. You must use uppercase ("INT"), the full signal name ("SIGINT" or "sigint"), or the signal number ("2"). SIGINT is the only signal name with such a restriction.

If you instruct dbx to catch a signal, whenever that signal is directed to your program, dbx intercepts it and stops your program. Your program does not see this signal until you continue your program with the cont command. If your program has a handler for the signal, the signal is then passed to the program. If there is no handler for the signal, the program does not see the signal. You can suppress passing the signal to the program's signal handler by issuing a step or next command, rather than cont.

If you issue a SIGINT signal at the keyboard (usually by pressing <Ctrl-C>) while you are running an application under dbx, what happens depends on the circumstances:

  • If the process is in the same IRIX process group as dbx, the interrupt signal is sent to both dbx and the process. Both dbx and the process stop running. You are left at the dbx command line.

  • If the process was added with addproc, dbx –P, or dbx –p, it is not in the same IRIX process group as dbx. In this case, the signal interrupt is sent to dbx but not to the process. dbx stops running, but the process continues to run. Use the showproc command to see whether the process is still running. Then use the suspend command to stop the process.

Continuing After Catching a Signal

The cont command allows you to continue execution after catching a signal. You can also use the cont command to specify a different signal to send to your program than the one that dbx caught. Using the same syntax, you can also send a signal to your program when you continue, even if your program did not stop because of a caught signal.

Use the following forms of the cont command when handling signals. In each case, if you do not provide a signal, but your program stopped because dbx caught a signal intended for your program, then dbx sends that signal to your program when you continue execution:

cont [signal] 

Continues execution with the current line and sends the specified signal to your program.

cont [signal] {at | to} line 


Sets a temporary breakpoint at the specified source line, then resumes execution with the current line and sends the specified signal to your program.

cont [signal] in procedure 


Sets a temporary breakpoint to stop execution upon entering the specified procedure, then resumes execution with the current line and sends the specified signal to your program.

For example, if your program stopped because dbx caught a SIGINT signal, dbx will automatically send that signal to your program, if you enter:

(dbx) cont

Suppose you have a procedure called alarm_handler to handle an alarm signal sent to your program. If you want to test this procedure by single-stepping through it, you can execute the following command:

(dbx) cont SIGALRM in alarm_handler

This sets a temporary breakpoint to stop your program upon entering alarm_handler, continues execution of your program, and sends a SIGALRM signal to your program. Your program then enters the alarm_handler procedure and stops. You can then single-step through the procedure and observe execution.

Stopping on C++ Exceptions

The intercept command stops program execution on C++ exceptions. You can append a conditional expression to an intercept command by using the if clause. However, the context of an intercept break is not that of the throw; the context is the exception handling code of the C++ runtime library. Hence, only global variables have unambiguous interpretation in the if clause. To refer to a variable whose scope is that of the throw, use the fully qualified name for the variable.

The syntax of the intercept command is:

intercept {all | item} 


Stops on all C++ exceptions, or exceptions that throw the base type item.

intercept unexpected {[all] | [item [, item] ]} 


Stops on all C++ exceptions that have either no handler or are caught by an "unexpected" handler. You may omit all. If you specify item, stops on exceptions that throw the base type item.

intercept ... if expression 


You can append the if clause to all intercept commands. Your program stops only if expression is non-zero. Note that the context for evaluation of expression is the C++ runtime library, not that of the throw, so use global variables or fully qualified names in expression.

bx is an alias for intercept and unx is an alias for unexpected.

The following program example illustrates the if clause with the intercept command:

int global = 1;

main (){
 int local = 2;
 try {
  throw -1;
 }
 catch (int key) {
  printf ("exception: %d.\n", key);
 }
}

To set a break with a condition on the global variable, enter:

(dbx) intercept int if global != 0

Use a fully qualified name to set a break with a condition on the local variable:

(dbx) intercept int if main.local != 0

Do not include complex expressions involving operators such as * and & in your type specification for an intercept command. Note, however, that if you use the intercept command with a specific base type, you will also stop your program on throws of pointer, reference, const and volatile types. For example:

(dbx) bx char

Your program will stop on throws of type char, char *, char&, const char&, volatile char*, and so forth.

Like all other break points, pgrp or a pid clause can be appended to an intercept command. For example:

(dbx)intercept int pid 12345
(dbx)intercept char pgrp

Stopping at System Calls

Because system calls are part of the operating system and their source is generally not available for debugging purposes, you cannot set breakpoints in system calls using the same method that you use for your program's procedures. Instead, dbx provides the syscall command to allow you to stop your program when it executes system calls. With the syscall command you can catch (breakpoint) system calls either at the entry to the system call or at the return from the system call.

The syntax of the syscall command is:

syscall catch {call | return} {system_call | all} 


Sets a breakpoint to stop execution upon entering (call) or returning from (return) the specified system call. Note that you can set dbx to catch both the call and the return of a system call.

If you use the keyword all rather than giving a specific system call, dbx catches all system calls.

syscall ignore {call | return} {system_call | all} 


Clears the breakpoint to stop execution upon entering (call) or returning from (return) the specified system call.

If you use the keyword all rather than giving a specific system call, dbx clears the breakpoints to stop execution upon entering (call) or returning from (return) all system calls.

syscall catch [{call | return}] 


Prints a list of all system calls caught upon entry (call) or return (return). If you provide neither the call nor return keyword, dbx lists all system calls that are caught.

syscall ignore [{call | return}] 


Prints a list of all system calls not caught upon entry (call) or return (return). If you provide neither the call nor return keyword, dbx lists all system calls that are ignored.

syscall 

Prints a summary of the catch and ignore status of all system calls. The summary is divided into four sections: 1) caught at call, 2) caught at return, 3) ignored at call, and 4) ignored at return.

The fork and sproc system calls are treated specially as they invoke new processes. The returns from these system calls are controlled by the dbx variables $promptonfork and $mp_program, not by syscall. This is discussed in "Handling fork System Calls" and "Handling sproc System Calls and Process Group Debugging".The execv and execve system calls are treated specially as they change a process into a new program. For more information, see "Handling exec System Calls". The system calls are listed in /usr/include/sys.s. dbx ignores the case of the system call names in all syscall commands; therefore, you can use uppercase or lowercase in these commands.

A particularly useful setting is:

(dbx) syscall catch call exit

This stops your program upon entry to exit. With your program stopped, you can do a stack trace before the termination to see why exit was called.

Stepping Through Your Program

Stepping is a process of executing your program for a fixed number of lines and then automatically returning control to dbx. dbx provides two commands for stepping through lines of code: step and next.

For both step and next, dbx counts only those source lines that actually contain code; for the purposes of stepping, dbx ignores blank lines and lines consisting solely of comments.

The next and step commands differ in their treatment of procedure calls. When step encounters a procedure call, it usually "steps into" the procedure and continues stepping through the procedure (counting each line of source). On the other hand, when next encounters a procedure call, it "steps over" the procedure—executing it without stopping but not counting lines in the procedure—and continues stepping through the current procedure.

The following code fragment illustrates the difference between step and next:

 55  foo( arg1, arg2 )
 56  int arg1, arg2;
 57  {
 58        if ( arg1 < arg2 ) {
 ...       ...
 78        return( 0 );
 79  }
 ...
211  x = foo( i, j );
212  y = 2 * x;

In this example, if at line 211 you execute a step command to advance one line, dbx allows the process to proceed to line 58 (the first code line of the foo procedure). However, if you execute a next command, dbx executes line 211—calling foo—and advances the process to line 212.

Stepping Using the step Command

The format of the step command is:

step [integer] 

Executes the specified number of lines of source code, stepping into procedures. If you do not provide an argument, step executes one line. If step encounters any breakpoints, it immediately stops execution.

By default, step steps into only those procedures that are compiled with the debugging options -g, -g2, or -g3 for which line numbers are available in the symbol table. Note that this does not include standard library routines because they are not compiled using debugging options.

You can modify this behavior, even force dbx to step into procedures not compiled with full debugging information, by changing the value of the dbx variable $stepintoall.

Table 6-1 summarizes how the value of $stepintoall affects the step command.

Table 6-1. Effect of $stepintoall Variable on the step Command

$stepintoall value

Effect on step Command

0 (default)

Steps into all procedures that are compiled with debugging options -g, -g2, or -g3 for which line numbers are available in the symbol table.

1

In addition to the above procedures, steps into any procedures for which a source file can be found. Note that when you debug a source file compiled without symbols or compiled with optimization, the line numbers may jump erratically.

2

Steps into all procedures. Note that if dbx cannot locate a source file, then it cannot display source lines as you step through a procedure.

If your program has DSOs, set the environment variable LD_BIND_NOW to 1 before you run your program. This will force complete run-time linking. Otherwise, you can accidentally step into the run-time-linker, rld(1), which becomes part of your program at run time. Useful stack traces are then impossible. To avoid this situation, enter the following before the run command:

(dbx) setenv LD_BIND_NOW 1 

Stepping Using the next Command

The format of the next command is:

next [integer] 

Executes the specified number of lines of source code, stepping over procedures. If you do not provide an argument, next executes one line. If next encounters any breakpoints, even in procedures that it steps over, it immediately stops execution.

Using the return Command

If you step into a procedure and then decide you don't want to step through the rest of it, use return to finish executing the procedure and return to the calling procedure.

The format of the return command is:

return 

Continues execution until control returns to the procedure that invoked the return command.

return proc 

Continues execution until control returns to the named procedure. Execution continues, unless stopped by a breakpoint, until the latest invocation of the procedure named by proc at the time the command was issued is reached. Execution doesn't stop at subsequent invocations of the same procedure. The search for the frame to return to starts with the previous frame, because the current frame is skipped in looking for a frame whose name matches proc. If execution is stopped for any reason, this command is cancelled.

Starting at a Specified Line

When you continue your program, you typically do so at the place where it stopped using the cont command. However, you can also force your program to continue at a different address by using the goto command:

goto line 

Begins execution at the specified line. You may not use the goto command to resume execution with a line outside of the current procedure.

Referring to C++ Functions

As discussed in the section "Accessing C++ Member Variables" in Chapter 5, debugging a program written in C++ has some unique features. This section discusses setting breakpoints in C++ functions.

For the purpose of dbx debugging, functions in C++ programs fall into three general categories:

Member functions 


Refers to member functions using the syntax classname::functionname. For example, refers to the member function foo in the class Window as Window::foo.

Global C++ functions 


Refers to global functions using the syntax ::functionname. For example, refers to the global function foo as ::foo.

Non-C++ functions 


Refers to non-C++ functions using the syntax functionname. For example, refers to the function printf as printf.

When using dbx with C++, you cannot distinguish between overloaded functions. For example, consider two functions:

print(int);
print(float);

The following command sets a breakpoint in both functions:

(dbx) stop in ::print

The following example illustrates various possibilities:

#include <stdio.h> 
class foo {
   int n; 
   public:
   foo() {n = 0;}
   foo(int x);
   int bar();
   int bar(int); 
};

int foo:: bar() 
{
   return n; 
}

int foo:: bar(int x) 
{
   return n + x; 
}

foo::foo(int x) 
{
   n = x; 
}

int square(int x) 
{
   return x*x; 
}

main() 
{ 
   foo a; 
   foo b = 11; 
   int x = a.bar(); 
   int y = b.bar(x) + square(x);  
   printf("y = %d\n", y); 
} 

If you enter:

(dbx) stop in foo::foo

dbx stops execution in the constructor for the variable b; dbx also stops in the constructor for the variable a.

If you enter:

(dbx) stop in foo::bar

dbx stops execution both when a.bar is called and when b.bar is called, because dbx is unable to distinguish between the overloaded functions.

To stop in square, enter:

(dbx) stop in ::square

To stop in printf (a C function), enter:

(dbx) stop in printf

To set breakpoints in a specific function from a C++ template, the name of the function must be in back quotation marks to force dbx to interpret the entire character string as the name of the function. Otherwise the < and > characters in the template name are interpreted by dbx as operators.

dbx sets breakpoints in all instantiations of the template if you do not use back quotation marks and simply leave out the template's type-argument list, that is leave out the two characters < and > and the characters included between them.

The following code illustrates these points:

template <class T>  myclass {
myclass() { /*... */ }
~myclass() { /*... */ }
myfunc(T) { /* ... */ }};

To set a breakpoint only in the <int> template function for myfunc enter:

(dbx) stop in `myclass<int>::myfunc` 

To set breakpoints in all functions myfunc for all instantiations of the template class enter:

(dbx) stop in  myclass::myfunc