Skip to content

Latest commit

 

History

History
394 lines (274 loc) · 12.2 KB

help.md

File metadata and controls

394 lines (274 loc) · 12.2 KB

DUEL - A high level data exploration language for gdb

Duel is a special purpose language designed for concise state exploration of debugged C programs, currently implemented for the GNU gdb. Duel is invoked by entering the gdb command duel (or dl) instead of print:

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

prints the array elements x[1] to x[10] that are greater than 5. The output includes the values 14 and 6, as well as their symbolic representation "x[3]" and "x[8]".

Note that some gdb concepts (such as the value history) do not work with the dl command, and Duel expressions are not understood by other gdb command.

Quick Start

Duel is based on expressions which return multiple values. The x..y operator returns all integers from x to y; the x,y operator returns x and then y, e.g.

(gdb) dl (1,9,12..15,22)

prints 1, 9, 12, 13, 14, 15 and 22. Such expressions can be used wherever a single value is used, e.g.

(gdb) dl x[0..99]

prints first 100 elements of array x.

Aliases are defined with x:=y:

(gdb) dl if(x[i:=0..99]<0) x[i]
x[i] = -4

The symbolic output "x[i]" can be fixed by surrounding i with curly braces, i.e.

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

The curly braces are like parentheses, but they replace the symbolic representation of the argument with its value. You can usually avoid this altogether with direct Duel operators:

(gdb) dl x[..100] <? 0
x[7] = -4

The ..n operator is a shorthand for 0..n-1, that is, "first n elements of the array". The x<?y, x==?y, x>=?y, etc., operators 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.

Operators x.y and x->y allow an expression y, evaluated under x scope:

(gdb) dl 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.

A useful alternative to loops is the x=>y operator. It returns y for each value of x, setting _ to reference the value of x, e.g.

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

Using _ instead of i also avoids the need for curly braces. When => operators are nested, __ refers to the parent scope value, ___ to the great-parent, and so on.

Finally, the x-->y operator expands lists and other data structures. If head points to a linked list threaded through the next field, then:

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

produce the data field for each node in the list. x-->y returns x, x->y, x->y->y, x->y->y->y, etc. 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:

(gdb) head-->next[[3]]->data
head-->next[[3]]->data = 26

For example,

(gdb) dl head-->next[[50..60]]->data

return the 50th through the 60th elements in the list. By specifying a negative number one can count from the end:

(gdb) head-->next[[-1]]->data
head-->next[[-1]]->data = 123

And curly braces evaluate the offset, as usual:

(gdb) head-->next[[{-1}]]->data
head-->next[[73]]->data = 123

The #/x operator counts the number of values, so

(gdb) dl #/( head-->next->data >? 50 )

counts the number of data elements over 50 on the list.

Operator x@y stops x generating values, as soon as y becomes true. It is mainly used with the unbounded x.. range:

(gdb) dl argv[0..]@(_ == 0)
argv[0] = 0x7fffffffe2bb "./a.out"
argv[1] = 0x7fffffffe2dd "--foo"
argv[2] = 0x7fffffffe300 "--bar"
argv[3] = 0x7fffffffe306 "42"

The @(_ == const) is so common, that it can be abbreviated to @const:

(gdb) dl argv[0..]@0

Semantics

Duel's semantics are modeled after the Icon programming language. The input consists of expressions which return sequences of values. C statements are expressions, too. Typically binary operators evaluate its operands and produce one result value for every combination of values from the left and right operands.

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 there are no other right operand value, 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:

(gdb) dl (5,3)+(6..8)
(5) + (6) = 11
(5) + (7) = 12
(5) + (8) = 13
(3) + (6) = 9
(3) + (7) = 10
(3) + (8) = 11

The computation for operators like x>?y is similar, but when the condition is false, the next values are fetched instead of returning a value, reducing the sequence of x values to those that satisfy the condition. Operators like .. return a sequence of values for each pair of operands.

