Skip to content

Latest commit

 

History

History
554 lines (438 loc) · 29 KB

File metadata and controls

554 lines (438 loc) · 29 KB

OPDF Project Progress Tracker

Table of Contents

Overview

This document tracks the implementation progress of the Object Pascal Debug Format (OPDF) project, covering both the standalone debugger toolchain and the FPC compiler integration.

Repository: /data/devel/opdebugger (standalone debugger + opdf-lib)
FPC Branch: feature/opdf-support in /data/devel/fpc-3.3.1/src


Completed Work

Debugger Features 1–6, 14–17 (Completed)

  • ✓ Feature 1: locals command — lists all in-scope local variables with evaluated values

  • ✓ Feature 1: locals globals subcommand — also enumerates global variables

  • ✓ Feature 2: inspect command — structured multi-line layout for records, classes, and interfaces

  • ✓ Feature 3: print <arr>[N..M] — array slice display with out-of-bounds clamping

  • ✓ Feature 4: set <var> = <value> — in-process variable assignment (primitives and enums)

  • ✓ Feature 5: break …​ if count=N — hit-count conditional breakpoints with condition and info breakpoints commands

  • ✓ Feature 6: display / undisplay — auto-print variables on every breakpoint stop, with out-of-scope handling

  • ✓ Feature 15: Step over (next/n) and step into (step/s) — source-line-aware stepping

  • ✓ Feature 17: recPrimitive SubKind byte — compiler-emitted semantic classification (Integer, Boolean, Char, WideChar, Float, Currency) replaces name-matching heuristics in debugger

  • ✓ Integration tests 14–21, 25–27 covering all features; 27/27 automated tests passing

  • ✓ Sentinel breakpoint pattern applied to all tests (prevents program output mixing with debugger output on detach)

Standalone Debugger (Phases 0–2)

  • ✓ Project structure (PasBuild multi-module), hexagonal architecture

  • ✓ OPDF shared library (opdf_types.pas, opdf_io.pas, opdf_demangle.pas)

  • ✓ ELF64 section reader (elf_reader.pas) for embedded .opdf data

  • ✓ Primitive, boolean, float, enum, set type evaluators

  • ✓ ShortString, AnsiString, UnicodeString, WideString evaluators

  • ✓ Pointer evaluator

  • ✓ Static and dynamic array evaluators (with element limit and truncation)

  • ✓ Record evaluator with field access

  • ✓ Class evaluator with inheritance, nil-pointer handling, dot notation

  • ✓ Interface type loading, display, and evaluator (nil or type name with pointer address)

  • ✓ Field-backed property evaluation; method-backed properties report gracefully

  • ✓ Set type evaluator — decodes bitfield using base enum member names

  • ✓ Global variable support (linker-resolved addresses)

  • ✓ Local variable and parameter support (RBP-relative stack addressing)

  • ✓ Nested procedure scope variable access via saved RBP chain

  • ✓ Function scope records with low/high PC

  • ✓ Breakpoint management (INT3 injection/restoration + single-step)

  • file:line breakpoint syntax

  • run command — fork/exec with ptrace, pause at entry point

  • next — step to next source line (single-step loop)

  • callstack / backtrace — frame traversal

  • ✓ Command-line argument passing to debugged programs

  • verbose on|off command, --verbose/-v CLI flag

  • ✓ Linux ptrace adapter (x86_64 and i386 register structs both implemented)

  • ✓ Integration test suite: 13/13 automated tests passing (pre-Phase 3 baseline)

FPC Compiler Integration (Phases 1A–2)

  • dbg_opdf enum in systems.inc; -gO flag in options.pas

  • dbgopdf_typemap.pas — TTypeMapper (type ID allocation)

  • dbgopdf.pas — TOPDFDebugWriter emitting into al_opdf asm list

  • ✓ Registered in x86_64/cputarg.pas and i386/cputarg.pas

  • ✓ All type handlers: ordinal, float, string, enum, set, pointer, array, record, class, interface

  • ✓ All symbol handlers: global var, local var, parameter, property, function scope

  • ✓ Line information: per-procedure labels, accumulated in FLineInfoList, merged at inserttypeinfo

  • ✓ Embedded .opdf ELF section — linker resolves all symbol references

  • ✓ Cross-module type dedup via global state (G_TypeMapper, G_EmittedTypeIDs)

  • ✓ Unit directory record (type 19) — indexes per-unit data sizes

  • ✓ Method scopes emitted independently of parent class dedup state

  • ✓ SubKind byte in recPrimitive — derived from def.ordtype / tfloatdef in appenddef_ord / appenddef_float

  • ✓ Verified: 104-unit project loads 24K types, 1K scopes, 70K line mappings

