Making debugging with GDB a bit easier


2015/03/14 by Tomas Vondra

I spend more and more time debugging PostgreSQL internals - analyzing bugs in my patches, working on new patches etc. That requires looking at structures used by the internals - optimizer, planner, executor etc. And those structures are often quite complex, nested in various ways so exploring the structure with a simple print gets very tedious very quickly:

(gdb) print plan
$16 = (PlannedStmt *) 0x2ab7dc0
(gdb) print plan->planTree
$17 = (struct Plan *) 0x2ab6590
(gdb) print plan->planTree->lefttree
$18 = (struct Plan *) 0x2ab5cf0
(gdb) print plan->planTree->lefttree->lefttree
$19 = (struct Plan *) 0x2ab5528
(gdb) print plan->planTree->lefttree->lefttree->lefttree
$20 = (struct Plan *) 0x2ab1290
(gdb) print *plan->planTree->lefttree->lefttree->lefttree
$21 = {type = T_SeqScan, startup_cost = 0, total_cost = 35.5, plan_rows = 2550,
        plan_width = 4, targetlist = 0x2ab4e48, qual = 0x0, lefttree = 0x0,
        righttree = 0x0, initPlan = 0x0, extParam = 0x0, allParam = 0x0}
(gdb) print *(SeqScan*)plan->planTree->lefttree->lefttree->lefttree
$22 = {plan = {type = T_SeqScan, startup_cost = 0, total_cost = 35.5,
        plan_rows = 2550, plan_width = 4, targetlist = 0x2ab4e48, qual = 0x0,
        lefttree = 0x0, righttree = 0x0, initPlan = 0x0, extParam = 0x0,
        allParam = 0x0}, scanrelid = 1}

And then you move somewhere else and have to start from the scratch :-(

Fortunately, gdb provides a Python API so that it's possible to extend it quite easily - define new commands, change the way values are printed etc.


I've hacked a small script that defines a new command pgprint that makes my life a bit easier. It's not a perfect nor the most beautiful code in the world, but you can do this for example:

(gdb) pgprint plan
          type: CMD_SELECT
      query ID: 0
    param exec: 0
     returning: False
 modifying CTE: False
   can set tag: True
     transient: False
  row security: False

     plan tree: 
        -> HashJoin (cost=202.125...342.812 rows=2550 width=16)
                target list:
                        TargetEntry (resno=1 resname="id" origtbl=16405 origcol=1 ...
                        TargetEntry (resno=2 resname="id" origtbl=16410 origcol=1 ...
                        TargetEntry (resno=3 resname="id" origtbl=16420 origcol=1 ...
                        TargetEntry (resno=4 resname="id" origtbl=16430 origcol=1 ...

                -> HashJoin (cost=134.750...240.375 rows=2550 width=12)
                        target list:
                                TargetEntry (resno=1 resname=(NULL) origtbl=0 ori ...
                                TargetEntry (resno=2 resname=(NULL) origtbl=0 ori ...
                                TargetEntry (resno=3 resname=(NULL) origtbl=0 ori ...

                        -> HashJoin (cost=67.375...137.938 rows=2550 width=8)
                                target list:
                                        TargetEntry (resno=1 resname=(NULL) origt ...
                                        TargetEntry (resno=2 resname=(NULL) origt ...
                ...
   range table:
        RangeTblEntry (kind=RTE_RELATION relid=16405 relkind=r)
        RangeTblEntry (kind=RTE_RELATION relid=16410 relkind=r)
        RangeTblEntry (kind=RTE_RELATION relid=16420 relkind=r)
        RangeTblEntry (kind=RTE_RELATION relid=16430 relkind=r)
 relation OIDs: [16405, 16410, 16420, 16430]
   result rels: (NIL)
  utility stmt: (NULL)
      subplans: (NIL)

It's available at github so just download it somewhere on disk, load it into gdb like this:

(gdb) source /home/tomas/work/gdbpg/gdbpg.py

and start using pgprint command. Or define your own commands - it's really simple.

Is that the best thing since sliced bread? Hardly, but hopefully it will make your life a bit easier.

The gdb Python API is way more powerful, so the are certainly ways to improve this simple script (aside from adding support for more Node types). Feel free to send me a patch, pull request or just an idea for improvement.





comments powered by Disqus