Duel types also follow the C semantics, with some important differences. C types are checked statically; Duel types are checked when operators are applied, e.g., (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.

Commands

Command Description
duel prints the list of commands
dl alias for duel
dl help prints short help
dl ? alias for dl help
dl longhelp prints this help file
dl ?? alias for dl longhelp
dl examples prints Examples section of this file
dl operators prints Operators section of this file
dl aliases prints the list of aliases, see x:=y operator below
dl clear clears the list of aliases

Operators

Variables and functions of the inferior (program being debugged) can be read and called normally and work as expected. Convenience gdb variables (like $a) and references to results (like $1) can be used too.

The complete list of operators, in the precedence order:

  • (x), {x} - curly braces work as parentheses, but print the value of x, not its symbolic representation.
  • x#y - for every value of x, set y to the index of this x value (0, 1, ...).
  • x.y, x->y, x-->y, x[y], x[[y]], x@y - the first two operators are scope operators, they evaluate y in the scope of x. The third one walks the linked list, evaluating x, x->y, x->y->y, etc until NULL. The fourth is a familiar C array element access, the fifth takes the y-th value in the sequence of values of x (counting from the end, if y is negative). The last one stops x from generating values as soon as y (evaluated in the scope of x) becomes true. If y is a literal, stops as soon as x value becomes equal to y. If y is a sequence of values, stops when at least one value in the sequence is true, and prints x if at least one value in the sequence is false.
  • (cast)x, -x, *x, &x, !x, ~x, #/x, +/x, &&/x, ||/x - first six are conventional unary C operators, the last four are grouping operators. The first one counts the numbers of values of x, the second sums them, the third returns a boolean AND of all values of x, the fourth - boolean OR. Just like in C, AND and OR operators are lazy and stop as soon as the result value is known.
  • x/y, x*y, x%y - conventional C operators.
  • x-y, x+y - conventional C operators.
  • x<<y, x>>y - conventional C operators.
  • x..y, ..x, x.. - ranges. First returns integers from x to y, inclusive (x can be greater than y, for counting backwards). Second returns first x integers starting from 0 (in other words, it works like 0..x-1). Last returns an unlimited list of integers starting from x, and is normally used with x@y.
  • x<=y, x>=y, x<y, x>y, x<=?y, x>=?y, x<?y, x>?y - first four are conventional C operators, the second four return x if the condition is true, otherwise they return nothing (so, they work like a filter of the x value sequence).
  • x==y, x!=y, x==?y, x!=?y - same. First two are conventional C operators, the other two are filters.
  • x&y - conventional C operator.
  • x^y - conventional C operator.
  • x|y - conventional C operator.
  • x&&y - conventional C operator.
  • x||y - conventional C operator.
  • x?y:z - conventional C operator.
  • x:=y - create an alias x for the value of y. Note, that it is an alias to y, not a copy of y value. If y value changes, the value of the alias will change too. Use dl aliases and dl clear commands to see and, respectively, clear the list of aliases.
  • x,y - creates a sequence of two values.
  • x=>y - for every value of x, store it in _ and evaluate y.
  • if (x) y else z, if (x) y - C statement turned into an operator. In the first form it's equivalent to x?y:z, in the second form it has a filter semantics, returning y for every non-zero x (equivalent to x !=? 0 => y).
  • x;y - evaluate x, ignoring all the results, then evaluate y.

Examples

Compute simple expression:

dl (0xff-0x12)*3

Display multiplication table:

dl (1..10)*(1..10)

Display x[i] for the selected indexes:

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

Display x[i] backwards:

dl x[9..0]

Display x[i] elements that are greater than 5:

dl x[..100] >? 5

Display x[i] elements that are between 5 and 10:

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

Same:

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

Same, if x[i] elements are integers:

dl x[..100] ==? 6..9

Display y[x[i]] for each non-zero x[i]:

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

Display both emp[i].code and emp[i].name for first 50 emp[] elements

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

Display val[i].singl or val[i].doubl depending on val[i].is_doubl:

dl val[..50].(is_doubl?doubl:singl)

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

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

Check if x[i] array is sorted:

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

Check if x has non-unique elements:

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

Same:

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

Find the first positive x[i]

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

Find first five positive x[i]

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

Return the first x[i] greater than 6 (note, no limit on i):

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

Display argv[] array until the first NULL:

dl argv[0..]@0

Display emp[] values until emp[i].code is 0:

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

Walk the linked list by the next pointer, starting from head, print val of each list element:

dl head-->next->val

Display the 21st element of a linked list:

dl head-->next[[20]]

Count elements on a linked list:

dl #/head-->next

Find the last element in a linked list:

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

Or just

dl x-->y[[-1]]

Walk the cyclic linked list (start from head, walk until seeing head again):

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

Walk the binary tree:

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

Select matching strings from the array:

dl $_regex(items[i:=0..20], "foo") ==? 1 => items[i]

Find first 10 primes greater than 1000:

dl (1000..=>if(&&/(2,3.._-1=>__%_ )) _)[[..10]]

To Do

Features of the original Duel, that are not implemented in the Duel.py yet:

  • Builtins: frame(), sizeof(), frames_no, func.x
  • Variables: int i; i=5; i++
  • Assignments: x[..10]=0
  • while and for operators

Features that were not in the original Duel:

  • gdb scope specification: file.c::var
  • gdb convenience variables ($a), references to results ($1) and functions ($_streq)
  • negative indexes in [[ ]] operator

Author

Duel was designed by Michael Golan as part of a PhD thesis in the Computer Science Department of Princeton University. He also implemented Duel in C as a patch for gdb 4.x.

Duel.py is a pure-python implementation written by Sergei Golubchik

Duel stands for Debugging U (might) Even Like, or Don't Use this Exotic Language. Judge for yourself!