Design Decisions (Resolved)

  • External file vs embedded section — Embedded ELF section chosen. External .opdf files cannot carry line information because code addresses are only known after linking. All address fields use symbol references resolved by the assembler/linker. See docs/design-decisions.adoc for full rationale.

  • ogopdf.pas → opdf_types.pas — The og prefix is FPC’s Output Generator convention (ogelf.pas, ogcoff.pas) and is inappropriate for the standalone opdf-lib. All units in opdf-lib follow the opdf_* naming convention.

  • tpdf_type_mapper.pas → dbgopdf_typemap.pas — FPC debug subsystem units use the dbg prefix (dbgbase.pas, dbgdwarf.pas). The type mapper is part of that subsystem.

  • FPC compiler isolationdbgopdf.pas imports only FPC-internal units plus dbgopdf_typemap. No opdf-lib units are imported. REC_* constants are duplicated locally with { must match opdf_types.pas } comments.


Known Limitations

  • Arrays indexed by an enum type (e.g. array[TDays] of String) display bounds as [0..6] rather than [Mon..Sun] — enum-keyed array bounds not yet decoded

  • Nested procedure static link not modelled in OPDF — the saved RBP chain approximation works for one level of nesting only

  • set of non-enum ordinal types (e.g. set of 3..7) show raw bit positions, not names

  • Method-backed properties with float return types (Single, Double) are not evaluated — requires PTRACE_GETFPREGS for XMM0 reading (Feature 8 limitation)

  • Method-backed properties in imported units compiled without -gO cannot be evaluated — getter function scope is not in OPDF; ELF symbol fallback is a future enhancement

  • GetPointerSize in pdr_opdf_adapter.pas is conditioned on the debugger’s compile-time architecture, not the debuggee’s — will be wrong when a 64-bit debugger debugs a 32-bit binary; fixed by the PointerSize prerequisite below

  • Class field access inside methods now resolves bare field names via implicit Self (Feature 16); method-backed properties still require explicit Self.PropertyName dot notation

  • elf_reader.pas only supports ELF64; ELF32 (Linux i386) requires a format extension

Stack Frame Resolution

The current callstack walker and variable access rely on frame pointer chains (RBP on x86_64, EBP on i386). This works for FPC-compiled code because FPC generates frame pointers by default, but breaks in several scenarios:

  • Prebuilt RTL without frame pointers: The FPC RTL is typically distributed compiled with -O2, which may omit frame pointers. When an exception is raised inside an RTL function that has no stack frame, the RBP chain skips the calling user function entirely. The debugger sees the caller’s caller instead. This is a known issue with frame-pointer-based stack walking in Lazarus/fpdebug as well.

  • Function prologue/epilogue: At the first instruction of a function (before push rbp; mov rbp, rsp) or after the epilogue restores RBP, the frame is not established. Reading RBP at that point gives the wrong frame.

  • Non-x86 architectures: ARM32 does not guarantee frame pointers. PowerPC uses the stack pointer as frame pointer with a different walking mechanism.

  • Mixed-language callstacks: C library code or system calls compiled without frame pointers break the chain.

DWARF addresses this with .debug_frame / .eh_frame unwind tables that describe frame resolution rules at every instruction boundary. OPDF does not have an equivalent yet. A future recUnwindInfo record type could map address ranges to frame resolution rules. Alternatively, the debugger could fall back to reading .eh_frame sections (present even in release builds) when no OPDF data is available for a given frame.

Priority: Low — FPC debug builds preserve frame pointers on x86/x86_64. This becomes important when tackling optimized code, non-x86 architectures, or mixed-language stacks.


Priority: OPDF Scalability (Completed)

Multi-unit testing with a 104-unit project (maximus) revealed scalability concerns: 3.4x type duplication (25,652 records for 7,540 unique types) and O(n) linear scans.

