Tree Browser

Until recently the only way to debug trees from gdb was to call debug_tree as follows:

(gdb) p debug_tree (current_function_decl) 

An alternative for interactively scan tree structures is to use the Tree Browser. You can access Tree Browser from anywhere during a debugging session as follows:

(gdb) p browse_tree (current_function_decl)
 
Tree Browser 
foo 
Up/prev expressions updated. 
TB> 

For listing available commands, you could try:

TB> h 
Possible commands are: 
 
                   x  -  Exits tree-browser. 
                   q  -  Exits tree-browser. 
                   h  -  Prints this help message. 
              update  -  Update information about parent expressions. 
             verbose  -  Sets/unsets verbose mode (default is on). 
                 fun  -  Go to the current function declaration. 
                  nx  -  Go to the next expression in a BIND_EXPR. 
                  pr  -  Go to the previous expression in a BIND_EXPR. 
                  up  -  Go to the parent tree node. 
                last  -  Go to the last expression in a BIND_EXPR. 
               first  -  Go to the first expression in a BIND_EXPR. 
                 hpr  -  Go to the previous visited node (history previous). 
                arg0  -  Child 0. 
                arg1  -  Child 1. 
                arg2  -  Child 2. 
                arg3  -  Child 3. 
     decl_saved_tree  -  Body of a function. 
                type  -  Field accessor. 
                size  -  Field accessor. 
           unit_size  -  Field accessor. 
              offset  -  Field accessor. 
          bit_offset  -  Field accessor. 
             context  -  Field accessor. 
          attributes  -  Field accessor. 
     abstract_origin  -  Field accessor. 
           arguments  -  Field accessor. 
              result  -  Field accessor. 
             initial  -  Field accessor. 
            arg-type  -  Field accessor. 
 arg-type-as-written  -  Field accessor. 
               chain  -  Field accessor. 
              values  -  Field accessor. 
              domain  -  Field accessor. 
     method_basetype  -  Field accessor. 
              fields  -  Field accessor. 
           arg-types  -  Field accessor. 
            basetype  -  Field accessor. 
     pointer_to_this  -  Field accessor. 
   reference_to_this  -  Field accessor. 
                vars  -  Field accessor. 
        supercontext  -  Field accessor. 
                body  -  Field accessor. 
           subblocks  -  Field accessor. 
               block  -  Field accessor. 
                real  -  Field accessor. 
                imag  -  Field accessor. 
             purpose  -  Field accessor. 
               value  -  Field accessor. 
                 elt  -  Field accessor. 
                 min  -  Field accessor. 
                 max  -  Field accessor. 
                  sc  -  Search a node having a TREE_CODE given as a parameter. 
                  sn  -  Search an identifier having a name given as a parameter. 
                  pp  -  Pretty print current node. 
                   p  -  Prints the current node. 
TB>  

Note that this list of commands is susceptible to change, since this is a pretty new tool and is still in development.

Now let's try some of these commands: we're on the declaration of the current function, we can have a look at its body.

TB> decl_saved_tree 
{ 
  int T.1; 
  int T.2; 
  int i; 
  extern  koo; 
  extern  bar; 
  extern  toons; 
 
  i = 0, i = i + 1, koo (), if (i != 0) 
    { 
      bar (i) 
    } 
  else 
    { 
      T.1 = i * 3, T.2 = i + T.1, toons (T.2) 
    }, return i; 
} 
TB>  

The above output is a pretty-print of the body of the current function. A call to debug_tree would have printed more things about the structure of the Abstract Syntax Trees (AST), as follows:

