Go to main content
Oracle® Developer Studio 12.6: dbxtool Tutorial

Exit Print View

Updated: June 2017
 
 

Using Advanced Breakpoint Techniques

    This section demonstrates some advanced techniques for using breakpoints:

  • Using breakpoint counts

  • Using bounded breakpoints

  • Picking a useful breakpoint count

  • Watchpoints

  • Using breakpoint conditions

  • Micro replay using pop

  • Using fix and continue

This section, and the example program, are inspired by an actual bug discovered in dbx using much the same sequence described in this section.


Note -  To get the correct output as shown in this section, the example program must still be "buggy". If you fixed the bug, re-download the OracleDeveloperStudio12.5-Samples directory from Example Program.

The source code includes a sample input file named in, which triggers a bug in the example program. in contains the following code:

display nonexistent_var	# should yield an error
display var
stop in X	# will cause one "stopped" message and display
stop in Y	# will cause second "stopped" message and display
run
cont
cont
run
cont
cont

When you run the program with the input file, the output is as follows:

$ a.out < in
> display nonexistent_var 
error: Don't know about 'nonexistent_var'
> display var 
will display 'var'
> stop in X 
> stop in Y 
> run 
running ...
stopped in X
var = {
      a = '100'
      b = '101
      c = '<error>'
      d = '102
      e = '103'
      f = '104'
      }
> cont 
stopped in Y
var = {
      a = '105'
      b = '106'
      c = '<error>'
      d = '107'
      e = '108'
      f = '109'
      }
> cont 
exited
> run 
running ...
stopped in X
var = {
      a = '110'
      b = '111'

      c = '<error>'
      d = '112'
      e = '113'
      f = '114'
      }
> cont 
stopped in Y
var = {
      a = '115'
      b = '116'

      c = '<error>'
      d = '117'
      e = '118'
      f = '119'
      }
> cont 
exited
> quit
Goodby

This output might seem voluminous but the point of this example is to illustrate techniques to be used with long running, complex programs where stepping through code or tracing just are not practical.

Notice that when showing the value of field c, you get a value of <error>. Such a situation might occur if the field contains a bad address.

The Problem

Notice that when you ran the program a second time, you received additional error messages that you did not get on the first run:

error: cannot get value of 'var.c'

The error() function uses a variable, err_silent, to silence error messages in certain circumstances. For example, in the case of the display command, instead of displaying an error message, problems are displayed as c = '<error>'.

Step 1: Repeatability

The first step is to set up a debug target and configure the target so the bug can easily be repeated by clicking Restart image:Restart button .

    Start debugging the program as follows:

  1. If you have not yet compiled the example program, do so by following the instructions in Example Program.

  2. Choose Debug → Debug Executable.

  3. In the Debug Executable dialog box, browse for or type the path to the executable.

  4. In the Arguments field, type:

    < in

    The directory portion of the executable path is displayed in the Working Directory field.

  5. Click Debug.

    image:Debug Executable dialog box

In a real world situation, you might want to populate the Environment field as well.

When debugging a program, dbxtool creates a debug target. You can use the same debugging configuration by choosing Debug → Debug Recent and then choosing the desired executable.

You can set many of these properties from the dbx command line. They will be stored in the debug target configuration.

The following techniques help sustain easy repeatability. As you add breakpoints, you can quickly go to a location of interest by clicking Restart without having to click Continue on various intermediate breakpoints.

Step 2: First Breakpoint

Put the first breakpoint inside the error() function in the case where it prints an error message. This breakpoint will be a line breakpoint on line 33.

In a larger program, you can easily change the current function in the Editor window by typing the following, for example, in the Debugger Console window:

(dbx) func error

The lavender stripe indicates the match found by the func command.

  1. Create the line breakpoint by clicking in the left margin of the Editor window on top of the number 33.

    image:Editor window with lavender stripe on line 31 and                                                 breakpoint on line 33
  2. Click Restart image:Restart button to run the program and upon hitting the breakpoint, the stack trace shows the error message that is generated due to the simulated command in the in file:

    > display var	# should yield an error

    The call to error() is expected behavior.

    image:Call Stack window with error message                                                 frame
  3. Click Continue image:Continue button to continue the process and hit the breakpoint again.

    An unexpected error message appears.

    image:Call Stack window with error message                                                 frame