Solution: compiler-side cross-module type dedup + reader-side multi-header support. FPC creates a new TOPDFDebugWriter instance per compilation unit, so dedup state (G_TypeMapper, G_EmittedTypeIDs) is stored in global variables that persist across modules. Each unit still gets its own OPDF header (required by linker section concatenation), but types are only emitted once across all units. A UnitDirectory record (type 19) is emitted at the end of the main program module to index all units.

Results: 104 units, 1.1x dedup ratio (down from 3.4x), 6.4 MB section size.

Diagnostics

  • ✓ opdf_dump diagnostic tool (Pascal, raw stream parsing, multi-header detection)

  • ✓ OPDF spec updated to v0.3.0 with multi-unit design and format comparison

  • ✓ Progress tracker updated with scalability work items

Compiler: Cross-Module Type Dedup (Completed)

  • ✓ Global G_TypeMapper — consistent TypeIDs across compilation units

  • ✓ Global G_EmittedTypeIDs — skip duplicate type emission across modules

  • TypeAlreadyEmitted guard in all 14 appenddef_* methods

  • ✓ Per-unit byte counting via G_ByteCounter for unit directory

  • recUnitDirectory (type 19) emitted at end of main program module

  • OPDF_FLAG_HAS_DIRECTORY header flag

  • ✓ Method scopes emitted independently of parent class dedup state

Reader: Multi-Header + Deduplication (Completed)

  • TryReadNextHeader in TOPDFReader — scans past linker alignment padding bytes

  • ✓ Multi-header loop in TOPDFReaderAdapter.Load — processes all linker-concatenated units

  • ✓ Type deduplication — all type record handlers check FTypes.Find(TypeID) before allocation

  • recUnitDirectory handling in adapter (skip) and opdf_dump (decode)

  • ✓ Recursion depth limit in EvaluateVariableInfo — prevents segfault on deep class hierarchies

  • ✓ Stack variable address computation in ResolveFieldAccess — fixes inspect for locals

  • ❏ Sorted arrays for line info and function scopes — O(log n) binary search

  • ❏ Filename string interning — avoid duplicate allocations for repeated source paths

Verification (Completed)

  • ✓ FPC compiler rebuilt with cross-module dedup changes

  • ✓ Recompile maximus with new compiler — 104 units, 24K types, 1K scopes, 70K lines

  • ✓ Verify with opdf_dump: 104 headers parsed, 1.1x dedup ratio, UnitDirectory present

  • ✓ Run all 18 integration tests — all passing

  • ✓ Test pdr debugger against maximus: breakpoints, locals, inspect all working


Feature Work Items

Full specification: docs/feature-specs.adoc

Prerequisite: OPDF Format Fix — DONE

  • ✓ Add PointerSize: Byte to TOPDFHeader in opdf_types.pas

  • ✓ Emit SizeOf(Pointer) into that field in dbgopdf.pas

  • opdf_io.pas reader — N/A, binary format uses QWord for all address fields

  • ✓ Update pdr_opdf_adapter.pas to derive pointer size from the loaded header

  • ✓ All integration tests passing (no binary format change — field was already emitted)

Tier 1: High Value, Lower Complexity

Feature 1: locals Command — DONE

  • ✓ Add GetScopeLocals(RIP) to IDebugInfoReader / pdr_opdf_adapter.pas

  • ✓ Add GetLocalVariables to pdr_engine.pas

  • ✓ Register locals and locals globals commands in pdr_main.pas

  • ✓ Integration test: test_14_locals.pas

Feature 2: inspect Command — DONE

  • ✓ Add Methods: array of String to TDebuggerClass in pdr_ports.pas

  • ✓ Add GetInspectLines(Expr) to pdr_engine.pas with per-category structured output

  • ✓ Register inspect/ins command in pdr_main.pas

  • ✓ Integration test: test_15_inspect.pas

Feature 3: Array Slice Display — DONE

  • ✓ Add TryParseSlice parser in pdr_main.pas

  • ✓ Add EvaluateArraySlice(VarName, LowIndex, HighIndex) to pdr_engine.pas

  • ✓ Clamp out-of-bounds indices with [WARN] message

  • ✓ Register print MyArray[N..M] syntax in pdr_main.pas

  • ✓ Integration test: test_16_array_slice.pas