TB> p 
 <expr_with_file_location 0x401a2aa0 
    type <void_type 0x401a089c void VOID 
        align 8 symtab 0 alias set -1 
        pointer_to_this <pointer_type 0x401a0910>> 
    side-effects public 
    arg 0 <bind_expr 0x401a28e0 type <void_type 0x401a089c void> 
        side-effects 
        vars <var_decl 0x401c3910 T.1 type <integer_type 0x40199414 int> 
            used SI file one.c line 10 
            size <integer_cst 0x401964e0 constant 32> 
            unit size <integer_cst 0x40196580 constant 4> 
            align 32 context <function_decl 0x401c34fc foo> chain <var_decl 0x401c3984 T.2>> 
        body <compound_expr 0x401a2960 type <void_type 0x401a089c void> 
            side-effects 
            arg 0 <expr_with_file_location 0x401a2ac0 type <void_type 0x401a089c void> 
                side-effects public 
                arg 0 <init_expr 0x401a2900 type <void_type 0x401a089c void> 
                    side-effects arg 0 <var_decl 0x401c36cc i> 
                    arg 1 <integer_cst 0x401a2780 constant 0>> 
                arg 1 <identifier_node 0x401c6480 one.c> 
                one.c:3:0> 
            arg 1 <compound_expr 0x401a29a0 type <void_type 0x401a089c void> 
                side-effects 
                arg 0 <expr_with_file_location 0x401a2b60 type <integer_type 0x40199414 int> 
                    side-effects public 
                    arg 0 <modify_expr 0x401a2b20 type <integer_type 0x40199414 int> 
                        side-effects arg 0 <var_decl 0x401c36cc i> 
                        arg 1 <plus_expr 0x401a2b00 type <integer_type 0x40199414 int> 
                            arg 0 <var_decl 0x401c36cc i> arg 1 <integer_cst 0x40196c20 1>>> arg 1 <identifier_node 0x401c6480 one.c> 
                    one.c:4:0> 
                arg 1 <compound_expr 0x401a2a40 type <void_type 0x401a089c void> 
                    side-effects 
                    arg 0 <expr_with_file_location 0x401a2980 type <integer_type 0x40199414 int> 
                        side-effects public 
                        arg 0 <call_expr 0x401a27c0 type <integer_type 0x40199414 int> 
                            side-effects arg 0 <addr_expr 0x401b35d0>> arg 1 <identifier_node 0x401c6480 one.c> 
                        one.c:5:0> 
                    arg 1 <compound_expr 0x401a2a80 type <void_type 0x401a089c void> 
                        side-effects 
                        arg 0 <expr_with_file_location 0x401a2a20 type <void_type 0x401a089c void> 
                            side-effects public arg 0 <cond_expr 0x401a2a00> arg 1 <identifier_node 0x401c6480 one.c> 
                            one.c:7:0> 
                        arg 1 <expr_with_file_location 0x401a2a60 type <void_type 0x401a089c void> 
                            side-effects public arg 0 <return_expr 0x401b3768> arg 1 <identifier_node 0x401c6480 one.c> 
                            one.c:12:0>>>>> 
        block <block 0x40198294 used vars <var_decl 0x401c36cc i> 
            supercontext <block 0x401982c0 used supercontext <function_decl 0x401c34fc foo> subblocks <block 0x40198294>>>> 
    arg 1 <identifier_node 0x401c6480 one.c> 
    one.c:3:0> 
TB>  

An interesting thing to remark in this dumping is that a node is represented as a well parenthesized expression. Each tree node contains several fields that are in general aligned at the same indentation level. For example BIND_EXPR has a child called vars (a set of variabl definitions), a body, and a block. All these fields are accessible from Tree Browser.

Thus if we continue our exploration of the current tree structure,

TB> arg0 
{ 
  int T.1; 
  int T.2; 
  int i; 
  extern  koo; 
  extern  bar; 
  extern  toons; 
 
  i = 0, i = i + 1, koo (), if (i != 0) 
    { 
      bar (i) 
    } 
  else 
    { 
      T.1 = i * 3, T.2 = i + T.1, toons (T.2) 
    }, return i; 
} 
TB> body 
i = 0, i = i + 1, koo (), if (i != 0) 
  { 
    bar (i) 
  } 
else 
  { 
    T.1 = i * 3, T.2 = i + T.1, toons (T.2) 
  }, return i; 
TB> 

Here I have to write some notes on the current chaining of expressions procedure. A compound_expr contains two operands: arg0 the child that contains the expression and arg1 the child that contains the rest of the list of expressions. In arg0 GCC stores an expr_with_file_location node that contains the expression and an information about the position of this expression in the original source code.

For accessing the next expression you can use the next command:

TB> nx 
i = i + 1, koo (), if (i != 0) 
  { 
    bar (i) 
  } 
else 
  { 
    T.1 = i * 3, T.2 = i + T.1, toons (T.2) 
  }, return i; 
TB> nx 
koo (), if (i != 0) 
  { 
    bar (i) 
  } 
else 
  { 
    T.1 = i * 3, T.2 = i + T.1, toons (T.2) 
  }, return i; 
TB> nx 
if (i != 0) 
  { 
    bar (i) 
  } 
else 
  { 
    T.1 = i * 3, T.2 = i + T.1, toons (T.2) 
  }, return i; 
TB>  

You can choose to go backwards via the history stack: this pops the last visited node from the stack.

TB> hpr 
koo (), if (i != 0) 
  { 
    bar (i) 
  } 
else 
  { 
    T.1 = i * 3, T.2 = i + T.1, toons (T.2) 
  }, return i; 
TB>  

Or via the previous expression command (this information is not yet included in GCC's trees, Tree Browser stores it in a hash table):

TB> pr 
i = i + 1, koo (), if (i != 0) 
  { 
    bar (i) 
  } 
else 
  { 
    T.1 = i * 3, T.2 = i + T.1, toons (T.2) 
  }, return i; 
TB>  

Extensions

Some extensions could be added in the near future, such as including commands that allows you to modify tree structure, apply optimizations locally on a given node.

A more interesting but much more long term project is to write an interactive program editor/optimizer, but for the moment this is only science fiction... :-)