Chapter 5. Examining and Changing Data

This chapter describes how to examine and change data in your program while running it under dbx. Topics in this chapter include:

Using Expressions

Many dbx commands accept one or more expressions as arguments. Expressions can consist of constants, dbx variables, program variables, and operators. This section discusses operators and constants. “Creating and Removing dbx Variables” in Chapter 4, describes dbx variables, and “Displaying and Changing Program Variables”, describes program variables.


In general, dbx recognizes most expression operators from C, Fortran 77, and Pascal. dbx also provides some of its own operators. Operators follow the C language precedence. You can also use parentheses to explicitly determine the order of evaluation.

The following list describes the operators provided by dbx.

  • not: unary operator returning false if the operand is true.

  • or: binary logical operator returning true if either operand is nonzero.

  • xor: binary operator returning the exclusive-OR of its operands.

  • /: binary division operator (// also works for division).

  • div: binary operator that coerces its operands to integers before dividing.

  • mod: binary operator returning op1 modulo op2. This is equivalent to the C % operator

  • #exp: unary operator returning the address of source line specified by exp.

  • file#exp: unary operator returning the address of source line specified by exp in the file specified by file.

  • proc #exp: unary operator returning the address of source line specified by exp in the file containing the procedure proc.

The # operator takes the line number specified by the expression that follows it and returns the address of that source line. If you precede the # operator with a filename enclosed in quotation marks, the # operator returns the address of the line number in the file you specify. If you precede the # operator with the name of a procedure, dbx identifies the source file that contains the procedure and returns the address of the line number in that file.

A pound sign (#) introduces a comment in a dbx script file. When dbx sees a pound sign in a script file, it interprets all characters between the pound sign and the end of the current line as a comment. See “Executing dbx Scripts” in Chapter 4, for more information on dbx script files. To include the # operator in a dbx script, use two pound signs (for example, ##27).

Example 5-1. Using operators

To print the address of line 27 in the current source file, enter:

(dbx) print #27

To print the address of line 27 in the source file foo.c (assuming that foo.c contains source that was used to compile the current object file), enter:

(dbx) print "foo.c"   #27

To print the address of line 27 in the source file containing the procedure bar (assuming that bar is a function in the current object file), enter:

(dbx) print bar #27

The following list shows the C language operators recognized by dbx:

  • Unary: ! & + - * sizeof()

  • Binary: % << >> == <= >= != < > & && | || + - * / [ ] -> .

Note: C does not allow the use of the sizeof operator on bit fields. However, dbx allows you to enter expressions using the sizeof operator on bit fields; in these cases, dbx returns the number of bytes in the data type of bit fields (such as int or unsigned int). The C language exclusive-OR (^) operator is not supported. Use the dbx xor operator instead.

The following list describes the Pascal operators recognized by dbx:

  • Unary: not ^ + -

  • Binary: mod = <= >= <> < > and or + - * / div [ ]

The following list describes the FORTRAN 77 and Fortran 90 language operators recognized by dbx. Note that dbx does not recognize Fortran logical operators (such as .or. and .TRUE.).

  • Unary: + -

  • Binary: + - * / %

Fortran array subscripts may be in either square brackets, [ ], or the standard parenthesis, ( ), and the Fortran 90 member selection operator (%) is allowed.


You can use both numeric and string constants under dbx. Expressions cannot contain constants defined by #define declarations to the C preprocessor.

  • Numeric constants: in numeric expressions, you can use any valid integer or floating point constants. By default, dbx assumes that numeric constants are in decimal. You can set the default input base to octal by setting the dbx $octin variable to a nonzero value. You can set the default input base to hexadecimal by setting $hexin to a nonzero value. If you set both $octin and $hexin to nonzero values, $hexin takes precedence.

    You can override the default input type by prefixing 0x to indicate a hexadecimal constant, or 0t to indicate a decimal constant. For example, 0t23 is decimal 23 and 0x2A is hexadecimal 2A.

    By default, dbx prints the value of numeric expressions in decimal. You can set the default output base to octal by setting the $octints variable to a nonzero value. You can set the default output base to hexadecimal by setting the dbx $hexints variable to a nonzero value. If you set both $octints and $hexints to nonzero values, $hexints takes precedence.

  • String constants: most dbx expressions cannot include string constants. The print and printf commands are two of the dbx commands that accept string constants as arguments. You can also use the set command to assign a string value to a dbx variable.

    Otherwise, string constants are useful only as arguments to functions that you call interactively. See “Using Interactive Function Calls”, for information on interactive function calls.

    You can use either the double-quotation mark (“) or the single-forward quotation mark (') to quote strings in dbx. In general, dbx recognizes the following escape sequences in quoted strings (following the standard C language usage):

    \\ \n \r \f \b \t \' \" \a

    Enclosing a character string in back quotation marks ( `) indicates that the whole string is the name of a program element, not a character-string constant. This is useful, for example, when referring to C++ templates, which include in their names the greater-than (>) and less-than (<) characters. Without back quotation marks, dbx would attempt to interpret the characters as operators. For further discussion, see “Qualifying Names of Program Elements”, and “Referring to C++ Functions” in Chapter 6.

Printing Expressions

dbx provides the following commands for printing values of expressions:

  • print [exp1,[exp2, ...]]: prints the value(s) of the specified expression(s).

  • printd [exp1 ,[exp2], ...]: prints the value(s) of the specified expression(s) in decimal. pd is an alias for printd. See “Creating and Removing dbx Variables” in Chapter 4, for more information about dbx aliases.

  • printo [exp1 ,[exp2], ... ]: prints the value(s) of the specified expression(s) in octal. po is an alias for printo.

  • printx [exp1 ,[exp2], ...]: prints the value(s) of the specified expression(s) in hexadecimal. px is an alias for printx.

For displaying information about variables, the duel command is a flexible alternative to the print command; see “Using the High-Level Debugging Language duel”.

The variable types are as follows:

Table 5-1. Variable Types


Variable Name


Signed char



Unsigned char



Signed short



Unsigned short



Example 5-2. Printing expressions

Examples include:

(dbx) pd sc
(dbx) pd ssh
(dbx) px sc
(dbx) px ssh
(dbx) pd usc
(dbx) pd ush

dbx always prints the bits in the appropriate type. pd is an exception; it expands signed types with sign extension so the decimal value looks correct.

Another example:

(dbx) print sc, usc
'\377' '\377'

If the dbx $hexchars variable is set, this command displays 0xff 0xff. (This is a change from releases previous to IRIX 5.2. Previously, the px, po cases on signed char expanded to 32 bits, so px sc printed 0xffffffff.)

If the printed data type is pointer, dbx uses the format specified by the $addrfmt or $addrfmt64 predefined dbx variable ($addrfmt64 is used on only 64-bit processes).

The following is the syntax of the printf command:

printf string ,[exp1 ,[exp2] ...]

This command prints the value(s) of the specified expression(s) in the format specified by the string, string. The printf command supports all formats of the IRIX printf command except %s. For a list of formats, see the printf(3S) man page.

Value History for Print and Calls

Values printed by the print command as well as values returned by the ccall command can be saved so they can be displayed later or used in other expressions.

Use the following command to enable this feature:

% set $printhistory=0

The value variables are created with names starting with $, followed by a number and displayed after each print and ccall command. These values can be later referred to by using the generated name. The last value can also be referred to simply as $.

Example 5-3. Value history

(dbx) set $valuehistory=1
(dbx) print foof()
$1 = 9.9899997711181641
(dbx) print $1/4567.98987
$2 = 0.0021869575142289366
(dbx) print $
$3 = 0.0021869575142289366

These values are kept until a givenfile command is used. They are then discarded.

The set command can be used to print the complete list of value history, in addition to the dbx variables.

Using Data Types and Type Coercion (Casts)

You can use data types for type conversion (also known as casting) by including the name of the data type in parentheses before the expression you want to cast. For example, to convert a character into an integer, use (int) to cast the value as shown in the following example:

Example 5-4. Casting value

(dbx) print (int) 'b'

To convert an integer into a character, use (char) to cast the value as shown in the next example:

(dbx) print (char) 67

This is standard C language type casting.

Qualifying Names of Program Elements

You can use the same name for different program elements, such as variables, functions, statement labels, several times in a program. This is convenient and, during program execution, the potential ambiguity presents no problem. For example, you can use a temporary counter named i in many different functions. The scope of each variable is local; space is allocated for it when the function is called and freed when the function returns. However, in dbx you sometimes need to distinguish occurrences of identical names.

dbx allows unambiguous reference to all program elements by using source file and routine names as qualifying information that makes otherwise indistinguishable names unique. To find the fully qualified name of the active version of a name, use the which command. To find the fully qualified names of all versions of a name, use the whereis command. Note that if a variable, such as i, is used many times, whereis can generate many lines of output.

The fully qualified name of a program element allows you not only to refer within a procedure to variables of the same name with different scopes, but to refer unambiguously to program elements outside your current frame or activation stack.

dbx qualifies names with the file (also called module), the procedure, a block, or a structure. You can manually specify the full scope of a variable by separating scopes with periods. For example:


In this expression, i is the variable name, main is a procedure in which it appears, and mrx is the source file (omitting the file extension) in which the procedure is defined.

For languages without modules, such as C, C++, and Fortran, the base name of the source file, that is the file name up to the first dot in the name, is taken as a module name. For example, if b is a Fortran subroutine in t.f, then t.b names the routine.

To illustrate how names are qualified, consider a C program called test that contains a function compare. In this example, the variable i is declared in both the main procedure and the compare function:

int compare ( int );

main( argc, argv )

int argc;
char **argv;
   int i;

int compare ( arg1, arg2 )
   int i;

To trace the value of the i variable that appears in the function compare, enter:

(dbx) trace

To print the value of the i that appears in the procedure main, enter:

(dbx) print test.main.i

It is possible to have variable scopes in C and C++ that are in unnamed program blocks. dbx provides names for these scopes, starting with __$$blk1 and followed by __$$blk2, __$$blk3, etc., which it places in the fully qualified name of the variable as it would an explicit scope name. The whereis and which commands show the names assigned. dbx provides a special name __aout for your base executable. So for example, you can use __aout.main to refer to a global C function main in your executable. You can, of course, also refer to the function using the name of your executable; if your executable is named myaout, myaout.main also refers to the global C function main.

If you wish to refer to a variable that occurs in a DSO, dbx uses a naming convention similar to that for variables in your executable. If, for example, strcpy is a function from the file stuff.c in the library, then both libc.stuff.strcpy and libc.strcpy refer to the function strcpy.

In C, struct, union, and enum tags can conflict with other names. From the context, dbx usually interprets correctly a reference to one of these tags. However, if dbx does not, prefix the tag with the marker __$T_ to prevent confusion with variables or functions. For example; use __$T_foo if you wish to refer to:

struct foo { /* ... */ }	

In ANSI C, statement label names also can conflict with other names. The ambiguity is removed by applying a prefix of __$L_ to the label name. Thus, for example:

int myfoo { int x;   x:  goto x; ++x}	

If you enter the following, the output is the address of the variable x:

(dbx) print &x   

If you enter the following, the output is the address of label x. The -32 compiler provides no debugging information on C labels.:

(dbx) print &__$L_x 

To refer to Fortran statement labels you must either use the __$L_ prefix or use back quotation marks to force dbx to recognize a numerical label as a name. For example, if you have the following:

do 10  i = 1,10
10 continue

Both of the following commands lists the address of the label:

(dbx) print &`10`
(dbx) print &__$L_10

You may have multiple source files with the same name, for example myfile.c, that are in different directories. The module name myfile may refer to either source file. dbx resolves this ambiguity by prefixing the fully qualified file names with a unique, numeric label. The which and whereis commands show the label used. For example, suppose the main executable has two myfile.c source files, then __aout.myfile refers to either source file, __aout._$1_myfile refers to one of them, and __aout._$2_myfile refers to the other.

A leading dot (a period at the beginning of the identifier) tells dbx that the first qualifier is not a module (file).

The leading dot is useful when a file and a procedure have the same name. For instance, suppose mrx.c contains a function called mrx. Further, suppose that mrx.c contains a global variable called mi and a local variable, also called mi. To refer to the global variable, use the qualified form .mrx.mi, and to refer to the local variable, use the qualified form mrx.mrx.mi.

Displaying and Changing Program Variables

You can use the value of program variables in dbx expressions. You can also change the value of program variables while running your program under dbx control.

Variable Scope

You can access the value of a variable only while it is in scope. The variable is in scope only if the block or procedure with which it is associated is active.

After you start your program, whenever your program executes a block or procedure that contains variables, your program allocates space for those variables and they “come into scope.” You may access the values of those variables as long as the block or procedure is active. Once the block or procedure ends, the space for those variables is deallocated and you may no longer access their values.

Displaying the Value of a Variable

You can display the value of a program variable by using the printd, printf, printo, and printx commands and the pd, po, and px aliases described in “Printing Expressions”.

Example 5-5. Displaying Variable Values

To print the value of the program variable total, enter the following:

(dbx) print total

The print command also displays arrays, structures, and other complex data structures. For example, if message is a character array (a string), dbx prints the string:

(dbx) print message
"Press <Return> to continue."

As a more complex example, consider a simple linked list stored as an array of elements, each element consisting of a pointer to the next element and an integer value. If the array is named list, print the entire array by entering:

(dbx) print array

dbx prints the value of each element in the array:

    [0] struct list {
        next = (nil)
        value = 1034
    [1] struct list {
        next = 0x10012258
        value = 1031
    [2] struct list {
        next = 0x10012270
        value = 1028
    [3] struct list {
        next = 0x10012288
        value = 1025
    [4] struct list {
        next = 0x100122a0
        value = 1022
    [5] struct list {
        next = 0x100122b8
        value = 1019

To print an individual element, enter a command such as:

(dbx) print array[5]
struct list {
    next = 0x100122b8
    value = 1019

If your array consists of simple elements such as integers or floating point values, you can directly examine the contents of memory using the / (examine forward) command described in “Examining Memory and Disassembling Code” in Chapter 7.

Suppose a single-precision floating point array is named float_vals. To see the six consecutive elements beginning with the fifth element, enter:

(dbx) &float_vals[4] / 6f
10012018:  0.25000000000000000 0.20000000298023224 0.16666699945926666
10012028:  0.12500000000000000 0.11111100018024445

You can also print parts of arrays and complex structures with duel, a high-level debugging language. For more information, see “Using the High-Level Debugging Language duel”.

Changing the Value of a Variable

The assign command changes the value of existing program variables. You can also use the assign command to change the value of machine registers, as described in “Changing Register Values” in Chapter 7.

The following is the syntax of the assign command:

assign variable=expression

This command assigns the value of expression to the program variable.

Example 5-6. assign command

(dbx) assign x = 27
(dbx) assign y = 37.5

Example 5-7. Using casts to change variable values

If you receive an incompatible types error when you try to assign a value to a pointer, use casts to make the assignment work. For example, if next is a pointer to a structure of type element, you can assign next a null pointer by entering:

(dbx) assign *(int *) (&next) = 0
(dbx) assign next = 0
(dbx) assign next = (struct list*) 0;

In this example, nil denotes that the value of the pointer is 0; nil is similar to NULL in the C language.

Conflicts between Variable Names and Keywords

When naming variables in your program, avoid using any dbx keywords. If you have a variable with the same name as a dbx keyword and you attempt to use that variable in a dbx command, dbx reports a syntax error.

If you do have a program variable with the same name as a dbx command, you can force dbx to treat it as a variable by enclosing the variable in parentheses.

dbx keywords include:

all    not
and    or
at     pgrp
div    pid
if     sizeof
in     to
mod    xor

Example 5-8. Variable name and keyword conflicts

For example, if you try to print the value of a variable named in by entering the following command, dbx displays an error.

(dbx) print in
print in
     ^ syntax error
Suggestion: in is a dbx keyword; a revised command is in history.
Type !16 or !! to execute: print (in)

The correct way to display the value of in is to enter the following command:

(dbx) print (in)

Case Sensitivity in Variable Names

Whether dbx is case sensitive when it evaluates program variable names depends on the value of the dbx $casesense variable.

If $casesense is 2 (the default), then the language in which the variable was defined is taken into account (for example, C and C++ are case sensitive while Pascal and Fortran are not). If $casesense is 1, case is always checked. If $casesense is 0, case is always ignored. Note that file (module) names are always case sensitive since they represent UNIX file names.

Displaying and Changing Environment Variables Used by a Program

You can control the values of environment variables used by a program without affecting the shell. The dbx commands printenv, setenv, and unsetenv control the debugging environment much like their csh counterparts control the shell environment. However, they only affect your program while it is being debugged. dbx passes the values shown by the printenv command to the shell as the environment to use while your program is run by the run or rerun commands.

The following commands control your program's environment variables:

  • printenv: prints the list of environment variables affecting the program being debugged.

  • setenv: same as printenv.

  • setenv var: sets the environment variable var to an empty value.

  • setenv var value: sets the environment variable var to value, where value is not a dbx variable.

  • setenv var $var: sets the environment variable var to $var, where $var is a dbx variable.

  • setenv var “charstring”: sets the environment variable var to "charstring".

  • unsetenv var: removes the specified environment variable.

If you attempt to change the PAGER or EDITOR environment variables, the effect on dbx is undefined; the new values may, or may not, affect how dbx runs.

Using the High-Level Debugging Language duel

The duel language is a high-level debugging language that you can invoke from dbx. duel is an acronym for Debugging U (might) Even Like.

The duel language does not control processes; it provides the following commands for printing data such as parts of arrays and complex structures. The following is the syntax of this command:

duel [alias] [clear]

The duel command invokes the debugging language. alias shows all current duel aliases. clear deletes all duel aliases.

To invoke duel from within dbx, type:

(dbx) duel

Example 5-9. duel usage

To print the array elements x[1] to x[10] that are greater than 5, enter:

(dbx) duel x[1..10] >? 5
x[3] = 14
x[8] = 6

The output includes the values 14 and 6, as well as their symbolic representation x[3] and x[8].

Using duel Quick Start

The duel language is implemented by adding the duel command to dbx. All dbx commands work as before. The duel command, however, is interpreted by duel, and its concepts are not understood by other dbx commands.

Note: This version of duel does not allow interactive function calls.

duel is based on expressions that return multiple values. The x..y operator returns the integers from x to y; the x,y operator returns x and then y.

Example 5-10. duel and multiple values

(dbx) duel (1,9,12..15,22)

This command prints 1, 9, 12, 13, 14, 15, and 22.

You can use such expressions wherever a single value is used. For example:

(dbx) duel x[1,9,12..15,22]   

This command prints elements 1, 9, 12, 13, 14, 15, and 22 of the array x. duel incorporates C operators, and casts C statements as expressions.

The semicolon (;) prevents duel output. duel aliases are defined with x:=y and provide an alternative to variable declaration. You can also return x[i] instead of using printf:

(dbx) duel if(x[i:=0..99]<0) x[i]
x[i] = -4

Example 5-11. duel and symbolic output

The symbolic output x[i] can be fixed by surrounding i with {}. For example:

(dbx) duel if(x[i:=0..99]<0) x[{i}]
x[7] = -4

The braces ({}) are like parentheses (), but force the symbolic evaluation to use i's value, instead of i. You can usually avoid this altogether with direct duel operators:

(dbx) duel x[..100] <? 0
x[7] = -4

The ..n operator is a shorthand for 0..n-1. For example, ..100 is the same as 0..99. The operators x<?y, x==?y, x>=?y compare their left side operand to their right side operand as in C, but return the left side value if the comparison result is true. Otherwise, they look for the next values to compare, without returning anything.

duel's x.y and x->y allow an expression y, evaluated under x's scope:

(dbx) duel emp[..100].(if(code>400) (code,name))
emp[46].code = 682
emp[46].name = “Ela”

The if() expression is evaluated under the scope of each element of emp[], an array of structures. In C language terms, we have to write:

for(i = 0; i < 100; i++ ) {
  if(emp[1].code > 400) {
    printf(“%d %s\n”,emp[i].cond,emp[i].name);

Example 5-12. duel and loop alternatives

A useful alternative to loops is the x=>y operator. It returns y for each value of x, setting the underbar (_) to reference x's value. For example:

(dbx) ..100 => if(emp[_].code>400) emp[_].code,emp[_].name   

Using _ instead of i also avoids the need for {i}. Finally, the x-->y operator expands lists and other data structures. If head points to a linked list threaded through the next field, then:

(dbx) duel head-->next->data   
head->data = 12
head->next->data = 14
head-->next[[2]]->data = 20
head-->next[[3]]->data = 26

This produces the data field for each node in the list. x-->y returns x, x->y, x->y->y, x->y->y->y, ... until a NULL is found. The symbolic output x-->y[[n]] indicates that ->y was applied n times. x[[y]] is also the selection operator:

(dbx) duel head-->next[[50..60]]->data   

This example returns the 50th through the 60th elements in the list. The #/x operator counts the number of values. For example:

(dbx) duel #/( head-->next->data >? 50 )   

This example counts the number of data elements over 50 on the list. Several other operators, including x@y, x#y, and active call stack access are described in the “duel Operators”.

duel Operator Summary

Most duel operators have the same precedence as their C counterparts. Table 5-2, lists duel operators in decreasing precedence.

Table 5-2. duel Operator Summary





{} () [] -> . f() -->

x-->y expands x->y x->y->y ...


x[[y]] x#y x@y

Generate x; select, index, or stop at y


#/ - * & ! ~ ++ -- (cast)

#/x number of x values


frame(n) sizeof(x)

Reference to call stack activation level n



Simple assignment


x/y x*y x%y

Multiply, divide, and reminder


x-y x+y

Add and subtract


x<<y x>>y

Shift left and shift right


x..y ..y x..

..y 0..y-1. x..y Return x, x+1...y


< > <= >= <? >? <=? >=?

x>?y Return x if x>y


== != ==? !=?

x==?y Return x if x=y











x&&y &&/x

&&/x Are all x values nonzero?


x||y ||/x

||/x Is any x value nonzero?


x? y:z

For each x, if(x) y else z



x:=y set x as a duel alias to y



Return x, then y



For each x, evaluate y with x value `_'


if() else while() for()

C statements cast as operators



Evaluate and ignore x, return y



Fortran multidimensional array separator: x[7,,5]. Use square brackets; x(7,,5) will not work in duel.

duel Examples

Table 5-3, lists and briefly explains duel examples.

Table 5-3. duel Examples



duel (0xff-0x12)*3

Compute simple expression

duel (1..10)*(1..10)

Display multiplication table

duel x[10..20,22,24,40..60]

Display x[i] for the selected indexes

duel x[9..0]

Display x[i] backwards

duel x[..100] >? 5

Display x[i] that are greater than 5

duel x[..100] >? 5 <? 10

Display x[i] if 5<x[i]<10

duel x[..100] ==? (6..9)

Same as above

duel x[0..99]=>if(_>5 && _<10) _

Same as above

duel y[x[..100] !=? 0]

Dsplay y[x[i]] for each nonzero x[i]

duel emp[..50].code

Display emp[i].code for i=0 to 49

duel emp[..50].(code,name)

Display emp[i].code & emp[i].name

duel val[..50].(is_dbl? x:y)

Display val[i].x or val[i].y depending on val[i].is_dbl.

duel val[..50].if(is_dbl) x else y

Same as above

duel (hash[..1024]!=?0)->scope

hash[i].scope for non-null hash[i]

duel x[i:=..100] >? x[i+1]

Check if x[i] is not sorted

duel x[i:=..100] ==? x[j:=..100]=> if(i<j) x[{i,j}]

Check if x has nonunique elements

duel if(x[i:=..99] == x[j:=i+1..99]) x[{i,j}]

Same as above

duel (x[..100] >? 0)[[0]]

Return the first (0th element) positive x[i]

duel (x[..100] >? 0)[[2]]

Return the third positive x[i]

duel (x[..100] >? 0)[[..5]]

Return the first five positive x[i]

duel (x[0..] >? 6)[[0]]

Return the first x[i]>6, no limit on i

duel argv[0..]@0

argv[0] argv[1].. until first null

duel x[0..]@20 >? 9

x[0..n]>9 where n is first x[n]==20

duel emp[0..]@(code==0)

emp[0]..emp[n-1] where emp[n].code==0

duel head-->next->val

Return val of each element in a linked list

duel head-->next[[20]]

Return the twenty-first element of a linked list

duel *head-->next[[20]]

Display above as a struct

duel #/head-->next

Count elements on a linked list

duel x-->y[[#/x-->y - 1]]

Reutrn last element of a linked list

duel x-->y[[#/x-->y - 10..1]]

Return last ten elements of a linked list

duel head-->next-> if(next) val >? next->val

Check if the list is sorted by val

duel head-->(next!=?head)

Expand cyclic linked list (tail->head)

duel head-->(next!=?_)

Handle termination with p->next==p

duel root-->(left,right)->key

Expand binary tree, and show keys

duel root-->(left,right)->( (left!=?0)->key>=?key, (right !=?0 )->key<=?key)

Check bin tree as sorted by key

duel (T mytype) x

Convert x to user defined type mytype

duel (struct s*) x

Convert x to struct s pointer

duel if(x) y; else z *ERR*

`;' must be followed by an expression

duel {x} y *ERR*

`}' requires `;' if followed by exp

fortarray[2..5,, 6,7]

Print two-dimensional Fortran array elements

duel Semantics

The duel semantics are modeled after the Icon programming language. The input consists of expressions that return sequences of values. C statements are cast as expressions, too. Expressions are parsed into abstract syntax trees, which are traversed during evaluation. The evaluation of most nodes (operators) recursively evaluates the next value for each operand, and then applies the operator to produce the next result. Only one value is produced each time, and duel's eval function keeps a state record for each node (backtracking, co-routines, consumer-producer or threads are good metaphors for the evaluation mechanism).

For example, in (5,3)+6..8, the evaluation of + first retrieves the operands 5 and 6, to compute and return 5+6. Then 7, the next right operand is retrieved and 5+7 is returned, followed by 5+8. Since no other right operand value exists, the next left operand, 3 is fetched. The right operand's computation is restarted returning 6, and 3+6 is returned. The final return values are 3+7 and 3+8.

The computation for operators like x>?y is similar, but when x<=y, the next values are fetched instead of returning a value, forming the basis for an implicit search. Operators like .. return a sequence of values for each pair of operands.

The duel values follow the C semantics. A value is either an lvalue (can be used as the left-hand side of assignment), or an rvalue. Therefor, objects like arrays can not be directly manipulated. However, operators like x..y can accomplish such tasks.

The duel types also follow the C semantics, with some important differences. C types are checked statically; duel types are checked when operators are applied. For example, (1,1.0)/2 returns 0 (int) and 0.5 (double); (x,y).z returns x.z and y.z even if x and y are of different types, as long as they both have a field z.

Values and types of symbols are looked up at run-time (using the dbx lookup rules).

To avoid this ambiguity, the keyword T must precede a user-defined type. For example, if value is a typedef, C's (value (*)()) x is written in duel as (T value (*)()) x. Types that begin with a reserved keyword don't need T. For example, (struct value*) x and (long *[5]) y are accepted. As special cases, (type)x and (type*)x are accepted but discouraged (it causes (printf)(“hi”), which is valid in C, to fail). A side effect is that sizeof x must be written as sizeof(x).

duel Operators

The duel operators are described in the following list:

x=y x+y x-y x*y x/y x%y x^y x|y x&y x<<y x>>y

x>y x<y x>=y x<=y x==y x!=y x[y]

These binary operators follow their C semantics. For each value of x, they are evaluated for every value of y. For example, (5,2)>(4,1) evaluates as 5>4, 5>1, 2>4, 2>1 returning 1, 1, 0, 1.

The y values are reevaluated for each new value of x. For example, i=4; (4,5)>i++ evaluates as 4>4 and 5>5. Beware of multiple y values in assignment. For example, x[..3]=(4,6,9) does not set x[0]=4, x[1]=6, and x[2]=9. It assigns 4, 6, and 9 to each element, which has the same effect as x[..3]=9. Use x[i:=..3]=(4,6,9)[[i]] to achieve the desired effect.

-x ~x &x *x !x ++x --x x++ x-- sizeof(x) (type)x

These unary operators follow their C semantics. They are applied to each value of x. The increment and decrement operators require an lvalue, so i:=0 ; i++ produces an error because i is a duel alias to 0, an rvalue. Parenthesis must be used with sizeof(x). Note that sizeof x is not allowed. Cast to user defined type requires generally requires T. For example, (T val(*)())x, but (val)x and (val*)x are accepted as special cases.

x&&y x||y

These logical operators also follow their C semantics, but have nonintuitive results for multi-valued x and y. For example, (1,0,0) || (1,0) returns 1,1,0,1,0 - the right hand-side (1,0) is returned for each left-hand side 0. It is best to use these operators only in single value expressions.

x? y:z if(x)y if(x)y else z

These expressions return the values of y for each nonzero value returned by x, and the values of z for each zero value returned by x. For example, if(x[..100]==0) y returns y for every x[i]==0, not if all x[i] are zero (if(&&/(x[..100]==0)); y does that). Also, if(x) y; else z is illegal. duel's semicolon is an expression separator, not a terminator.

while(x)y for(w;x;y)z

The while(x)y expression returns y as long as all values of x are nonzero. The for() expression is similar and both have the expected C semantics. For example, for(i=0 ; i<100 ; i++) x[i] is the same as x[..100]. Unlike the if() expression, while(x[..100]==0) continues to execute only if all elements of x are zero, that is, the condition is evaluated into a single value using an implicit &&/x.

At present, assignments are not supported, so the for is of limited utility except to assign aliases.

x,y x..y ..x x..

These operators produce multiple values for single value operands. x,y returns x, then y. x..y returns the integers from x to y. When x>y, the sequence is returned in descending order, that is, 5..3 returns 5, 4, 3.

The ..x operator is a shorthand for 0..x-1. For example, ..3 returns 0, 1, 2. The x.. operator is a shorthand for x..maxint. It returns increasing integer values starting at x indefinitely, and should be bounded by [[n]] or @n operators.

A comma (,) retains its precedence level in C. The precedence of .. is above < and below arithmetic operators, so 0..n-1 and x==1..9 work as expected.


The ,, operator is very low precedence, is only usable inside the [] array operators, and is used to separate the dimension expressions of Fortran multi-dimensional arrays. Note the deviation from Fortran and dbx command-line usage; array operators are square brackets, [ ], not parentheses, ( ).

x<?y x>?y x>=?y x<=?y x!=?y x==?y

These operators work like their C counterparts but return x if the comparison is true. If the comparison is false, the next (x,y) value is tried, forming the basis of an implicit search.

(x) {x} x;y x=>y

Both () and {} act as C parenthesis.

The {} set the returned symbolic value as the actual value. For example, if i=5 and x[5]=3, then x[i] produces the output x[i] = 3, x[{i}] produces x[5] = 3, and {x[i]} produces 3.

The semicolon is an operator. x;y evaluates x, ignoring the results, then evaluates and returns y. For example, (i:=1..3 ; i+5) sets i to 3 and returns 8.

The x=>y operator evaluates and returns y for each value of x. For example, (i:=1..3 => i+5) returns 6, 7, and 8. The value returned by x is also stored implicitly in _, which can be used in y. For example, 1..5 => z[_][_] produces z[1][1], z[2][2], and so forth. The symbolic value for _ is that of the left side value, hence {_} is not needed.

Semicolon (;) has the lowest precedence, so it must be used inside () or {} for compound expressions. The precedence of => is just below comma (,).

Be aware that if(a) x; else {y;} z is illegal; a semicolon is not allowed before } or else and must be inserted before z.

x->y x.y

These expressions work as in C for a symbol y. If y is an expression, it is evaluated under the scope of x. For example, x.(a+b) is the same as x.a+x.b, if a and b are fields of x (if they are not, they are looked up as local or global variables). x may return multiple values of different types. For example, (u,v).a returns u.a and v.a, even if u and v are different structures.

Also, the value of x is available as _ inside y. For example, x[..100].(if(a) _) produces x[i] for each x[i].a!=0. Nested x.y are allowed. For example, u.(v.(a+b)) looks up a and b first under v, then under u.


The duel aliases store a reference to y in x. Any reference to x is then replaced by y. If y is a constant or an rvalue, its value is replaced for x. If y is an lvalue (e.g., a variable), a reference to same lvalue is returned. For example, x:=emp[5] ; x=9 assigns 9 to emp[5].

Aliases retain their values across invocation of the duel command. A duel alias to a local variable references a stray address when the variable goes out of scope.

The special command duel clear delete all the duel aliases; duel alias shows all current duel aliases. Symbols are looked up as duel aliases first, so a duel alias x will hide a local x.

The duel aliases are separate from dbx aliases. Currently, duel aliases are shared across all processes.


The expansion operator x-->y expands a data structure x following the y links.

It returns x, x->y, x->y->y, until a null is found. If x is null, no values are produced. If y returns multiple values, they are stacked and each is further expanded in a depth-first notion. For example, if r is the root of a tree with children u->childs[..u->nchilds], then u-->(childs[..nchilds]) expands the whole tree. y is an arbitrary expression, evaluated exactly like x->y (this includes _).


The expression x@y produces the values of x until x.y is nonzero. For example, for(i=0 ; x[i].code!= -1 && i<100 ; i++) x[i] can be written as x[..100]@(code==-1).

The evaluation of x is stopped as soon as y evaluates to true. x->y (or x=>y) is used to evaluate y when x is not a struct or a union. If y is a constant, (_==y) is used. For example, s[0..]@0 produces the characters in string s up to but not including the terminating null.

#/x &&/x ||/x

These operators return a single summary value for all the values returned by x. The #/x returns the number of values returned by x. For example, #/(x[..100]>?0) counts the number of positive x[i]. The &&/x returns 1 if all the values produced by x are nonzero, and ||/x returns 1 if any of x's values are nonzero. Like in C, the evaluation stops as soon as possible.

For example, ||/(x[..100]==0) and &&/(x[..100]==0) check if one or all of x[i] are zero, respectively.

x#y x[[y]]

The operator x#y produces the values of x and arranges for y to be an alias for the index of each value in x. It is commonly used with x-->y to produce the element's index. For example, head-->next->val#i=i assigns each val field its element number in the list.

The selection operator x[[y]] produces the yth result of x. If y returns multiple value, each select a value of x. For example, (5,7,11,13)[3,0,2] returns 13, 5, and 11 (13 is the third element, 5 is the 0th element).

Do not use side effects in x, since its evaluation can be restarted depending on y. For example, after (x[0..i++])[[3,5]] the value of i is unpredictable.

Note: Within a duel command, the # operator does not have anything to do with line numbers or dbx comments.

frame(n) frames_no func.x

The frame(n) for an integer n returns a reference to the nth frame, or activation level, on the stack (0 is the inner most function and frame(frames_no-1) is main()).

Frame values can be compared to function pointers. For example, frame(3)==myfunc is true if the fourth frame is a call to myfunc, and in scope resolution. For example, frame(3).x returns the local variable x of the fourth frame.

The frames_no is the number of active frames on the stack. For example, (frames(..frames_no) ==? myfunc).x displays x for all active invocations of myfunc. As a special case, (frames(..frames_no)==?f)[[0]].x can be written as f.x (x can be an expression).

Differences from Other Languages

The following list describes the differences between duel and the C, and Fortran languages.

  • Differences from C: both {} and ; are operators, not statements or expression separators. For example, if(x) y; else {z;} u is illegal; use if(x) y else {z} ; u. Ambiguities require preceding user-defined types (typedef) with the keyword T.

    For example, if value is a user type, C's sizeof(value*) is written sizeof(T value*), except for the casts (t)x and (t*)x; sizeof(x) requires parenthesis for variable x.

  • Differences from Fortran: because the comma (,) is used to separate a sequence of values, the usual dbx syntax for multi-dimensional array references of myarr[3,4] does not mean the same thing to duel as it does to dbx.

    In duel, refer to the dimensions of a multi-dimensional Fortran array using ,, as the dimension separator. In other words, if myarr is a two-dimensional array, myarr[3,,4] refers to the Fortran array element myarr(3,4).

    The base dbx syntax for this element remains unchanged. For example, to show that element of myarr, use one of the following:

    (dbx) print myarr[3,4]   
    (dbx) duel  myarr[3,,4]   

Determining Variable Scopes and Fully Qualified Names

The which command allows you to determine the scope of a variable. This command is useful for programs that have multiple variables with the same name occurring in different scopes.

Example 5-13. which command

The which command prints the fully qualified name of the active version of a specified variable. For example, to determine the scope of the variable i, enter:

(dbx) which i

In this example, the variable i that is currently active is local to the procedure foo2 that appears in the module foo (corresponding to the file foo.c in a C language program).

The which command also determines the fully qualified name of other program elements, such as procedures or type descriptors, that are submitted as arguments for the command. The whereis command prints the fully qualified names of all versions of the name of any program element. dbx searches (a possibly limited part of) your program for all occurrences of the name and returns the fully qualified names. The range of the search is determined by the dbx $whereisdsolimit variable. By default, $whereisdsolimit is 1 and only the main executable is checked by whereis. To search all objects, set $whereisdsolimit to 0. To check just the first n objects, set $whereisdsolimit to n.

Displaying Type Declarations

The whatis command displays the type declaration for a specified variable or procedure in your program.

Example 5-14. whatis command

To display the type declaration for the variable i, enter:

(dbx) whatis i
int i;

The following example illustrates the output of whatis for an array of structures:

(dbx) whatis array
struct list {
    struct list* next;
    int value;
} array[12];

When you provide a procedure name to whatis, dbx reports the type of the value returned by the procedure and the types of all arguments to the procedure:

(dbx) whatis foo
int foo(i)
int i;
(dbx) whatis main
int main(argc, argv)
int argc;
char** argv;

Examining the Stack

Each time your program executes a procedure, the information about where in the program the call was made from is saved on a stack. The stack also contains arguments to the procedure and all of the procedure's local variables. Each procedure on the stack defines an frame. Activation levels can also consist of blocks that define local variables within procedures.

The most recently called procedure or block is numbered 0. The next active procedure (the one that called the current procedure) is numbered 1. The last activation level is always the main program block.

The stack determines the scope of many dbx commands and expressions. For example, unless you qualify a variable, as described in “Qualifying Names of Program Elements”, dbx assumes that variables you reference are local to the current activation level. If a variable does not appear in the current activation level, dbx successively examines previous activation levels in the stack until it finds the referenced variable. The maximum number of activation levels examined is determined by the dbx $stacktracelimit variable, which has a default value of 100.

Printing Stack Traces

The where command prints stack traces. Stack traces show the current activation levels (procedures) of a program.

Example 5-15. Stack trace

Consider the following stack trace for a program called test:

(dbx) where
>  0 foo2(i = 5) [“/usr/var/tmp/dbx_examples/test.c”:44, 0x1000109c]
   1 foo(i = 4) [“/usr/var/tmp/dbx_examples/test.c”:38, 0x1000105c]
   2 main(argc = 1, argv = 0xffffffad78) [“/usr/var/tmp/dbx_examples/test.c”:55,
   3 __start() [“/shamu/lib/libc/libc_64/crt1text.s”:137, 0x10000ee4]

This program has four activation levels. The most recent, a call of the procedure foo2, is numbered 0. The currently selected activation level is 0, indicated by the > character.

The stack trace also reports that foo2 was passed one argument: the value 5 was assigned to the local variable i. The trace indicates that the program was stopped at line 44 of the file test.c, which translates to machine address 0x1000109c.

The stack trace reports similar information for the next two activation levels in this example. You can see that the function foo called foo2 from line 38 in test.c. In turn, foo was called by main at line 55 of the file test.c. Finally, the run-time start-up level was called at line 137 from the file ctrltext.s.

If a program is highly recursive, stack traces can get quite long. The dbx $stacktracelimit variable controls the maximum number of activation levels that appear in a stack trace. In the example above, setting $stacktracelimit = 2 before issuing the where command reduces the set of reported frames to just levels 0 and 1.

Example 5-16. Stack trace and -g compiler option

If you compile with -g0 or with no -g option, limited symbols are reported. In cases such as this, where detailed symbolic information is not available, the four hexadecimal values returned represent dbx's guess that the function has four integer arguments.

The following example illustrates such a case:

(dbx) where
>  0 fooexample(0x300000000, 0x4000000ff, 0x5000000ff, 0x0)
[“/usr/var/tmp/dbx_examples/test3.c”:10, 0x10000cf8]
  1 main(0x3, 0x4, 0x5, 0x0) [“/usr/var/tmp/dbx_examples/test3.c”:5,
  2 __start() [“/shamu/lib/libc/libc_64/csu/crt1text.s”:137,
(dbx) quit
Process 22582 terminated
int fooexample(int,int,int);
int main()
       return 0;
int fooexample(int i, int j, int k)
       int x = i + j + 3*k;
       return x;

The following examples show register values from code compiled without a -g option. MIPS1 or MIPS2 code using the 32-bit ABI (for example, on an Indy workstation):

(dbx) where
> 0 subr1(0x3, 0x7fffaf14, 0x7fffaf1c, 0x0) [“t.c”:3, 0x4009ec]
  1 test(0x3, 0x7fffaf14, 0x7fffaf1c, 0x0) [“t.c”:8, 0x400a10]
  2 main(0x1, 0x7fffaf14, 0x7fffaf1c, 0x0) [“t.c”:13, 0x400a48]
  3 __start() [“crt1text.s”:133, 0x40099c]

There are four hexadecimal values displayed in most lines of the code above since the 32-bit MIPS ABI has four integer argument passing registers. No user-useful registers are passed to __start().

MIPS3 or MIPS4 code using the 64-bit ABI (for example, on a Power Challenge system):

(dbx) where
> 0 subr1(0x3, 0xffffffaed8, 0xffffffaee8, 0x0, 0x2f, 0x10, 0x0, 0xfbd82a0)
[“/usr/people/doc/debug/t.c”:3, 0x10000c9c]
  1 test(0x3, 0xffffffaed8, 0xffffffaee8, 0x0, 0x2f, 0x10, 0x0, 0xfbd82a0)
[“/usr/people/doc/debug/t.c”:9, 0x10000ce8]
  2 main(0x1000000ff, 0xffffffaed8, 0xffffffaee8, 0x0, 0x2f, 0x10, 0x0,
0xfbd82a0) [“/usr/people/doc/debug/t.c”:14, 0x10000d2c]
  3 __start() [“/shamu/redwood2/work/irix/lib/libc/libc_64/csu/crt1text.s”:137,

There are eight hexadecimal values displayed in most lines of the code above since the 64-bit MIPS ABI has eight integer argument passing registers. No user-useful registers are passed to __start().

The values listed as arguments are the integer argument-passing register values. Typically, only the 0 entry of the stack has those argument values correct. Correctness is not guaranteed because the code generator can overwrite the values, using the registers as temporary variables.

The debugger reports the integer argument-passing registers because this information may be of some value.

For example, for the code samples above, the following code calls subr1():

int test(void)

This code displays 0x3 as the argument register value. The other registers listed for subr1 contain arbitrary data.

Moving within the Stack

The up and down commands move up and down the activation levels in the stack. These commands are useful when examining a call from one level to another. You can also move up and down the activation stack with the func command described in “Moving to a Specified Procedure”.

The up and down commands each take num as an argument. up [num] moves up the specified number of activation levels in the stack. The default is one level. down [num] moves down the specified number of activation levels in the stack. The default is one level.

When you change activation levels, your scope changes. For example, unless you qualify a variable, as described in “Qualifying Names of Program Elements”, dbx assumes that variables you reference are local to the current activation level. Also, dbx changes the current source file to the file containing the procedure's source.

Consider examining the stack trace for a program called test4 and moving up in the activation stack:

(dbx) where
>  0 foo2(i = 5) [“/usr/var/tmp/dbx_examples/foo.c”:46, 0x10001214]
   1 foo(i = 4) [“/usr/var/tmp/dbx_examples/foo.c”:40, 0x100011d4]
   2 main(argc = 1, argv = 0xffffffad78)
[“/usr/var/tmp/dbx_examples/test4.c”:25, 0x10000fa0]
   3 __start() [“/shamu/lib/libc/libc_64/csu/crt1text.s”:137, 0x10000f34]
(dbx) print i
(dbx) up 
foo:  40  r = foo2(i+1);

The current activation level is now the procedure foo. As indicated in the output, the variable i receives the argument passed to foo and is therefore local to foo. The variable i at this activation level is different from the variable i in the foo2 activation level. You can reference the currently active i as i; whereas you must qualify the reference to the i in foo2:

(dbx) print i
(dbx) print foo2.i
<symbol not found>

Moving up one more activation level brings you to the main procedure:

(dbx) up
main:  25  j = foo(j);
(dbx) file

In this example, the source for main is in test4.c, whereas the source for foo and foo2 is in foo.c; therefore, dbx changes the current source file when you move up to the main activation level.

dbx resets the source file when you return to the foo2 activation level:

(dbx) down 2
foo2:  46  printf(“foo2 arg is %d\n”,i);
(dbx) file

Moving to a Specified Procedure

The func command moves you up or down the activation stack. You can specify the new activation level by providing either a procedure name or an activation level number.

The syntax for the func command is:

func [activation_level][procedure]

The following arguments are available:

  • func (with no arguments): displays the name of the procedure corresponding to the current activation level.

  • activation_level|procedure: changes the current activation level. If you specify an activation_level by number, dbx changes to that activation level. If you specify a procedure, dbx changes to the activation level of that procedure. If you specify a procedure name and that procedure has called itself recursively, dbx changes to the most recently called instance of that procedure.

When you change your activation level, your scope changes. For example, unless you qualify a variable as described in “Qualifying Names of Program Elements”, dbx assumes that variables you reference are local to the current activation level. Also, dbx changes the current source file to the one containing the procedure's source and the current line to the first line of the procedure.

You can also give the func command the name of a procedure that is not on the activation stack, even when your program is not executing. In this case, dbx has no corresponding activation level to make current. However, dbx still changes the current source file to the one containing the procedure's source and the current line to the first line of the procedure.

Example 5-17. func command

For example, consider the following activation stack:

(dbx) where
>  0 foo2(i = 5) [“/usr/var/tmp/dbx_examples/foo.c”:46, 0x10001214]
   1 foo(i = 4) [“/usr/var/tmp/dbx_examples/foo.c”:40, 0x100011d4]
   2 main(argc = 1, argv = 0xffffffad78)
[“/usr/var/tmp/dbx_examples/test4.c”:25, 0x10000fa0]
   3 __start() [“/shamu/lib/libc/libc_64/csu/crt1text.s”:137, 0x10000f34]

In this case, you can go to the main activation stack by entering:

(dbx) func main
main:  25  j = foo(j);

This command changes the current activation level to 2 and changes the current source file to test4.c.

If you use the func command to go to a function that is not on the activation stack, dbx changes only the current source file to the one containing the procedure's source and the current line to the first line of the procedure:

(dbx) func bar
   3  {
(dbx) file

Printing Activation Level Information

The dump command prints information about the variables in an activation level. The following is the syntax for this command:

dump [procedure] [.]

The following arguments are available:

  • dump (with no arguments): prints information about the variables in the current procedure.

  • procedure: prints information about the variables in the specified procedure. The procedure must be active. Starts searching for procedure at the current activation level as set by the up or down command. (See “Moving within the Stack”, for more information about the up and down commands.)

  • . : prints information about the variables in all procedures in all activation levels.

Example 5-18. dump command

Executing dump while in a function called foo2 appears as:

(dbx) dump
foo2(i = 5) [“/usr/var/tmp/dbx_examples/foo.c”:46, 0x10001214]

To examine the information for the procedure main, enter:

(dbx) dump main
main(argc = 1, argv = 0xffffffad78) [“/usr/var/tmp/dbx_examples/test4.c”:25,
j = 4
i = 12
r = <expression or syntax error>
a = 0
total = 0

To perform a complete dump of the program's active variables, enter:

(dbx) dump .
>  0 foo2(i = 5) [“/usr/var/tmp/dbx_examples/foo.c”:46, 0x10001214]
   1 foo(i = 4) [“/usr/var/tmp/dbx_examples/foo.c”:40, 0x100011d4]
r = 0
   2 main(argc = 1, argv = 0xffffffad78)
[“/usr/var/tmp/dbx_examples/test4.c”:25, 0x10000fa0]
j = 4
i = 12
r = <bad operand>
a = 0
total = 0

Using Interactive Function Calls

You can interactively call a function in your program from dbx.

If the function returns a value, you can use that function in a normal dbx expression. For example, consider a function prime defined in your program that accepts an integer value as an argument, and returns 1 if the value is prime and 0 if it is not. You can call this function interactively and print the results by entering a command such as:

(dbx) print prime(7)

Using the ccall Command

If your function does not return a value, or if you want to execute a function primarily for its side effects, you can execute the function interactively with the dbx command ccall. The following is the syntax for this command:

ccall func [arg1, arg2,... ,argn]

This command calls a function with the given arguments. Regardless of the language the function was written in, the call is interpreted as if it were written in C, and normal C calling conventions are used.

Note: Structure and union arguments to a function, and structure and union returns from a function, are not supported.

Functions called interactively honor breakpoints. Thus you can debug a function by setting breakpoints and then calling it interactively.

Example 5-19. Activation levels and stack trace

If you perform a stack trace using the where command while stopped in a routine executed interactively, dbx displays only those activation levels created by your interactive function call. The activation levels for your active program are effectively invisible.

For example, a stack trace looks like this during an interactive function call:

(dbx) where
>  0 foo2(i = 9) [“/usr/var/tmp/dbx_examples/foo.c”:46, 0x10001214]
   1 foo(i = 8) [“/usr/var/tmp/dbx_examples/foo.c”:40, 0x100011d4]

===== interactive function call =====

   2 foo2(i = 5) [“/usr/var/tmp/dbx_examples/foo.c”:46, 0x10001214]
   3 foo(i = 4) [“/usr/var/tmp/dbx_examples/foo.c”:40, 0x100011d4]
   4 main(argc = 1, argv = 0xffffffad78)
[“/usr/var/tmp/dbx_examples/test4.c”:25, 0x10000fa0]
   5 __start() [“/shamu/lib/libc/libc_64/csu/crt1text.s”:137, 0x10000f34]

If you stop execution of an interactively called function, you are responsible for eventually unstacking the call and returning from the function call. To unstack a call, you can complete the call using dbx commands such as cont, resume, next, or step as many times as necessary. If you run or rerun your program, dbx automatically unstacks all interactive function calls.

Using the clearcalls Command

Another way to unstack an interactive function call is to execute the clearcalls command, which clears all stopped interactive calls.

(dbx) clearcalls

When stopped or faulted within one or more nested interactive calls, the clearcalls command removes these calls from the stack and returns the program to its regular callstack. This command is useful when a segmentation fault, infinite loop, or other fatal error is encountered within the interactive call.

When stopped in an interactive call, the call stack displayed by where shows the following line at the end of each stack of interactive call instantiation.

==== interactive function call ====

Example 5-20. Use of clearcalls

If the procedure foo() is interactively called from main(), you see the following stack:

>  0 foo2(i = 9) [“/usr/var/tmp/dbx_examples/foo.c”:46, 0x10001214]
   1 foo(i = 8) [“/usr/var/tmp/dbx_examples/foo.c”:40, 0x100011d4]

===== interactive function call =====

   2 foo2(i = 5) [“/usr/var/tmp/dbx_examples/foo.c”:46, 0x10001214]
   3 foo(i = 4) [“/usr/var/tmp/dbx_examples/foo.c”:40, 0x100011d4]
   4 main(argc = 1, argv = 0xffffffad78)
[“/usr/var/tmp/dbx_examples/test4.c”:25, 0x10000fa0]
   5 __start() [“/shamu/lib/libc/libc_64/csu/crt1text.s”:137, 0x10000f34]

Nesting Interactive Function Calls

You can also nest interactive function calls. In other words, if you have one or more breakpoints in a function, and you call that function repeatedly, each interactive call is stacked on top of the previous call. Breakpoints in a function affect all nesting levels, so you cannot have different breakpoints at different nesting levels.

Example 5-21. Nesting levels

The where command shows the entire stack trace from which you can determine the nesting depth. The following example has two nesting levels.

(dbx) where
>  0 foo2(i = 17) [“/usr/var/tmp/dbx_examples/foo.c”:46, 0x10001214]
   1 foo(i = 16) [“/usr/var/tmp/src/dbx_examples/foo.c”:40, 0x100011d4]

===== interactive function call =====

   2 foo2(i = 9) [“/usr/var/tmp/dbx_examples/foo.c”:46, 0x10001214]
   3 foo(i = 8) [“/usr/var/tmp/dbx_examples/foo.c”:40, 0x100011d4]

===== interactive function call =====

   4 foo2(i = 5) [“/usr/var/tmp/dbx_examples/foo.c”:46, 0x10001214]
   5 foo(i = 4) [“/usr/var/tmp/dbx_examples/foo.c”:40, 0x100011d4]
   6 main(argc = 1, argv = 0xffffffad78)
[“/usr/var/tmp/src/dbx_examples/test4.c”:25, 0x10000fa0]
   7 __start() [“/shamu/lib/libc/libc_64/csu/crt1text.s”:137,

To set a conditional breakpoint, for example, type:

(dbx) stop in foo if j == 7
Process     0: [3] stop in foo if j==7

If j is not within the scope of foo, then you will receive an error message if you attempt to call foo interactively. To prevent this, disable or delete any such breakpoints, conditional commands, or traces before executing the interactive function call.

Obtaining Basic Blocks Counts

dbx permits interactive control of a pixie-instrumented binary.

pixie clear clears the basic block counts for the current execution. pixie write writes out the counts file with the current basic block counts. The counts reflect the execution of the program since the run command or since the last pixie clear command, whichever was more recent.

When you debug a program that has been instrumented by pixie, it is often desirable to perform experiments over different code paths and do comparisons of the results. You can do this by capturing the pixie basic block counts at any point in the program's execution.

Example 5-22. Basic block counts

Suppose you want to determine the basic block counts for the section of code between lines 10 and 15 of a given file. Just set breakpoints at the two lines of interest, zero the counts when the first breakpoint is encountered, and then write out the counts file when the second breakpoint is encountered.

(dbx) stop at “pix.c”:15
Process 0: [3] stop at “pix.c”:15
(dbx) stop at “pix.c”:20
Process 0: [4] stop at “pix.c”:20
(dbx) run
Process 997 (pix.pixie) started
[3] Process 997 (pix.pixie) stopped at [main:15 ,0x400a48  (pixie
0x404570)] 15 first = 12;
(dbx) pixie clear
[4] Process 997 (pix.pixie) stopped at [main:20 ,0x400aa8 (pixie
0x404684)] 20 total = multiply(total, 2);
(dbx) pixie write
(dbx) sh prof -pixie prog
Profile listing generated Tue Feb 14 11:08:46 1995
    with:       prof -pixie prog
Total cycles  Total Time  Instructions  Cycles/inst      Clock   Target
          53    5.3e-07s            27        1.963   100.0MHz    R4000

          10: Total number of Load Instructions executed.
          40: Total number of bytes loaded by the program.
           3: Total number of Store Instructions executed.
          12: Total number of bytes stored by the program.

           2: Total number nops executed in branch delay slot.
           0: Total number conditional branches executed.
           0: Total number conditional branches actually taken.
           0: Total number conditional branch likely executed.
           0: Total number conditional branch likely actually taken.

          18: Total cycles waiting for current instr to finish.
          26: Total cycles lost to satisfy scheduling constraints.
           5: Total cycles lost waiting for operands be available.
-p[rocedures] using basic-block counts.                               *
   Sorted in descending order by the number of cycles executed in each  *
   procedure. Unexecuted procedures are not listed.                     *
          cycles(%)  cum %     secs    instrns      calls procedure(file)

          27(50.94)  50.94     0.00         19      1 main(prog:prog.c)
          18(33.96)  84.91     0.00          4      1 multiply(prog:prog.c)
           8(15.09) 100.00     0.00          4      2 add(prog:prog.c)

The above example uses the sh command to invoke prof directly from dbx.

For more information about the prof and pixie commands, refer to the prof(1) and pixie(1) man pages.

Accessing C++ Member Variables

Debugging a program written in C++ is somewhat different from debugging programs written in other languages. This section describes features that affect how you access variables. See also “Referring to C++ Functions” in Chapter 6.

Typically you use standard C++ syntax to access member variables of objects. For example, if the string _name is a member variable of the object myWindow, you can print its value by entering:

(dbx) print myWindow._name
0x1001dc1c = “MenuWindow” 

To display a static member variable for a C++ class, you must specify the variable with the class qualifier. For example, to print the value of the static member variable costPerShare of the class CoOp, enter:

(dbx) print CoOp::costPerShare