Feature 4: Variable Assignment (set) — DONE

  • ✓ Add SetVariable(VarName, Value) to pdr_engine.pas

  • ✓ Value conversion: integer literals, True/False, enum member names, `$`hex / `0x`hex

  • ✓ Write via FProcessController.WriteMemory (PTRACE_POKEDATA on Linux)

  • ✓ Register set <var> = <value> command in pdr_main.pas

  • ✓ Integration test: test_17_set_variable.pas

Feature 4b: Array Element Assignment
  • ❏ CLI parsing — detect VarName[Index] syntax in pdr_main.pas; split into base name and integer index

  • ❏ Engine logic — look up base array variable, verify tcArray, compute element address as base + (index - LowBound) * ElementSize

  • ❏ Add tcFloat write support to SetVariable (currently only tcPrimitive and tcEnum are handled) — parse value string via Val, write raw bytes

  • ❏ Support element types: tcPrimitive, tcEnum, tcFloat

  • ❏ Integration test: test_17b_set_array_element.pas

Note
Consider whether array assignment should remain strictly per-element (SomeArray[3] = value), or whether a range form is useful — e.g. SomeArray[1..3] = (1, 2, 3) for assigning distinct values, or SomeArray[1..3] = (all 2) for flood-filling a range to a single value. The latter would be handy for initialising or resetting an array during a debug session without stepping through setup code. No decision taken yet — the per-element form is sufficient for an initial implementation.

Feature 5: Conditional Breakpoints (hit-count form) — DONE

  • ✓ Extend TBreakpointEntry record with ConditionType, HitCount, CurrentHitCount

  • ✓ Evaluate hit-count condition in engine Continue loop; re-arm and continue if not met

  • ✓ Parse break foo.pas:N if count=K syntax

  • condition <num> count=N command to set/change/remove conditions on existing breakpoints

  • info breakpoints command to list all breakpoints with conditions and hit counts

  • ✓ Integration test: test_20_conditional_break.pas

Feature 6: display Command — DONE

  • ✓ Add FDisplayList to pdr_engine.pas with AddDisplay, RemoveDisplay, ClearDisplay, EvaluateDisplayList

  • ✓ Print all display entries after every execution stop (continue, next, step)

  • ✓ Register display <var>, undisplay <var>, undisplay, info display

  • ✓ Handle out-of-scope variables gracefully — prints (out of scope)

  • ✓ Integration test: test_21_display.pas

Feature 7: Hardware Watchpoints

  • ✓ Add TWatchpointType, SetWatchpoint, ClearWatchpoint, GetFiredWatchpoint to IProcessController (pdr_ports.pas)

  • ✓ Implement via PTRACE_PEEKUSER/PTRACE_POKEUSER on x86_64 DR0–DR3 / DR6 / DR7 in pdr_linux_ptrace.pas

  • ✓ Disambiguate watchpoint vs breakpoint SIGTRAP via DR6 status register

  • ✓ Add SetWatch, RemoveWatch, GetWatchpointList to pdr_engine.pas

  • ✓ Display old/new values on watchpoint hit

  • ✓ Register watch, rwatch, awatch, unwatch, info watchpoints commands

  • ✓ Enforce 4-slot hardware limit with clear error message

  • ✓ Integration test: test_22_watchpoint.pas — 22/22 tests passing

Tier 2: High Value, Higher Complexity

Feature 8: Method Call Injection (Getter Property Evaluation)

  • ✓ Add InjectCall(MethodAddr, SelfPtr, ManagedReturn, RetValue) to IProcessController

  • ✓ Add FindFunctionByName(Name, FuncInfo) to IDebugInfoReader

  • ✓ Implement FindFunctionByName in pdr_opdf_adapter.pas — linear scan of FFunctionScopes

  • ✓ Implement InjectCall in pdr_linux_ptrace.pas — full x86_64 call injection:

    • Save/restore all registers via PTRACE_GETREGS/PTRACE_SETREGS

    • INT3 sentinel at saved RIP for return detection

    • Red zone skip (128 bytes), 16-byte RSP alignment

    • Hidden result buffer in RSI for managed return types (AnsiString)

    • User breakpoint passthrough during injection (single-step past, re-insert)

    • Iteration limit (1000) for timeout protection

  • ✓ Evaluate method-backed properties via InjectCall in ResolveClassProperty (pdr_typesys.pas) — explicit print Obj.PropName triggers call injection

  • FormatInjectedReturnValue helper — formats ordinal (RAX) and AnsiString (hidden ptr) returns

  • ✓ Safe default: print Obj and inspect show <getter: MethodName> without calling — avoids side effects; matches GDB/LLDB behaviour

  • inspect shows [use 'print Obj.Prop' to evaluate] hint for method properties

  • ✓ Updated test_05_properties expected output for getter annotations

  • ✓ New integration test: test_24_method_properties.pas — AnsiString + Integer getters

  • ✓ Updated output filter in run_tests.sh to support dot-notation variable names

  • ✓ 24/24 integration tests passing

