Skip to content

Latest commit

 

History

History
559 lines (419 loc) · 15.9 KB

File metadata and controls

559 lines (419 loc) · 15.9 KB

Using GDB to Debug specs

This guide explains how to use the GDB debugging toolkit for specs, which provides convenient "dump" commands for inspecting all major classes and data structures during debugging.

Table of Contents

  1. Building with Debug Symbols
  2. Loading the GDB Macros
  3. Quick Reference
  4. Detailed Examples
  5. Tips and Tricks
  6. Troubleshooting

Building with Debug Symbols

To debug specs effectively, you must build with debug symbols enabled.

On Linux and macOS

cd specs/src
python3 setup.py -v DEBUG
make clean all

The -v DEBUG flag tells the setup script to enable debug symbols and disable optimizations, making it easier to inspect variables and step through code.

On Windows

msbuild specs\specs.sln /p:Configuration=Debug /p:Platform=x64

However, gdb is not normally the debugger that you use on Windows.


Loading the GDB Macros

Automatic Loading (Recommended)

When you run GDB from the specs/src/ directory, the .gdbinit file is automatically loaded. So you can add the specs.gdb file to .gdbinit. Or you can specify it on the command line:

cd specs/src
gdb ../exe/specs -x gdb/specs.gdb

Manual Loading

If you're running GDB from a different directory, you can manually load the macros from within GDB:

(gdb) source gdb/specs.gdb

Verify Loading

After loading, you should see a welcome message:

specs GDB extension loaded successfully

========================================
specs GDB debugging macros loaded
========================================

Available commands:
dump_pstate <var>              - Dump ProcessingState
dump_sb <var>                  - Dump StringBuilder
dump_item <var>                - Dump Item (polymorphic)
dump_items <var>               - Dump itemGroup
dump_token <var>               - Dump Token
dump_alu_value <var>           - Dump ALUValue
dump_alu_counters <var>        - Dump ALUCounters
dump_alu_vec <var>             - Dump AluVec
dump_alu_function <var>        - Dump AluFunction
dump_external_func_rec <var>   - Dump ExternalFunctionRec
dump_python_func_collection <var> - Dump PythonFunctionCollection
dump_python_func_by_name <col> <name> - Dump PythonFuncRec by name
dump_python_func_rec <var>     - Dump PythonFuncRec
dump_python_func_arg <var>     - Dump PythonFuncArg
dump_exception <var>           - Dump SpecsException

Breakpoint helpers:
bp_apply               - Break on all 9 Item subclass apply methods
bp_getstr              - Break on InputPart::getStr
bp_compile             - Break on itemGroup::Compile
bp_parseAluExpression  - Break on parseAluExpression, where expressions are parsed
bp_pyfuncs             - Break on the imoprtant functions related to Python functions:
.                         - PythonFunctionCollection::Initialize, where the Python Function Collection is initialized
.                         - PythonFunctionCollection::GetFunctionByName where the function record is retrieved based on name
.                         - PythonFuncRec::setArgValue, where an argument for an external function is set
.                         - PythonFuncRec::Call, where an external function is invoked

For more help, type: help dump-processing-state

Quick Reference

ProcessingState Commands

Command Purpose
dump_pstate <var> Dump the entire ProcessingState (current record, cycle counter, separators, etc.)
dump_sb <var> Dump the StringBuilder (output string being built, current position)

Item Commands

Command Purpose
dump_item <var> Dump an Item (polymorphic; detects DataField, TokenItem, etc.)
dump_items <var> Dump an itemGroup (list of all compiled spec items)
dump_data_field <var> Dump a DataField (input source, output placement, conversion)
dump_token_item <var> Dump a TokenItem
dump_set_item <var> Dump a SetItem (assignment expression)
dump_condition_item <var> Dump a ConditionItem (IF/WHILE/etc.)
dump_split_item <var> Dump a SplitItem (SPLITW/SPLITF)

InputPart Commands