Step 3: Breakpoint Counts

It would be better to arrive at this location repeatedly on each run without having to click Continue after the first hit of the breakpoint due to the command:

> display var # should yield an error

You can edit the program or input script and eliminate the first troublesome display command. However, the specific input sequence you are working with might be a key to reproducing this bug so you do not want to alter the input.

    Because you are interested in the second time you reach this breakpoint, set its count to 2.

  1. In the Breakpoints window, right-click the breakpoint and choose Customize.

  2. In the Customize Breakpoint dialog box, type 2 in the Count Limit field.

  3. Click OK.

    image:Customize breakpoint dialog box

Now you can repeatedly arrive at the location of interest.

In this case,choosing a count of 2 was trivial. However, sometimes a place of interest is called many times. See Step 7: Determining the Count Valueto easily choose a good count value. But for now, you will explore another way of stopping in error() only in the invocation you are interested in.

Step 4: Bounded Breakpoints

  1. Open the Customize Breakpoint dialog box for the breakpoint inside error() and disable breakpoint counts by selecting Always Stop from the drop-down list for the Count Limit.

  2. Rerun the program.

    Pay attention to the stack trace the two times you stop in error(). The first time, the stop in error() looks like the following screen:

image:Call Stack window

The second time, the stop in error() looks like the following screen:

image:Call Stack window

To arrange to stop at this breakpoint when it is called from runProgram (frame [7]), open the Customize Breakpoint dialog box again and set the While In field to runProgram.

image:Customize Breakpoint window

Step 5: Looking for a Cause

The unwanted error message is issued because err_silent is not > 0. Take a look at the value of err_silent with balloon evaluation.

  1. Put your cursor over err_silent in line 31 and wait for its value to be displayed.

    image:Editor window with balloon evaluation showing                                                 err_silent = 0

    Follow the stack to see where err_silent was set.

  2. Click Make Caller Current image:Make Caller Current button twice to evaluateField(), which has already called evaluateFieldPrepare() simulating a complex function that might be manipulating err_silent.

    image:Editor window with lavender stripe in                                                 evaluateField
  3. Click Make Caller Current again to get to printField(), where err_silent is being incremented. printField() has also already called printFieldPrepare(), also simulating a complex function that might be manipulating err_silent.

    image:Editor window with lavender strip in                                                 printField

    Notice how err_silent++ and err_silent-- bracket some code.

    err_silent could go wrong in either printFieldPrepare() or evaluateFieldPrepare(), or it might already be wrong when control gets to printField().

Step 6: More Breakpoint Counts

    To find out whether err_silent was wrong before or after the call to printField(), put a breakpoint in printField().

  1. Select printField(), right-click, and choose New Breakpoint.

    The New breakpoint type is pre-selected and the Function field is pre-populated with printfield.

  2. Click OK.

    image:New Breakpoint dialog box
  3. Click Restart image:Restart button .

    The first time you hit the breakpoint is during the first run, on the first stop, and on the first field, var.a. err_silent is 0, which is OK.

    image:Editor window with balloon evaluation
  4. Click Continue.

    err_silent is still OK.

  5. Click Continue again.

    err_silent is still OK.

Reaching the particular call to printField() that resulted in the unwanted error message might take a while. You need to use a breakpoint count on the printField breakpoint. But what shall the count be set to? In this simple example, you could attempt to count the runs and the stops and the fields being displayed, but in practice the process might be more difficult. There is a way to determine the count semi-automatically.

Step 7: Determining the Count Value

  1. Open the Customize Breakpoint dialog box for the breakpoint on printField() and set the Count Limit field to infinity.

    image:Customize Breakpoint dialog box

    This setting means that you will never stop at this breakpoint. However, it will still be counting.

  2. Set the Breakpoints window to show more properties, such as counts.

    1. Click the Change Visible Columns button image:Change Visible Columns button at the top right corner of the Breakpoints window.

    2. Select Count Limit, Count, and While In.

    3. Click OK.

      image:Change Visible Columns dialog                                                   box
  3. Run the program again. You will hit the breakpoint inside error(); the one bounded by runProgram().

  4. Look at the count for the breakpoint on printField().

    image:Breakpoints window

    The count is 15.

  5. In the Customize Breakpoint window again, click the drop-down list in the Count Limit column and select Use current Count value to transfer the current count to the count limit, and click OK.

