reckfellENGINE

C# debugging: choose the right tool

A runnable example and the boundaries of Output, Remote Inspector, Profiler and managed debugging.

On this page

Start with the symptom

SymptomTool and result
Project does not builddotnet build output; fix the first error
Wrong command resultOutput, operation inputs and its result
Wrong object or property valueRemote tree and Inspector of the running application
Slow frameFrame metrics, then measurement of a specific workload
Growing memoryA repeatable scenario and separate native/C# accounting
C# locals or stepping requiredA separately configured managed debugger

Reckfell 1.0.0 can transmit C# errors and object properties through remote diagnostics. This is not full managed breakpoint, locals and watches support in the built-in panel. Built-in C# method profiling is not provided here either.

Run the check level

Use a C# project with Reckfell.NET.Sdk 1.0.0 and net10.0.

  1. Save all four files below with their exact names.
  2. Run dotnet build.
  3. Create a level with an Entity root, attach DiagnosticsExample.cs and save as diagnostics.rscene.
  4. Run it. Output should contain DIAGNOSTICS checksum=8386560; lifetime=True; ok=True.

MEASURE and MEMORY values vary with the machine and run. They are not performance targets. In the running tree find DiagnosticsExample/InspectionTarget: DiagnosticToken equals 424242. A standalone level root may have another name; the child is named InspectionTarget.

DebugInspectionTarget.cs
C#
using Reckfell;

public partial class DebugInspectionTarget : Entity
{
    [Export] public int DiagnosticToken { get; set; } = 424242;
}
WorkMeasurement.cs
C#
using System;
using System.Diagnostics;

public readonly record struct WorkMeasurement(double Milliseconds, long AllocatedBytes, long Checksum)
{
    public static WorkMeasurement Run(int iterations)
    {
        if (iterations < 0 || iterations > 10_000_000)
            throw new ArgumentOutOfRangeException(nameof(iterations));
        long before = GC.GetAllocatedBytesForCurrentThread();
        long start = Stopwatch.GetTimestamp();
        long checksum = 0;
        for (int i = 0; i < iterations; i++) checksum += i;
        double elapsed = Stopwatch.GetElapsedTime(start).TotalMilliseconds;
        long allocated = GC.GetAllocatedBytesForCurrentThread() - before;
        return new WorkMeasurement(elapsed, allocated, checksum);
    }
}
ObjectLifetimeProbe.cs
C#
using System;
using System.Collections.Generic;
using Reckfell;

public static class ObjectLifetimeProbe
{
    public static bool Run()
    {
        var retained = new List<Entity>();
        var parent = new Entity();
        var child = new Entity();
        retained.Add(child);
        parent.AddChild(child);
        parent.RemoveChild(child);
        parent.Free();
        bool detachedSurvived = ReckfellObject.IsInstanceValid(child);
        child.Free();
        bool wrapperOutlivedNative = retained[0] is not null &&
            !ReckfellObject.IsInstanceValid(retained[0]);
        retained.Clear();
        return detachedSurvived && wrapperOutlivedNative;
    }

    public static void PrintSnapshot()
    {
        double objects = Performance.GetMonitor(Performance.Monitor.ObjectCount);
        double entities = Performance.GetMonitor(Performance.Monitor.ObjectNodeCount);
        double orphans = Performance.GetMonitor(Performance.Monitor.ObjectOrphanNodeCount);
        long managed = GC.GetTotalMemory(false);
        ReckfellApi.Print($"MEMORY objects={objects}; entitiesInTree={entities}; outsideTree={orphans}; managedApproxBytes={managed}");
    }
}
DiagnosticsExample.cs
C#
using System.Globalization;
using Reckfell;

public partial class DiagnosticsExample : Entity
{
    public bool Succeeded { get; private set; }

    public override void _Ready()
    {
        var target = new DebugInspectionTarget { Name = "InspectionTarget" };
        AddChild(target);
        _ = WorkMeasurement.Run(64);
        WorkMeasurement measured = WorkMeasurement.Run(4096);
        bool lifetime = ObjectLifetimeProbe.Run();
        Succeeded = measured.Checksum == 8386560 && lifetime;
        ReckfellApi.Print($"DIAGNOSTICS checksum={measured.Checksum}; lifetime={lifetime}; ok={Succeeded}");
        ReckfellApi.Print("MEASURE ms=" + measured.Milliseconds.ToString("F4", CultureInfo.InvariantCulture) +
            "; currentThreadAllocatedBytes=" + measured.AllocatedBytes);
        ObjectLifetimeProbe.PrintSnapshot();
    }
}

Preserve reproducibility

Record engine version, Debug/ExportRelease configuration, platform, reproduction steps and the first error. Change one cause at a time. Use logging settings rather than capturing only the final line without context.

Diagnostics panel · Measure C# work · Lifetime and memory

Reckfell Engine documentationBack to top ↑
QUICK LINKSGetting startedEntity hierarchyC# & .NET 10Scenes and prefabs
Reckfell guides and full API K
reckfellENGINE
RECKFELL ACCOUNT

Sign in

Sign in to your Reckfell account.

One account for the entire Reckfell platform.