# How to Debug gdext Extensions Using Godot’s Built-in Debugger and Breakpoints

> Learn to debug gdext extensions in Godot using the built-in debugger and breakpoints. Compile with debug symbols, enable logging, and set breakpoints for efficient troubleshooting.

- Repository: [godot-nim 4+/gdext-nim](https://github.com/godot-nim/gdext-nim)
- Tags: how-to-guide
- Published: 2026-03-02

---

**To debug gdext extensions in Godot, compile your Nim library with the debug target to generate symbol information, optionally enable the `Dev.debugCallbacks` flag for verbose lifecycle logging, and use the `EngineDebugger` API to set breakpoints or pause execution programmatically.**

The gdext-nim bindings allow you to debug Nim-based GDExtensions directly within the Godot editor. By exposing Godot’s `EngineDebugger` class and compiling with debug symbols, you can leverage breakpoints, step-through debugging, and detailed logging without leaving the editor environment.

## Compile with Debug Symbols and Logging

Before you can hit breakpoints, you must build the shared library with debug information. In `src/gdext/private/buildsettings.nim`, the `Target.debug` setting controls symbol generation, while the `Dev_debugCallbacks` boolean enables optional lifecycle logging.

### Configure the Debug Target

Create or modify your `config.nims` to specify the debug target:

```nim

# config.nims

import gdext/buildconf
let setting = BuildSettings(
  target: debug,               # ← enables debug symbols for the shared library

  platform: "linux",
  arch: "x86_64")
configure(setting)

```

When `target` is set to `debug`, the compiler passes `-d:debug` and includes the necessary symbol tables that Godot uses to map machine code back to Nim source lines. Rebuild the extension with `nimble build` to apply these settings.

### Enable Verbose Lifecycle Logs

For detailed console and file logging of object instantiation, creation, and freeing, enable the `Dev.debugCallbacks` flag in `src/gdext/private/buildsettings.nim`:

```nim

# src/gdext/private/buildsettings.nim

Dev_debugCallbacks {.booldefine: "Dev.debugCallbacks".} = on

```

With this flag active, `src/gdext/private/debugging.nim` compiles a `GroupLogger` that writes entries such as `Instantiate`, `CREATE`, and `FREE` to both the console and a `<project>.callbacks.log` file. This helps trace object lifecycles even when the debugger is not paused.

## Insert and Manage Breakpoints Programmatically

The `EngineDebugger` wrapper in `src/gdext/classes/gdenginedebugger.nim` exposes Godot’s native breakpoint API, allowing you to manipulate breakpoints from Nim code without touching the editor UI.

To insert a breakpoint at a specific line:

```nim
import gdext/classes/gdenginedebugger

proc setupBreakpoints() =
  let sourcePath = "res://my_extension/main.nim".toStringName
  EngineDebugger.insertBreakpoint(line = 42, source = sourcePath)

```

Other available methods include:
- **`EngineDebugger.removeBreakpoint(line, source)`** – Removes a single breakpoint.
- **`EngineDebugger.clearBreakpoints()`** – Clears all registered breakpoints.
- **`EngineDebugger.isBreakpoint(line, source)`** – Returns true if a breakpoint exists at the given location.

These calls forward directly to Godot’s `EngineDebugger.insert_breakpoint` and behave identically to breakpoints set via the editor gutter.

## Trigger the Debugger at Runtime

When your extension reaches a critical state that requires immediate inspection, call the `debug()` method to force the Godot debugger to pause:

```nim
proc heavyCalculation() =
  # Perform work...

  if result < 0:
    EngineDebugger.debug(canContinue = true, isErrorBreakpoint = false)

```

The parameters control pause behavior:
- **`canContinue`** – When `true`, Godot resumes execution after the next step/continue command.
- **`isErrorBreakpoint`** – When `true`, the debugger pauses even if the user has disabled breakpoints globally.

For custom script language integrations, you can also use `EngineDebugger.scriptDebug(language, ...)` to hook into the script debugging pipeline.

## Use the Godot Editor UI

Once your extension is compiled with debug symbols, the Godot Script Editor integrates seamlessly with Nim source files. In `src/gdext/classes/gdcodeedit.nim`, the `setLineAsBreakpoint` method handles UI interactions:

1. Open **Script** → **Editor** → **Script Editor**.
2. Click the gutter next to any line in your `.nim` file to toggle a breakpoint.
3. Run the scene; execution pauses when the line is reached, displaying the call stack and local variables.

The UI maps breakpoints using the exact source path strings (e.g., `res://my_extension/main.nim`) that you pass to `EngineDebugger.insertBreakpoint`, ensuring consistency between programmatic and visual breakpoint management.

## Inspect Runtime Data and Logs

While execution is paused, the Godot debugger’s **Locals**, **Members**, and **Inspect** panels display Nim object fields because gdext generates GDExtension-compatible class bindings. You can examine `ref object` properties and step through `_ready`, `_process`, and custom methods.

If you enabled `Dev.debugCallbacks`, check the console or the `<project>.callbacks.log` file for chronological event streams:

```

[2026-03-02 14:12:03] DEBUG: Instantiate: MyClass:12345678
[2026-03-02 14:12:03] NOTICE: CREATE: MyClass:12345678

```

Use the `addDebugInfo(self)` helper from `src/gdext/private/debugging.nim` to tag objects with their Nim type names for easier identification in these logs.

## Complete Debugging Example

The following skeleton demonstrates a debug-enabled extension with programmatic breakpoints and lifecycle logging:

```nim

# src/main.nim

import gdext
import gdext/classes/[gdenginedebugger, gdnode]
import gdext/private/debugging

type MyNode = ref object of Node

proc _ready(self: MyNode) =
  # Log initialization when Dev.debugCallbacks is on

  addDebugInfo(self)
  debug "MyNode._ready executed"
  
  # Set a breakpoint at line 15 of this file

  EngineDebugger.insertBreakpoint(15, "res://src/main.nim".toStringName)

proc _process(self: MyNode, delta: float64) =
  if delta > 0.5:
    # Pause execution when frame time spikes

    EngineDebugger.debug(canContinue = true)

gdextension(MyNode, "MyNode")

```

Compile with `nimble build -d:debug`, run the project in the Godot editor, and add a `MyNode` instance to your scene. The debugger will pause at line 15 when `_ready` runs, and again in `_process` whenever the delta exceeds 0.5 seconds.

## Summary

- **Compile with `target: debug`** in your `config.nims` to generate symbols required for breakpoint mapping.
- **Enable `Dev.debugCallbacks`** in `src/gdext/private/buildsettings.nim` to write detailed lifecycle logs to console and `<project>.callbacks.log`.
- **Use `EngineDebugger.insertBreakpoint`** and related methods from `src/gdext/classes/gdenginedebugger.nim` to manage breakpoints programmatically.
- **Call `EngineDebugger.debug`** to trigger immediate pauses at runtime based on application logic.
- **Interact via the Script Editor UI**, which uses `CodeEdit.setLineAsBreakpoint` from `src/gdext/classes/gdcodeedit.nim` to toggle visual breakpoints that map directly to Nim source lines.

## Frequently Asked Questions

### How do I enable debug symbols for gdext-nim extensions?

Set `target: debug` in your `config.nims` `BuildSettings` and recompile. This configures the Nim compiler to include debug information in the shared library, allowing Godot to map machine instructions back to your Nim source code for breakpoints and stack traces.

### Can I set breakpoints programmatically instead of using the Godot UI?

Yes. Use `EngineDebugger.insertBreakpoint(line, source)` where `source` is a `StringName` containing the absolute `res://` path to your Nim file. This method, defined in `src/gdext/classes/gdenginedebugger.nim`, functions identically to clicking the gutter in the Script Editor.

### What is the difference between `EngineDebugger.debug()` and `insertBreakpoint()`?

`insertBreakpoint()` registers a persistent breakpoint at a specific line that triggers whenever execution reaches that location, while `debug()` immediately pauses execution at the call site regardless of line numbers. Use `debug()` for conditional pauses based on runtime state, and `insertBreakpoint()` for static line-based debugging.

### Where are lifecycle logs saved when `Dev.debugCallbacks` is enabled?

When the `Dev.debugCallbacks` flag is set to `on` in `src/gdext/private/buildsettings.nim`, the `GroupLogger` in `src/gdext/private/debugging.nim` writes lifecycle events (instantiate, create, free) to both the Godot console and a file named `<project>.callbacks.log` in your project root.