Now when you run the program, you will stop in printField() the last time it is called before the unwanted error message.

Step 8: Narrowing Down the Cause

Use balloon evaluation to inspect err_silent again. Now it is -1. The most likely cause is one err_silent-- too many, or one err_silent++ too few, being executed before you got to printField().

You can locate this mismatched pair of err_silents in a small program like this example by careful code inspection. However, a large program might contain numerous pairings of the following:

err_silent++;
err_silent--;

A quicker way to locate the mismatched pair is by using watchpoints.

The cause of the error might not be a mismatched set of err_silent++; and err_silent--; at all, but a rogue pointer overwriting the contents of err_silent. Watchpoints would be more effective in catching such a problem.

Step 9: Using Watchpoints

    To create a watchpoint on err_silent:

  1. Select the err_silent variable, right-click, and choose New Breakpoint.

  2. Set Breakpoint Type to Access.

    Note how the Settings section changes and how the Address field is & err_silent.

  3. Select After in the When field.

  4. Select Write in the Operation field.

  5. Click OK.

    image:New Breakpoint window
  6. Run the program.

    You stop in init(). err_silent was incremented to 1 and execution stopped after that.

  7. Click Continue.

    You stop in init() again.

  8. Click Continue again.

    You stop in init() again.

  9. Click Continue again.

    You stop in init() again.

  10. Click Continue again.

    Now you stop in stopIn(). Things look OK here too, with no -1s.

Instead of clicking Continue over and over until err_silent is set to -1, you can set a breakpoint condition.

Step 10: Breakpoint Conditions

    To add a condition to your watchpoint:

  1. In the Breakpoints window, right-click the After Write breakpoint and choose Customize.

  2. Verify that After is selected in the When field.

    Selecting After enables you to see what the value of err_silent was changed to.

  3. Set the Condition field to err_silent == -1.

  4. Click OK.

    image:Customize Breakpoint dialog box
  5. Run the program again.

    You stop in checkThings(), which is the first time err_silent is set to -1. As you look for the matching err_silent++ you see what looks like a bug: err_silent is incremented only in the else portion of the function.

    image:Editor window showing program stopped in                                                 checkThings

    Could this be the bug you've been looking for?

Step 11: Verifying the Diagnosis by Popping the Stack

One way to double-check that you indeed went through the else block of the function would be to set a breakpoint on checkThings() and run the program. But checkThings() might be called many times. You can use breakpoint counts or bounded breakpoints to get to the right invocation of checkThings(), but a quicker way to replay what was recently executed is to pop the stack.

  1. Choose Debug → Stack → Pop Topmost Call.

    Notice the Pop Topmost Call does not undo everything. In particular, the value of err_silent is already wrong because you are switching from data debugging to control flow debugging.

    The process state reverts to the beginning of the line containing the call to checkThings().

  2. Click Step Into image:Step Into button . and observe as checkThings() is called again.

    As you step through checkThings(), you can verify that the process executes the if block where err_silent is not incremented and then is decremented to -1.

image:Editor window showing program stopped in if section of                                     checkThings( )

Although you appear to have found the programming error, you might want to triple check it.

Step 12: Using Fix to Further Verify The Diagnosis

Fix the code in place and verify that the bug has indeed gone away.

  1. Fix the code by putting the err_silent++ above the if statement.

    image:Editor window showing err_silent++                                                 moved
  2. Choose Debug > Apply Code Changes or press the Apply Code Changes button image:Apply Code Changes button .

  3. Disable the printField breakpoint and the watchpoint but leave the breakpoint in error() enabled.

    image:Breakpoints window
  4. Run the program again.

Note that the program completes without hitting the breakpoint in error() and its output is as expected.

image:Output window

Discussion

This example illustrates the same pattern as discussed at the end of Using Breakpoints and Stepping, that is, you stop the misbehaving program at some point before things have gone wrong and then steps through the code comparing the intent of the code with the way the code actually behaves. The main difference is that finding the point before things have gone wrong is a bit more involved.