Feature 9: Expression Evaluator

  • ❏ Write pdr_expr_preproc.pas — tokeniser resolving dot notation and [N] indexing

  • ❏ Integrate fpexprpars (FPC unit packages/fcl-base/src/fpexprpars.pp) as eval core

  • ❏ Register variable resolver callback using TTypeSystem.EvaluateVariable

  • ❏ Workaround for missing div/shl/shr in fpexprpars (rewrite in pre-processor)

  • ❏ Open upstream FPC issue and submit patch for div/shl/shr in fpexprpars

  • ❏ Enable print <expr> for compound expressions

  • ❏ Integration test: test_22_expression.pas

Feature 5 (form 2): Conditional Breakpoints — expression form

  • ❏ Parse break foo.pas:N if <expr> using Feature 9 evaluator

  • ❏ Evaluate expression condition in HandleBreakpointHit

Tier 3: Language Completeness

Feature 10: Variant Records

  • ❏ Extend REC_RECORD in opdf_types.pas with variant group descriptors

  • ❏ Update dbgopdf.pas appenddef_record to emit variant fields

  • ❏ Add TVariantRecordEvaluator to pdr_typesys.pas

  • ❏ Integration test: test_23_variant_record.pas

Feature 10b: Variant Type Support

  • ❏ Define recVariant record type in opdf_types.pas

  • ❏ Emit recVariant in dbgopdf.pas for tvariantdef

  • ❏ Add TOPDFReader.ReadVariant in opdf_io.pas

  • ❏ Parse and cache variant types in pdr_opdf_adapter.pas

  • ❏ Add TVariantEvaluator to pdr_typesys.pas (decode VarType discriminant + value)

  • ❏ Integration test: test_variant_types.pas

Feature 11: Generics (Basic Containers)

  • ❏ Hardcode TList<T>, TObjectList<T>, TDictionary<K,V> layout knowledge

  • ❏ Recognise container type names by prefix in pdr_typesys.pas

  • ❏ Integration test: test_24_generics.pas

Feature 12: Exception Metadata

  • ✓ Add ELF .symtab symbol lookup (FindSymbolAddress in elf_reader.pas)

  • ✓ Set internal breakpoint on fpc_raiseexception at program launch

  • ✓ Read exception class name from VMT (vmtClassName at offset +24)

  • ✓ Read FMessage AnsiString (offset +16 with _MonitorData, fallback +8)

  • ✓ Display: Exception: ClassName — 'message' with source location

  • ✓ Add catch / nocatch CLI commands to enable/disable break on raise

  • ✓ Disambiguate internal breakpoint from user breakpoints in Continue

  • ✓ Integration test: test_23_exception.pas — 23/23 tests passing

Feature 14: Compile-Time Constants

  • ✓ Add recConstant = 20 record type to OPDF format (opdf_types.pas)

  • ✓ Add TConstantKind enum: ckOrd, ckString, ckReal, ckNil, ckWideStr

  • ✓ Add TDefConstant packed record with embedded value bytes

  • ✓ Add WriteConstant / ReadConstant to opdf_io.pas

  • ✓ Implement appendsym_const in dbgopdf.pas — emits constants for constord, conststring, constreal, constnil, constwstring

  • ✓ Add TConstantInfo record and FindConstant method to IDebugInfoReader (pdr_ports.pas)

  • ✓ Add FConstants hash list with loading and lookup in pdr_opdf_adapter.pas

  • ✓ Add constant fallback in TTypeSystem.EvaluateVariable with Boolean/Char display refinement

  • ✓ Integration test: test_25_constants.pas

  • ❏ Extend appendsym_const for remaining constant kinds: constset, constguid, constpointer, constresourcestring, constwresourcestring

  • ❏ Add enum constant display (resolve member name from TypeID)