Command Purpose
dump_literal_part <var> Dump a LiteralPart (literal string)
dump_range_part <var> Dump a RangePart (character range)
dump_word_range_part <var> Dump a WordRangePart (word range with separator)
dump_field_range_part <var> Dump a FieldRangePart (field range with separator)
dump_clock_part <var> Dump a ClockPart (time value)
dump_id_part <var> Dump an IDPart (field identifier)
dump_expr_part <var> Dump an ExpressionPart (ALU expression)

Token Commands

Command Purpose
dump_token <var> Dump a Token (type, literal, original text)
dump_token_range <var> Dump a TokenFieldRange (range specification)

ALU Commands

Command Purpose
dump_alu_value <var> Dump an ALUValue (type, value, exactness)
dump_alu_counters <var> Dump ALUCounters (all counter variables)
dump_alu_unit <var> Dump an AluUnit (polymorphic; literal, counter, operator, etc.)
dump_alu_vec <var> Dump an AluVec (vector of AluUnits)
dump_alu_stats <var> Dump AluValueStats (statistical data)
dump_freq_map <var> Dump a frequencyMap (frequency distribution)

Utility Commands

Command Purpose
dump_reader <var> Dump a Reader (record count, EOF state)
dump_writer <var> Dump a Writer (output count)
dump_exception <var> Dump a SpecsException (file, line, message)

Python Interface Commands

Command Purpose
dump_alu_function <var> Dump an AluFunction (name, arg count, input dependency)
dump_external_func_rec <var> Dump an ExternalFunctionRec (polymorphic base class)
dump_external_func_collection <var> Dump an ExternalFunctionCollection (initialization state)
dump_python_func_collection <var> Dump a PythonFunctionCollection (internal Python function registry)
dump_python_func_rec <var> Dump a PythonFuncRec (Python function record with name and args)
dump_python_func_by_name <var> <fname> Dump a PythonFuncRec (Python function record with name and args) by collection and function name
dump_python_func_arg <var> Dump a PythonFuncArg (function argument with default value)

Breakpoint Helpers

Command Description
bp_apply Set breakpoints on all 9 ::apply methods of Item subclasses
bp_getstr Set breakpoint on InputPart::getStr
bp_compile Set breakpoint on itemGroup::Compile
bp_parseAluExpression Set breakpoint on parseAluExpression, where expressions are parsed
bp_pyfuncs Set breakpoints on all Python function-related methods. This includes PythonFunctionCollection::Initialize, where the Python Function Collection is initialized, PythonFunctionCollection::GetFunctionByName where the function record is retrieved based on name, PythonFuncRec::setArgValue, where an argument for an external function is set, and PythonFuncRec::Call, where an external function is invoked

Detailed Examples

Example 1: Inspecting ProcessingState During Execution

Suppose you're debugging a spec that processes records and you want to see the current state:

(gdb) bp_apply
Breakpoint 1 at 0x...

(gdb) run < input.txt
Starting program: ./specs ...
Breakpoint 2, DataField::apply (this=0x5fb8b0, pState=..., pSB=0x7fffffffcf50) at specitems/dataField.cc:444

(gdb) dump_pstate pState
ProcessingState @ 0x7fffffffd070
  Current Record:    "How am I doing?"
  Previous Record:   "well, hello there"
  Input Record:      "How am I doing?"
  Pad Char:          ' ' (0x20)
  Word Separator:    "" (local)
  Field Separator:   "  "
  Cycle Counter:     2
  Extra Reads:       0
  Record Count:      2
  Context Offset:    0
  Word Count:        -1
  Field Count:       -1
  Word Positions (3 cached):
    [0] 1-5
    [1] 7-11
    [2] 13-17
  Field Positions (0 cached):
    (none)
  Field Identifiers (0 entries):
    (none)
  FI Statistics (0 entries):
    (none)
  Break Values (1 entries):
    'a' = "well,"
  Break Level:       (none)
  Frequency Maps (0 entries):
    (none)
  Conditions (0 deep):
    (empty)
  Loops (0 deep):
    (empty)
  Input Station:     FIRST
  Input Stream:      1
  Stream Changed:    False
  Writers:           0x7fffffffcfe0
  Output Index:      1
  No Write:          False
  EOF:               False

