Integrated Debugger
HarbourBuilder includes a fully integrated debugger that runs in-process via the
.hrb bytecode format. No external debugger process, no inter-process communication
overhead — the debugger is part of the Harbour VM itself, providing a seamless debugging
experience with full control over execution.
Unlike traditional debuggers that attach to a running process, HarbourBuilder's debugger operates within the same Harbour VM. This means zero latency for variable inspection, instant breakpoint hits, and accurate call stack information at all times.
Architecture
Compilation for Debugging
To generate debuggable bytecode, compile with these flags:
| Flag | Purpose |
|---|---|
-gh | Generate debug symbols (line numbers, variable names, scope info) |
-b | Output .hrb bytecode file instead of C source |
harbour myapp.prg -gh -b
The .hrb file contains all the information the debugger needs to map bytecode
instructions back to source code lines, display variable names, and reconstruct the call stack.
The Debug Hook: hb_dbg_SetEntry
At the core of Harbour's debugging support is the hb_dbg_SetEntry() hook function.
This callback is invoked by the Harbour VM on every bytecode instruction when debug mode is active,
allowing the IDE to:
- Track the current source line being executed
- Inspect and modify local and static variables
- Evaluate expressions in the current scope
- Implement step-over, step-into, and step-out operations
- Check for breakpoint hits
When Harbour executes a .hrb file compiled with -gh, each instruction triggers
a call to hb_dbg_SetEntry. The IDE registers its own callback via
hb_dbg_SetEntry(), receiving the current file, line number, function name,
and variable context on every step.
Debug Toolbar
The debug toolbar provides five essential execution controls:
| Icon | Shortcut | Action | Description |
|---|---|---|---|
| ▶ | F5 | Run / Continue | Start execution or continue from a breakpoint |
| ⏸ | F6 | Pause | Pause execution at the current line |
| ↓ | F7 | Step Into | Execute the current line; if it calls a function, enter it |
| → | F8 | Step Over | Execute the current line; skip over function calls |
| ■ | Shift+F5 | Stop | Terminate the debug session |
Dockable Debug Panels
The debugger provides five dockable tabs that can be arranged to suit your workflow:
| Tab | Description |
|---|---|
| Watch | Add expressions to monitor their values in real-time. Supports Harbour expressions like oForm:cTitle, aArray[1], and function calls. |
| Locals | Shows all local variables in the current function scope, including their types and current values. Auto-expands arrays and objects. |
| Call Stack | Displays the full call stack with file names, line numbers, and function names. Click any frame to navigate to that source location. |
| Breakpoints | List of all breakpoints with file, line, condition, and hit count. Enable/disable or delete individual breakpoints. |
| Output | Console output from the debugged application, including ? and ?? output, MsgInfo() logs, and debug trace messages. |
Setting Breakpoints
Click in the gutter next to any line number to toggle a breakpoint. Breakpoints are indicated by a red dot:
- Standard breakpoint — pauses execution when the line is reached.
- Conditional breakpoint — right-click a breakpoint to set a Harbour expression; execution pauses only when the expression evaluates to
.T. - Hit count breakpoint — pause only after the breakpoint has been hit N times.
Debug Session Workflow
- Compile with debug flags — Press F9 to build; the IDE automatically adds
-gh -bwhen debugging. - Set breakpoints — Click in the gutter on lines you want to pause at.
- Start debugging — Press F5 or click the Run button.
- Inspect variables — Use the Watch and Locals tabs to examine state.
- Step through code — Use F7 (Step Into) or F8 (Step Over) to advance.
- Navigate the call stack — Click frames in the Call Stack panel to see context.
- Stop — Press Shift+F5 to end the session.
You can hover over any variable in the source editor while paused to see its current value in a tooltip. This works for local variables, object properties, and array elements.
Unit Test Coverage
The debugger subsystem is covered by 16 unit tests that verify:
| # | Test | Verifies |
|---|---|---|
| 1 | Breakpoint set and hit | Execution pauses at the correct line |
| 2 | Step Into function call | Debugger enters the called function |
| 3 | Step Over function call | Debugger skips over the function body |
| 4 | Step Out of function | Debugger returns to the caller |
| 5 | Local variable inspection | Locals panel shows correct names and values |
| 6 | Static variable inspection | Static variables are visible and correct |
| 7 | Watch expression evaluation | Expressions are evaluated accurately |
| 8 | Call stack accuracy | Stack frames match actual call chain |
| 9 | Conditional breakpoint (true) | Pauses when condition is met |
| 10 | Conditional breakpoint (false) | Does not pause when condition fails |
| 11 | Hit count breakpoint | Pauses only after N hits |
| 12 | Breakpoint enable/disable | Toggling works correctly |
| 13 | Multiple breakpoints | All breakpoints are respected |
| 14 | Array element inspection | Array elements display correctly |
| 15 | Object property inspection | Object properties are navigable |
| 16 | Debug session cleanup | Resources are freed on stop |
Debugger Shortcuts Summary
| Shortcut | Action |
|---|---|
| F5 | Run / Continue |
| F6 | Pause |
| F7 | Step Into |
| F8 | Step Over |
| Shift+F5 | Stop debugging |
| F9 | Toggle breakpoint on current line |
| Ctrl+Shift+F9 | Clear all breakpoints |
After debugging, use Build & Run to compile a release build
without debug symbols for distribution. Release builds are smaller and faster since they omit
the -gh -b flags.