A modern source-level debugger for the Clarion IDE — built as a managed addin that plugs into the IDE's existing debugger contract and supplies its own debug engine.
Clarion devs avoid the built-in debugger. The complaint is the experience, not the
engine: the standalone Cladb.exe debugger is a dated MDI app (Procedures / Stack Trace /
Watch / Disassembly child windows). Most devs gave up and print-debug instead
(Message(), Stop(), OutputDebugString + DebugView / DebugView++).
The opportunity is not a new debug engine — it's a modern front-end over a re-implemented engine, reusing the IDE's existing breakpoint/watch UI plumbing.
See docs/feasibility-investigation.md and
docs/TSWD-format.md.
Three things were established:
- The native engine is a sealed box.
Cladbrun.dllexports exactly one function (D32$StartDebugger); engine + GUI are welded together. You cannot drive it headlessly. → We must bring our own engine. - The IDE gives us the debug UX for free.
ICSharpCode.SharpDevelop.Debugging.IDebuggerDebuggerServiceprovide the breakpoint gutter, hover-eval tooltips, jump-to-line, and start/stop wiring. Register a customIDebuggerviaDebuggerDoozercodon and we becomeDebuggerService.CurrentDebugger.
- The Clarion debug symbol format (
TSWD) is fully decodable. Embedded in the EXE as a PE-debug-directory overlay. Both line-number tables decoded and validated at 100% — source-line→address (set breakpoints) and address→source-line (resolve hits). Mapped addresses confirmed to land on real instruction boundaries via disassembly.
Clarion has no 64-bit compiler (32-bit only, as of Clarion 11/12). So this is a
32-bit debugger. (Earlier notes about a "64-bit differentiator" are void — Clarion can't
produce 64-bit binaries.) The complement to this debugger for richer runtime tracing is
the existing DebugView++ fork (H:\DevLaptop\DebugViewPP).
| Layer | Source | Effort |
|---|---|---|
| Breakpoint gutter, watch tooltips, jump-to-line, start/stop | IDE — free via DebuggerService / IDebugger |
none |
| Modern watch / stack / queue / file-buffer panes | Us — reuse Modern Embeditor WebView2/Monaco stack | low |
IDebugger impl + codon registration |
Us — become CurrentDebugger |
low |
Win32 debug loop (DebugActiveProcess/WaitForDebugEvent, INT3 breakpoints, ReadProcessMemory, thread context) |
Us — well-trodden | medium |
| TSWD parser (TOC → module / line / symbol / type tables) | Us — the novel part | medium-hard |
| Clarion value formatter (render STRING/CSTRING/DECIMAL/GROUP/QUEUE/RECORD from memory) | Us — reuse dictionary/FileSchema type knowledge | low-medium |
| Phase | Deliverable | Status |
|---|---|---|
| 0 — Spike | TSWD line tables decoded & validated | ✅ done |
| 1a — Parser | ClarionDbg.Core (PE + TSWD parser) + ClarionDbg CLI (dump/resolve); C# at parity with the spike (5566/28651/100%). Build: VS2022 MSBuild, net48/x86. |
✅ done |
| 1b — Module map | Per-module range table (TOC +0x10) decoded + module-scoped resolver in ClarionDbg.Core (TryResolve, LineToRvasInModule). Verified round-trip (ABBROWSE line 152 ↔ RVA 0x3DA2F, exact; mid-statement correct); 99.9% .text coverage; garbage app-member modules gated out by the line-region check. |
✅ done |
| 1c — Engine | x86 debug engine working: CreateProcess(DEBUG_ONLY_THIS_PROCESS) + WaitForDebugEvent loop, INT3 plant/restore, GetThreadContext, ASLR-correct (loadBase+RVA), hit → TryResolve → module+line, honest exact/nearest reporting, clean teardown. Proven on clbrws.exe (break --entry --once). |
✅ done |
| 1d — Startup lines | Done — full round-trip proven: break --line 296 --module clbrws011 resolves line→RVA, plants INT3, stops at startup, resolves the hit back to clbrws011.clw line 296 (exact). Startup frame found empirically via --all-entries discovery (no need to decode the gated clbrws000–003 modules — they're global/dict init, off the startup path). |
✅ done |
| 1e — IDE glue | Built (pending deploy+test in live IDE). Non-invasive CA Debugger pad (Ctrl+Alt+G) in the ClarionAssistant addin: ClarionDebuggerService launches the engine with --json, streams hits, resolves the module→generated-source path via RedFileService.Active.Resolve, and calls DebuggerService.JumpToCurrentLine to light the editor's current-line bar. Coexists with the built-in debugger. Compiles clean (addin v5.0.315). |
deploy+test |
Generated
.clwsource IS on disk — under the.redredirect (e.g...\v8Source\clbrws011.clw); resolved via the addin's existingRedFileService. (Earlier "not persisted" assumption was wrong.) Engine discovery:ClarionDebuggerService.FindEngine()looks next to the addin, then falls back to the dev build path. For production, addClarionDbg.exe+ClarionDbg.Core.dlltodeploy.ps1.
- Decode the small-offset MEMBER modules (
clbrws.clw/001–003) + the line-region head[0x624,0x711); reconcile Table B. Not on the startup path, so not needed for the engine — revisit for 100% line coverage. - Symbol record table + Clarion type codes (Phase 3 watches).
| 2 — Stepping | Step over/into/out, continue, EBP-chain call-stack pane | ✅ done |
| 3 — Watches | Decode symbol table + Clarion type codes; render locals/globals/buffers/queues in WebView2; hover-eval | ✅ done |
| 4 — Polish | Conditional breakpoints / hit counts / tracepoints ✅, edit values ✅, run-to-cursor + break-on-entry ✅; watch expressions & set-next-statement (
SetInstructionPointer) pending | partial |
- Advanced breakpoints — conditions, hit-count rules, and tracepoints (log an
interpolated
{var}message and keep running), edited from the Breakpoints pane. - Run to cursor (right-click a line in the debugger Source panel) and break on procedure entry (right-click a Procedures-pane row), both reusing the breakpoint engine; run-to-cursor is a self-cleaning one-shot.
- Edit variable values — write a new value into the live process from the Variables tree / Watch.
- Procedures pane (filterable, jumps to the
.clwdefinition), Disassembly view with instruction-level stepping, per-pane filter & sort, and a DATE/TIME view-as forLONGvalues. - Rearrangeable two-column layout with a saved arrangement.
- Multi-DLL debugging foundation. The engine now tracks every image mapped into the target (EXE + DLLs) in a module table. All live-address math is rebased per-image, so breakpoints, hits, and stack frames resolve correctly across DLL boundaries — not just in the main EXE. Solution DLLs named by the host are pre-parsed so their breakpoints bind before launch; runtime-discovered DLLs register on load and drop cleanly on unload.
- Cleaner call stack. Frames are walked via the EBP chain (with an at-entry caller frame and monotonic-frame guards), replacing the old over-inclusive stack scan.
- Pause / break-into. A running target — stuck in a loop or idling in its message pump —
can now be paused on demand. The engine injects a break via
DebugBreakProcess, then reports the app thread actually in Clarion code (across any loaded module). A Pause button in the pad is enabled only while running. - Honour hardcoded breakpoints. An
int3the program executes itself —DebugBreak()/__debugbreak()(e.g. a ClarionDebugBreakunderIsDebuggerPresent()) — is now honoured as a "break here" instead of being swallowed. EIP is handled correctly (a hardcodedint3is the instruction, so it is not rewound). - Honest reporting in external code. When a thread is paused/broken outside the image's
.text(a system call, the OS, another module), the resolver reports external code instead of mislabelling it as the nearest Clarion record; the call stack still shows the real Clarion frames below. - Breakpoint-removal fix. Removing one of several gutter lines that snapped to the same
code line no longer leaves the shared
int3planted and firing. Each gutter line is now an independently-removable logical breakpoint, and the physicalint3is ref-counted so it is only unplanted when the last breakpoint referencing it is gone.
- Symbol record table: name offset →
{address / frame-offset, type code} - Clarion type-code enumeration (for value formatting)
Both appear to be the same tractable TSWD structure — see docs/TSWD-format.md.
C:\Clarion12\bin\Cladb.exe— working reference decoder of TSWDC:\Clarion12\bin\ClaDebugger.chm— debugger help / format notes- Test binary:
C:\Users\Public\Documents\SoftVelocity\Clarion11\Examples\HowToClarion\Browses\clbrws.exe(Full debug build)
- User Guide (source) — installing, opening the pad, setting breakpoints, watches, shortcuts, theming, and troubleshooting.
- Feasibility investigation — the spike that proved this was buildable.
- TSWD format notes — the Clarion debug-symbol format.
Requires Visual Studio 2022 / MSBuild with the .NET Framework 4.8 targeting pack. The engine and addin target .NET Framework 4.8 (x86).
.\deploy-addin.ps1 -Version 12 # build + deploy to a Clarion install (10 | 11 | 12 | all)Use -Kill to stop a running IDE first, or -NoBuild to deploy existing output.
- C# / .NET Framework 4.8 (x86) — the
ClarionDbgdebug engine (Core+ CLI) and the IDE addin. - Win32 Debugging API —
CreateProcess(DEBUG_ONLY_THIS_PROCESS),WaitForDebugEvent, INT3 breakpoints, thread context. - WebView2 (HTML / CSS / JavaScript) — the debugger pad front-end.
- PowerShell — build & deployment tooling.
- ClarionLive — github.com/ClarionLive
- Maintainer: @peterparker57
Contributions and issues are welcome via the GitHub repository.
Released under the MIT License — see LICENSE.