This shows you exactly what the current record is, how many times we've processed records, and the current separators.

Example 2: Inspecting a DataField

When debugging a data field specification:

(gdb) dump_data_field pDataField
Item @ 0x5fb8b0
  Original Index: 1
  readsLines: False
  producesOutput: False
  forcesRunoutCycle: False
  isBreak: False
----- end of 'Item' dump
  Label: a
  Output Start: 10
  Max Length: 20
  Strip: True
  Conversion: UCASE
  Alignment: Left

This tells you that the field is labeled 'A', outputs starting at column 10, has a max length of 20 characters, strips whitespace, converts to uppercase, and is left-aligned.

Example 3: Walking an itemGroup

To see the entire compiled specification:

(gdb) dump_items pItemGroup
itemGroup @ 0x...
  bNeedRunoutCycle: true
  bFoundSelectSecond: false
  Item count: 5
  Items:
    [0] @ 0x...
    [1] @ 0x...
    [2] @ 0x...
    [3] @ 0x...
    [4] @ 0x...

Then you can inspect individual items:

(gdb) dump_item pItemGroup.m_items[0]
Item @ 0x5fb410
  Original Index: 1
  readsLines: False
  producesOutput: False
  forcesRunoutCycle: False
  isBreak: False
----- end of 'Item' dump

Example 4: Examining ALU Expressions

When debugging expression evaluation:

(gdb) dump_alu_value myALUValue
ALUValue @ 0x5fdc90
  Type:  Int
  Value: "42"
  Exact: True

(gdb) dump_alu_counters g_counters
ALUCounters @ 0x5e2c60 <g_counters>
  Counters (2 entries):
    #1: (int) 117 (exact)
    #2: (float) -0.019522002761880094

Example 5: Conditional Breakpoints with Cycle Counter

To break only on a specific record number:

(gdb) break DataField::apply if pState.m_CycleCounter == 100
Breakpoint 1 at 0x...

(gdb) run < input.txt
...
Breakpoint 1, DataField::apply (this=0x..., pState=0x..., pSB=0x...) at specitems/specItems.cc:...

(gdb) dump_pstate pState
ProcessingState @ 0x...
  Cycle Counter:     100
  ...

This is useful for debugging issues that only occur on specific records.

Example 6: Debugging Python Function Integration

When debugging Python function calls and integration, there is one macro that sets most of the important python-related breakpoints:

(gdb) bp_pyfuncs
Breakpoint 2 at 0x50e8ce: file utils/PythonIntf.cc, line 351.
Breakpoint 3 at 0x51118c: file utils/PythonIntf.cc, line 629.
Breakpoint 4 at 0x50ce71: file utils/PythonIntf.cc, line 112.
Breakpoint 5 at 0x50d3c4: file utils/PythonIntf.cc, line 170.

(gdb) run -f myspec < input.txt

Breakpoint 2, PythonFunctionCollection::Initialize (this=0x5e6da0 <gFunctionCollection>, _path=0x5e5fd0 <getFullSpecPath()::res+16> "/home/sio/specs") at utils/PythonIntf.cc:351

(gdb) dump_python_func_collection gFunctionCollection
PythonFunctionCollection @ 0x...
  Initialized: true
  Functions (3 entries):
    my_custom_function @ 0x... (2 args)
    another_func @ 0x... (exact, 1 args)
    third_func @ 0x... (0 args)

(gdb) dump_python_func_by_name gFunctionCollection my_custom_function
PythonFuncRec @ 0x...
  Name: my_custom_function
  Func Ptr: 0x...
  doc: Computes the custom value based on input
  Arg Type: no exactness information
  Tuple: 0x...
  Args (2 items):
    [0] input_value (default: Int)
         = 0
    [1] multiplier (default: Float)
         = 1.5

