refactor creation of debugger process

This commit is contained in:
Matt Parker
2026-01-06 19:15:49 +10:00
parent 60e7f9d650
commit 50fdf68bd1
6 changed files with 54 additions and 27 deletions

View File

@@ -1,4 +1,5 @@
using Microsoft.VisualStudio.Shared.VSCodeDebugProtocol.Messages;
using SharpIDE.Application.Features.Run;
using SharpIDE.Application.Features.SolutionDiscovery;
using SharpIDE.Application.Features.SolutionDiscovery.VsPersistence;
@@ -10,9 +11,9 @@ public class Debugger
public required SharpIdeProjectModel Project { get; init; }
public required int ProcessId { get; init; }
private DebuggingService _debuggingService = new DebuggingService();
public async Task Attach(string? debuggerExecutablePath, Dictionary<SharpIdeFile, List<Breakpoint>> breakpointsByFile, SharpIdeProjectModel project, CancellationToken cancellationToken)
public async Task Attach(DebuggerExecutableInfo? debuggerExecutableInfo, Dictionary<SharpIdeFile, List<Breakpoint>> breakpointsByFile, SharpIdeProjectModel project, CancellationToken cancellationToken)
{
await _debuggingService.Attach(ProcessId, debuggerExecutablePath, breakpointsByFile, project, cancellationToken);
await _debuggingService.Attach(ProcessId, debuggerExecutableInfo, breakpointsByFile, project, cancellationToken);
}
public async Task SetBreakpointsForFile(SharpIdeFile file, List<Breakpoint> breakpoints, CancellationToken cancellationToken = default) => await _debuggingService.SetBreakpointsForFile(file, breakpoints, cancellationToken);

View File

@@ -0,0 +1,32 @@
using System.Diagnostics;
using Ardalis.GuardClauses;
using SharpIDE.Application.Features.Run;
namespace SharpIDE.Application.Features.Debugging;
public static class DebuggerProcessStreamHelper
{
public static (Stream Input, Stream Output, bool IsNetCoreDbg) NewDebuggerProcessStreamsForInfo(DebuggerExecutableInfo? debuggerExecutableInfoNullable)
{
if (debuggerExecutableInfoNullable is not {} debuggerExecutableInfo) throw new ArgumentNullException(nameof(debuggerExecutableInfoNullable), "Debugger executable info cannot be null.");
var debuggerExecutablePath = debuggerExecutableInfo.DebuggerExecutablePath;
Guard.Against.NullOrWhiteSpace(debuggerExecutablePath, nameof(debuggerExecutablePath), "Debugger executable path cannot be null or empty.");
var isNetCoreDbg = Path.GetFileNameWithoutExtension(debuggerExecutablePath).Equals("netcoredbg", StringComparison.OrdinalIgnoreCase);
var process = new Process
{
StartInfo = new ProcessStartInfo
{
//FileName = @"C:\Users\Matthew\Downloads\netcoredbg-win64\netcoredbg\netcoredbg.exe",
FileName = debuggerExecutablePath,
Arguments = "--interpreter=vscode",
RedirectStandardInput = true,
RedirectStandardOutput = true,
UseShellExecute = false,
CreateNoWindow = true
}
};
process.Start();
return (process.StandardInput.BaseStream, process.StandardOutput.BaseStream, isNetCoreDbg);
}
}

View File

