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.
- Building with Debug Symbols
- Loading the GDB Macros
- Quick Reference
- Detailed Examples
- Tips and Tricks
- Troubleshooting
To debug specs effectively, you must build with debug symbols enabled.
cd specs/src
python3 setup.py -v DEBUG
make clean allThe -v DEBUG flag tells the setup script to enable debug symbols and disable optimizations, making it easier to inspect variables and step through code.
msbuild specs\specs.sln /p:Configuration=Debug /p:Platform=x64However, gdb is not normally the debugger that you use on Windows.
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.gdbIf you're running GDB from a different directory, you can manually load the macros from within GDB:
(gdb) source gdb/specs.gdb
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
| 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) |
| 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) |
| 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) |
| Command | Purpose |
|---|---|
dump_token <var> |
Dump a Token (type, literal, original text) |
dump_token_range <var> |
Dump a TokenFieldRange (range specification) |
| 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) |
| 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) |
| 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) |
| 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 |
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.
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.
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
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
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.
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).
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}
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
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.
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...
...
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
...
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 ./specsOr:
gdb ./specs -x specs/src/gdb/specs.gdbError: 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
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:
- Step to a different location in the code
- Check that the variable is actually in scope
- Use
info localsto see available variables
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.
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.
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 thisTo debug specification parsing:
(gdb) break itemGroup::Compile
(gdb) run -f myspec.txt < input.txt
(gdb) dump_items thisTo debug ALU expression evaluation:
(gdb) break AluFunction::evaluate
(gdb) run -f myspec.txt < input.txt
(gdb) dump_alu_unit this