Feature 15: Step Over and Step Into — DONE

  • ✓ Fix StepLine (step over) — uses function scope address range (FindFunctionByAddress) instead of 100-line range heuristic

  • ✓ Implement StepInto — loops PTRACE_SINGLESTEP until source line changes (FindLineByAddress)

  • ✓ Wire step/s to StepInto and next/n to StepLine in CLI

  • ✓ Gate ptrace adapter [INFO] Step complete on gVerbose to prevent noise during StepInto loops

  • ✓ Fix Continue; name collision — FPC resolves bare Continue; inside TDebuggerEngine.StepLine to self.Continue (the method) instead of the loop control keyword; restructured loop to use if-else chains

  • ✓ Integration test: test_26_step_over.pas — step over a procedure call stays in caller

  • ✓ Integration test: test_27_step_into.pas — step into a function call descends into callee

Feature 16: Implicit Self Field Resolution

  • ✓ Emit Self as recLocalVar in dbgopdf.pas — relax the vo_is_hidden_para skip for method Self parameters only

  • ✓ Add Self-field resolution tier in TTypeSystem.EvaluateVariable (pdr_typesys.pas) — when a bare name is not found in locals/globals/constants, look up Self in scope, resolve its class type, and search fields (with inheritance chain walk)

  • ✓ Add Self-field fallback in TDebuggerEngine.GetInspectLines (pdr_engine.pas) — inspect FFontManager inside a method resolves via Self and renders the full structured view

  • ❏ Integration test: print FFontManager inside a method resolves the class field

  • ❏ Verify: local variables shadow class fields of the same name

Feature 17: Primitive SubKind Byte — DONE

  • ✓ Add TOPDFPrimitiveSubKind enum and SubKind: Byte field to TDefPrimitive (opdf_types.pas)

  • ✓ Update WritePrimitive to accept and write SubKind (opdf_io.pas)

  • ✓ Emit SubKind from FPC compiler — derive from def.ordtype in appenddef_ord, emit SUBKIND_FLOAT in appenddef_float (dbgopdf.pas)

  • ✓ Read SubKind in adapter, use for tcFloat vs tcPrimitive categorisation (pdr_opdf_adapter.pas)

  • ✓ Add SubKind: Byte to TTypeInfo (pdr_ports.pas)

  • ✓ Replace all 5 name-matching heuristics with SubKind-based checks (pdr_typesys.pas)

  • ✓ Display SubKind in opdf_dump output (opdf_dump.pas)

  • ✓ Update OPDF specification with SubKind byte and value table (opdf-specification.adoc)

  • ✓ All 27 integration tests pass unchanged

Feature 13: Optimized Code — Register-Allocated Variables

  • ❏ Extend LocationExpr encoding: value 3 = register-allocated

  • ❏ Update dbgopdf.pas appendsym_localvar to emit LocationExpr = 3 for LOC_REGISTER

  • ❏ Update EvaluateVariableInfo to read from live register state for LocationExpr = 3

  • ❏ Increment TOPDFHeader.Version; update opdf-specification.adoc

Upstream Contribution (Independent)

  • ❏ Submit patch to FPC: add div, shl, shr operators to fpexprpars.pp


Platform Expansion

  • ❏ Extend elf_reader.pas for ELF32 (Linux i386 support)

  • ❏ Write pe_reader.pas — PE/COFF section extraction (Windows binaries)

  • ❏ Write pdr_windows_debug.pasIProcessController via Windows debug API

  • ❏ Wire format dispatch in pdr_opdf_adapter.pas (ELF vs PE detection)

  • ❏ Windows i386: i386 CONTEXT struct variant in pdr_windows_debug.pas

  • ❏ macOS: Mach-O section reader + Mach port process controller (deferred)


Architecture Summary

Standalone Debugger

opdf-lib/      → core OPDF format, I/O, ELF reader, demangling
pdr-core/      → engine, type system, evaluators, port interfaces
pdr-adapters/  → Linux ptrace, OPDF reader adapter, arch helpers
pdr-cli/       → command-line interface (REPL)
tools/         → opdf_dump (Pascal diagnostic tool for .opdf section analysis)

