# How to Debug Flow Graphs with Breakpoints and Step-by-Step Execution in Ceres

> Debug Ceres Flow Graphs visually with breakpoints or programmatically using FlowGraphTracker for step-by-step execution control, logging, and profiling.

- Repository: [AkiKurisu/ceres](https://github.com/akikurisu/ceres)
- Tags: how-to-guide
- Published: 2026-02-24

---

**Use the editor's Debug toolbar for visual breakpoint debugging, or implement `FlowGraphTracker` for programmatic control over node execution, logging, and profiling.**

Ceres provides a robust visual scripting system called Flow graphs that requires sophisticated debugging capabilities. Whether you prefer interactive debugging in the Unity Editor or automated analysis at runtime, you can debug Flow graphs with breakpoints and step-by-step execution using two complementary approaches that share the same underlying execution engine.

## Editor-Based Debugging

The Flow editor provides a visual debugging interface accessible directly from the toolbar. This method requires no code and allows you to inspect node execution in real-time.

### Enabling Debug Mode

To begin debugging, click the **Debug** button in the upper-right toolbar of the Flow editor. This activates the debugging session and pauses execution after each node evaluation, allowing you to control the flow manually.

### Stepping Through Execution

Once Debug Mode is active, use the **Next Frame** (►) button to execute the current node and pause before the next one. This step-by-step execution lets you observe how data transforms through each node in the graph.

### Setting Breakpoints

For targeted debugging, right-click any node and select **Add Breakpoint**. After setting breakpoints, use the **Next Breakpoint** (⏭) button on the toolbar to run the graph until it hits the first breakpoint, then continue stepping from that point.

### Inspecting Port Values

While the graph is paused at a breakpoint, hover over any input port to view a tooltip displaying the current value. This allows you to verify data integrity at specific execution points without modifying the graph.

### Hot-Reload Support

Enable **Hot Reload** in the toolbar to edit `FlowGraphObject` assets while the game is playing. Changes apply instantly without restarting the scene, enabling rapid iteration during debugging sessions.

The complete editor debugging guide is available in the repository documentation at `Documentation~/docs/flow_debugging.md`.

## Runtime Debugging with FlowGraphTracker

When you need programmatic control, automated logging, or conditional breakpoints, implement the `FlowGraphTracker` API. This approach is defined in [`Runtime/Flow/Models/FlowGraphTracker.cs`](https://github.com/akikurisu/ceres/blob/main/Runtime/Flow/Models/FlowGraphTracker.cs) and provides hooks into every node execution.

### Basic Tracker Pattern

The `FlowGraphTracker` uses an auto-scope pattern to ensure proper cleanup. Wrap your graph execution in a `using` statement with `tracker.Auto()` to automatically handle tracker lifecycle:

```csharp
using Ceres.Graph.Flow;
using Cysharp.Threading.Tasks;

public async UniTask RunWithTracker(FlowGraph graph)
{
    // Create a concrete tracker (e.g., the built-in dependency logger)
    var tracker = new FlowGraphDependencyTracker(graph);

    // Auto-scope guarantees Dispose() runs even on exceptions
    using (tracker.Auto())
    {
        // Trigger the event you want to observe
        await graph.ExecuteEventAsync(context: null, eventName: "Start", evt: null);
    }   // <- tracker.Dispose() called here; prints log summary
}

```

The `FlowGraphDependencyTracker` logs each node entry/exit and identifies missing dependencies, helping you trace execution flow programmatically.

### Custom Breakpoint Tracker

You can halt Unity's editor by implementing a custom tracker that calls `Debug.Break()` when specific conditions are met:

```csharp
using Ceres.Graph.Flow;
using Cysharp.Threading.Tasks;
using UnityEngine;

public class ConditionalBreakpointTracker : FlowGraphTracker
{
    private readonly Func<ExecutableNode, bool> _condition;

    public ConditionalBreakpointTracker(Func<ExecutableNode, bool> condition)
    {
        _condition = condition;
    }

    public override UniTask EnterNode(ExecutableNode node)
    {
        if (_condition(node))
        {
            Debug.Break();   // Pops up Unity's debugger pause
        }
        return UniTask.CompletedTask;
    }
}

// Usage:
using (new ConditionalBreakpointTracker(n => n.GetTypeName().Contains("Log")).Auto())
{
    await graph.ExecuteEventAsync(null, "Start", null);
}

```

This approach allows you to set conditional breakpoints based on node type, GUID, or custom metadata without modifying the visual graph.

### Performance Profiling

Build a lightweight profiler by tracking timing data in `EnterNode` and `ExitNode`:

```csharp
public class PerfTracker : FlowGraphTracker
{
    private readonly Dictionary<string, NodeProfile> _profiles = new();
    private readonly Stack<NodeProfile> _stack = new();

    public override UniTask EnterNode(ExecutableNode node)
    {
        if (!_profiles.TryGetValue(node.Guid, out var p))
        {
            p = new NodeProfile { NodeName = node.GetTypeName(), NodeGuid = node.Guid };
            _profiles[node.Guid] = p;
        }
        p.Stopwatch.Restart();
        _stack.Push(p);
        return UniTask.CompletedTask;
    }

    public override UniTask ExitNode(ExecutableNode node)
    {
        var p = _stack.Pop();
        p.Stopwatch.Stop();
        p.TotalTicks += p.Stopwatch.ElapsedTicks;
        return UniTask.CompletedTask;
    }

    public override void Dispose()
    {
        foreach (var kv in _profiles)
        {
            var avgMs = (kv.Value.TotalTicks / (double)Stopwatch.Frequency) /
                        kv.Value.ExecutionCount * 1000;
            Debug.Log($"{kv.Value.NodeName}: {kv.Value.ExecutionCount} runs, Avg {avgMs:F3} ms");
        }
        base.Dispose();
    }
}

```

This profiler tracks average execution time per node, helping you identify bottlenecks in complex Flow graphs.

## How Execution Tracking Works

The debugging system relies on a unified execution model defined in [`Runtime/Flow/FlowGraphObject.cs`](https://github.com/akikurisu/ceres/blob/main/Runtime/Flow/FlowGraphObject.cs) and [`Runtime/Flow/Models/FlowGraphTracker.cs`](https://github.com/akikurisu/ceres/blob/main/Runtime/Flow/Models/FlowGraphTracker.cs).

When a graph executes via `FlowGraphObjectBase`, the runtime queries `FlowGraphTracker.GetActiveTracker()` to retrieve the current tracker. If no tracker is active, a no-op `Empty` tracker is returned, ensuring zero overhead in production builds.

The active tracker receives `EnterNode` and `ExitNode` callbacks for every `ExecutableNode` in the graph. This design allows the editor's visual debugger to function as a specialized tracker that pauses execution and renders port values, while custom code implementations can perform logging, profiling, or conditional breakpoints using the same callbacks.

## Summary

- **Editor debugging** provides visual breakpoints, step-by-step execution, and port inspection through the Flow editor toolbar without writing code.
- **Runtime debugging** uses the `FlowGraphTracker` API in [`Runtime/Flow/Models/FlowGraphTracker.cs`](https://github.com/akikurisu/ceres/blob/main/Runtime/Flow/Models/FlowGraphTracker.cs) to implement custom breakpoints, logging, and profiling.
- **Auto-scope pattern** with `tracker.Auto()` ensures proper cleanup and exception safety when using trackers programmatically.
- **Unified execution model** means both editor and code debugging use the same `EnterNode`/`ExitNode` callbacks, ensuring consistent state representation.

## Frequently Asked Questions

### How do I set a breakpoint on a specific node in the Flow editor?

Right-click the target node in the Flow editor and select **Add Breakpoint** from the context menu. Once set, use the **Next Breakpoint** (⏭) button in the toolbar to execute the graph until it reaches that node, or step through manually using **Next Frame** (►) to observe execution flow.

### Can I debug Flow graphs at runtime without using the editor?

Yes. Implement the `FlowGraphTracker` class from [`Runtime/Flow/Models/FlowGraphTracker.cs`](https://github.com/akikurisu/ceres/blob/main/Runtime/Flow/Models/FlowGraphTracker.cs) and override the `EnterNode` and `ExitNode` methods to add logging, profiling, or conditional breakpoints. Wrap your graph execution in a `using` statement with `tracker.Auto()` to ensure proper lifecycle management and automatic cleanup.

### What is the difference between `FlowGraphDependencyTracker` and a custom `FlowGraphTracker`?

`FlowGraphDependencyTracker` is a built-in implementation that logs node execution entry/exit points and identifies missing dependencies between nodes. A custom `FlowGraphTracker` allows you to implement specialized behavior such as performance profiling, conditional breakpoints using `Debug.Break()`, or integration with external logging systems by overriding the virtual `EnterNode` and `ExitNode` methods.

### Does enabling debug mode affect game performance?

The editor's debug mode pauses execution after each node, which inherently slows down execution to human-interactive speeds. For runtime debugging, the `FlowGraphTracker` system uses a no-op `Empty` tracker when no tracker is active, ensuring zero overhead in production builds. Only when a tracker is explicitly instantiated and registered via `GetActiveTracker()` does the runtime incur the cost of the `EnterNode`/`ExitNode` callbacks.