@@ -7,6 +7,7 @@ using Microsoft.VisualStudio.Shared.VSCodeDebugProtocol.Messages;
using Newtonsoft.Json.Linq;
using SharpIDE.Application.Features.Debugging.Signing;
using SharpIDE.Application.Features.Events;
using SharpIDE.Application.Features.Run;
using SharpIDE.Application.Features.SolutionDiscovery;
using SharpIDE.Application.Features.SolutionDiscovery.VsPersistence;
@@ -16,33 +17,14 @@ namespace SharpIDE.Application.Features.Debugging;
public class DebuggingService
{
private DebugProtocolHost _debugProtocolHost = null!;
public async Task Attach(int debuggeeProcessId, string? debuggerExecutablePath, Dictionary<SharpIdeFile, List<Breakpoint>> breakpointsByFile, SharpIdeProjectModel project, CancellationToken cancellationToken = default)
public async Task Attach(int debuggeeProcessId, DebuggerExecutableInfo? debuggerExecutableInfo, Dictionary<SharpIdeFile, List<Breakpoint>> breakpointsByFile, SharpIdeProjectModel project, CancellationToken cancellationToken = default)
{
Guard.Against.NegativeOrZero(debuggeeProcessId, nameof(debuggeeProcessId), "Process ID must be a positive integer.");
await Task.CompletedTask.ConfigureAwait(ConfigureAwaitOptions.ForceYielding);
if (string.IsNullOrWhiteSpace(debuggerExecutablePath))
{
throw new ArgumentNullException(nameof(debuggerExecutablePath), "Debugger executable path cannot be null or empty.");
}
var isNetCoreDbg = Path.GetFileNameWithoutExtension(debuggerExecutablePath).Equals("netcoredbg", StringComparison.OrdinalIgnoreCase);
var (inputStream, outputStream, isNetCoreDbg) = DebuggerProcessStreamHelper.NewDebuggerProcessStreamsForInfo(debuggerExecutableInfo);
var process = new Process
{
StartInfo = new ProcessStartInfo
{
//FileName = @"C:\Users\Matthew\Downloads\netcoredbg-win64\netcoredbg\netcoredbg.exe",
FileName = debuggerExecutablePath,
Arguments = "--interpreter=vscode",
RedirectStandardInput = true,
RedirectStandardOutput = true,
UseShellExecute = false,
CreateNoWindow = true
}
};
process.Start();
var debugProtocolHost = new DebugProtocolHost(process.StandardInput.BaseStream, process.StandardOutput.BaseStream, false);
var debugProtocolHost = new DebugProtocolHost(inputStream, outputStream, false);
var initializedEventTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
_debugProtocolHost = debugProtocolHost;
debugProtocolHost.LogMessage += (sender, args) =>

View File

@@ -0,0 +1,7 @@
namespace SharpIDE.Application.Features.Run;
public readonly record struct DebuggerExecutableInfo
{
public required bool UseInMemorySharpDbg { get; init; }
public required string? DebuggerExecutablePath { get; init; }
}

View File

@@ -22,7 +22,7 @@ public partial class RunService(ILogger<RunService> logger, RoslynAnalysis rosly
private readonly ILogger<RunService> _logger = logger;
private readonly RoslynAnalysis _roslynAnalysis = roslynAnalysis;
public async Task RunProject(SharpIdeProjectModel project, bool isDebug = false, string? debuggerExecutablePath = null)
public async Task RunProject(SharpIdeProjectModel project, bool isDebug = false, DebuggerExecutableInfo? debuggerExecutableInfo = null)
{
Guard.Against.Null(project, nameof(project));
Guard.Against.NullOrWhiteSpace(project.FilePath, nameof(project.FilePath), "Project file path cannot be null or empty.");
@@ -93,7 +93,7 @@ public partial class RunService(ILogger<RunService> logger, RoslynAnalysis rosly
// Attach debugger (which internally uses a DiagnosticClient to resume startup)
var debugger = new Debugger { Project = project, ProcessId = process.ProcessId };
_debugger = debugger;
await debugger.Attach(debuggerExecutablePath, Breakpoints.ToDictionary(), project, project.RunningCancellationTokenSource.Token).ConfigureAwait(false);
await debugger.Attach(debuggerExecutableInfo, Breakpoints.ToDictionary(), project, project.RunningCancellationTokenSource.Token).ConfigureAwait(false);
}
project.Running = true;

View File

@@ -62,6 +62,11 @@ public partial class RunMenuItem : HBoxContainer
private async void OnDebugButtonPressed()
{
GodotGlobalEvents.Instance.BottomPanelTabExternallySelected.InvokeParallelFireAndForget(BottomPanelType.Debug);
await _runService.RunProject(Project, true, Singletons.AppState.IdeSettings.DebuggerExecutablePath).ConfigureAwait(false);
var debuggerExecutableInfo = new DebuggerExecutableInfo
{
UseInMemorySharpDbg = false,
DebuggerExecutablePath = Singletons.AppState.IdeSettings.DebuggerExecutablePath
};
await _runService.RunProject(Project, true, debuggerExecutableInfo).ConfigureAwait(false);
}
}