FPC Integration

dbgopdf.pas          → OPDF debug writer (emits into al_opdf asm list)
dbgopdf_typemap.pas  → Type ID allocation (TTypeMapper)
aasmdata.pas         → al_opdf enum in TAsmListType
systems.inc          → dbg_opdf enum entry
options.pas          → -gO flag handling
x86_64/cputarg.pas   → dbgopdf registration
i386/cputarg.pas     → dbgopdf registration

Integration Tests (27 automated)

test_01_primitives         → Integer and Boolean variables
test_01_loop               → Loop and breakpoint testing
test_02_breakpoint_next    → Break and next commands
test_03_strings            → All string types
test_04_classes            → Class instances and fields
test_05_properties         → Properties with getters/setters
test_06_local_variables    → Stack-based locals
test_07_variable_shadowing → Scope resolution
test_08_static_arrays      → Static array bounds
test_09_dynamic_arrays     → Dynamic array length/elements
test_10_arguments          → Function arguments
test_11_callstack          → Stack traces
test_12_teaser_demo        → Nested scope (OuterProcedure / InnerNested)
test_13_sets               → Set type display ([Mon, Tue, Wed, Thu, Fri])
test_14_locals             → locals command: in-scope variable listing
test_15_inspect            → inspect command: record field layout
test_16_array_slice        → print MyArray[N..M] slice display with clamping
test_17_set_variable       → set command: modify Counter and MyFlag in-process
test_18_inspect_class_intf → inspect command: class + interface with methods
test_19_nested_scope       → nested procedure scoping: DeclIndex-based visibility
test_20_conditional_break  → conditional breakpoint: hit-count form (count=7 in loop)
test_21_display            → display command: auto-print Iter and Sum across continues
test_22_watchpoint         → hardware watchpoints: DR0-DR3 write/read-write detection
test_23_exception          → break on exception raise with class name and message
test_24_method_properties  → method-backed property evaluation via call injection
test_25_constants          → compile-time constant display (Integer, Boolean, Char, Real, String, UTF-8)
test_26_step_over          → step over: next skips called procedure, stays in caller
test_27_step_into          → step into: step descends into called function body

Quick Reference

Build Commands

# Build all modules (standalone debugger)
cd /data/devel/opdebugger
pasbuild compile

# Build FPC compiler with OPDF support
cd /data/devel/fpc-3.3.1/src
make -C compiler cycle

Testing

# Compile test with FPC OPDF support (use ppcx64 directly, not fpc driver)
/data/devel/fpc-3.3.1/x86_64-linux/lib/fpc/3.3.1/ppcx64 \
  @/data/devel/fpc-3.3.1/x86_64-linux/lib/fpc/3.3.1/fpc.cfg \
  -gO test.pas

# Run debugger on a test binary
../../pdr-cli/target/pdr test_01_primitives

# Run full automated test suite
bash run_tests.sh

# Run single test
bash run_tests.sh test_01_primitives

Debugger Commands (current)

run                          Start program (pauses at entry)
break file.pas:22            Set breakpoint by file:line
break 0x401000               Set breakpoint by hex address
break MyVar                  Set breakpoint by variable name
break <loc> if count=N       Conditional breakpoint (fire on Nth hit)
condition <num> count=N      Set/change hit-count condition
condition <num>              Remove condition (make unconditional)
info breakpoints             List all breakpoints with conditions
display <expr>               Auto-print expression on every stop
undisplay <expr>             Remove from auto-display list
undisplay                    Remove all display entries
info display                 List registered display expressions
continue / c                 Resume execution
next / n                     Step over: next source line, skipping into calls
step / s                     Step into: next source line, descending into calls
print <var>                  Print variable value
print <var>.<field>          Dot notation for class/record fields
print <arr>[N..M]            Array slice — elements N to M inclusive
locals                       List all in-scope variables with values
locals globals               Include global variables
inspect <var>                Structured view: fields, properties, methods
set <var> = <value>          Assign value to a primitive or enum variable
callstack / cs               Show call stack
delete <num>                 Remove breakpoint
verbose on|off               Toggle verbose/debug output
quit                         Exit debugger

Planned Commands

call <method>(<args>)        Invoke a method in the debugged process
break foo.pas:N if <expr>    Conditional breakpoint (expression — requires Feature 9)