This shows you the complete function signature, documentation, and argument defaults. The Tuple field shows whether arguments have been prepared for the function call. The collection dump now lists all functions with their key properties (exactness and argument count).


Tips and Tricks

1. Using Pretty-Printers

The GDB extension includes pretty-printers for common types. When you print a variable, it's automatically formatted nicely:

(gdb) p myALUValue
$1 = ALUValue {type: Int, value: "42", exact: true}

(gdb) p myToken
$2 = Token {type: RANGE, literal: "", argc: 0}

2. Inspecting Shared Pointers

The extension can dereference std::shared_ptr automatically:

(gdb) p myDataField.m_InputPart
$3 = std::shared_ptr<InputPart> (use count=2, weak count=0) 0x...

(gdb) dump_input_part myDataField.m_InputPart
InputPart @ 0x...
  Debug: Range[1:10]
  readsLines: true
  forcesRunoutCycle: false

3. Setting Breakpoints on Virtual Methods

Since specs uses polymorphism extensively, you can break on virtual methods:

(gdb) break InputPart::getStr
Breakpoint 1 at 0x...

(gdb) run < input.txt
...
Breakpoint 1, LiteralPart::getStr (this=0x..., pState=...) at specitems/InputPart.cc:...

GDB will break on any derived class's implementation.

4. Examining the Specification Before Execution

You can set a breakpoint at the start of processing and dump the entire compiled spec:

(gdb) break itemGroup::process
Breakpoint 1 at 0x...

(gdb) run < input.txt
...
Breakpoint 1, itemGroup::process (this=0x..., sb=..., pState=..., rd=..., tmr=...) at specitems/specItems.cc:...

(gdb) dump_items this
itemGroup @ 0x...
  Item count: 5
  Items:
    [0] @ 0x...
    [1] @ 0x...
    ...

5. Tracking State Changes

Use conditional breakpoints to track when state changes:

(gdb) break ProcessingState::setString
Breakpoint 1 at 0x...

(gdb) commands
> dump_pstate this
> continue
> end

(gdb) run < input.txt
ProcessingState @ 0x...
  Current Record:    "line 1"
  Cycle Counter:     1
ProcessingState @ 0x...
  Current Record:    "line 2"
  Cycle Counter:     2
...

Troubleshooting

Python Extension Not Loading

Error: ImportError: No module named specs_gdb

Solution: Make sure you're running GDB from the specs/src/ directory, or manually source the .gdb file:

cd specs/src
gdb ./specs

Or:

gdb ./specs -x specs/src/gdb/specs.gdb

Dump Commands Not Found

Error: Undefined command: "dump_pstate"

Solution: The Python extension may not have loaded. Check that the .gdb file was sourced:

(gdb) source specs/src/gdb/specs.gdb

Unable to Read Variables

Error: Error: <unable to read string>

Solution: This usually means the inferior (the running program) is in a bad state or the variable is uninitialized. Try:

  1. Step to a different location in the code
  2. Check that the variable is actually in scope
  3. Use info locals to see available variables

Calling Methods Fails

Error: Error: <unable to call Debug()>

Solution: Some methods may not be callable if the inferior is corrupted or in an inconsistent state. This is normal. The dump commands will still show the raw member variables.

GDB Crashes When Calling Methods

Solution: If calling virtual methods causes GDB to crash, you can disable method invocation by editing specs_gdb.py and commenting out the call_method_safe calls.


Building and Debugging Tips

Debugging a Specific Spec

Create a test input file and a spec file, then run:

cd specs/src
gdb ./specs
(gdb) set args -f myspec.txt < input.txt
(gdb) break itemGroup::Compile
(gdb) run
(gdb) dump_items this

Debugging Parsing

To debug specification parsing:

(gdb) break itemGroup::Compile
(gdb) run -f myspec.txt < input.txt
(gdb) dump_items this

Debugging Expression Evaluation

To debug ALU expression evaluation:

(gdb) break AluFunction::evaluate
(gdb) run -f myspec.txt < input.txt
(gdb) dump_alu_unit this

See Also