add v1 debug service
This commit is contained in:
15
src/SharpIDE.Application/Features/Debugging/Debugger.cs
Normal file
15
src/SharpIDE.Application/Features/Debugging/Debugger.cs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
using SharpIDE.Application.Features.Debugging.Experimental;
|
||||||
|
using SharpIDE.Application.Features.SolutionDiscovery.VsPersistence;
|
||||||
|
|
||||||
|
namespace SharpIDE.Application.Features.Debugging;
|
||||||
|
|
||||||
|
public class Debugger
|
||||||
|
{
|
||||||
|
public required SharpIdeProjectModel Project { get; init; }
|
||||||
|
public required int ProcessId { get; init; }
|
||||||
|
public async Task Attach(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var debuggingService = new DebuggingService();
|
||||||
|
await debuggingService.Attach(ProcessId, cancellationToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,119 +1,106 @@
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO.Pipelines;
|
using Ardalis.GuardClauses;
|
||||||
using OmniSharp.Extensions.DebugAdapter.Client;
|
using Microsoft.Diagnostics.NETCore.Client;
|
||||||
using OmniSharp.Extensions.DebugAdapter.Protocol.Events;
|
using Microsoft.VisualStudio.Shared.VSCodeDebugProtocol;
|
||||||
using OmniSharp.Extensions.DebugAdapter.Protocol.Models;
|
using Microsoft.VisualStudio.Shared.VSCodeDebugProtocol.Messages;
|
||||||
using OmniSharp.Extensions.DebugAdapter.Protocol.Requests;
|
using Newtonsoft.Json.Linq;
|
||||||
|
using SharpIDE.Application.Features.Debugging.Experimental;
|
||||||
|
|
||||||
namespace SharpIDE.Application.Features.Debugging;
|
namespace SharpIDE.Application.Features.Debugging;
|
||||||
|
|
||||||
public class DebuggingService
|
public class DebuggingService
|
||||||
{
|
{
|
||||||
|
public async Task Attach(int debuggeeProcessId, CancellationToken cancellationToken = default)
|
||||||
public async Task Test(CancellationToken cancellationToken = default)
|
|
||||||
{
|
{
|
||||||
|
Guard.Against.NegativeOrZero(debuggeeProcessId, nameof(debuggeeProcessId), "Process ID must be a positive integer.");
|
||||||
await Task.CompletedTask.ConfigureAwait(ConfigureAwaitOptions.ForceYielding);
|
await Task.CompletedTask.ConfigureAwait(ConfigureAwaitOptions.ForceYielding);
|
||||||
|
|
||||||
var process = new Process
|
var process = new Process
|
||||||
{
|
{
|
||||||
StartInfo = new ProcessStartInfo
|
StartInfo = new ProcessStartInfo
|
||||||
{
|
{
|
||||||
FileName = @"C:\Users\Matthew\Downloads\netcoredbg-win64\netcoredbg\netcoredbg.exe",
|
FileName = @"C:\Users\Matthew\Downloads\netcoredbg-win64\netcoredbg\netcoredbg.exe",
|
||||||
Arguments = """--interpreter=vscode""",
|
//FileName = @"C:\Users\Matthew\.vscode-insiders\extensions\ms-dotnettools.csharp-2.83.5-win32-x64\.debugger\x86_64\vsdbg.exe",
|
||||||
|
Arguments = "--interpreter=vscode",
|
||||||
RedirectStandardInput = true,
|
RedirectStandardInput = true,
|
||||||
RedirectStandardOutput = true,
|
RedirectStandardOutput = true,
|
||||||
UseShellExecute = false
|
UseShellExecute = false,
|
||||||
|
CreateNoWindow = true
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
process.Start();
|
process.Start();
|
||||||
|
|
||||||
var stoppedTcs = new TaskCompletionSource<StoppedEvent>();
|
var debugProtocolHost = new DebugProtocolHost(process.StandardInput.BaseStream, process.StandardOutput.BaseStream, false);
|
||||||
|
debugProtocolHost.LogMessage += (sender, args) =>
|
||||||
var client = DebugAdapterClient.Create(options =>
|
|
||||||
{
|
{
|
||||||
options.AdapterId = "coreclr";
|
Console.WriteLine($"Log message: {args.Message}");
|
||||||
options.ClientId = "vscode";
|
};
|
||||||
options.ClientName = "Visual Studio Code";
|
debugProtocolHost.EventReceived += (sender, args) =>
|
||||||
options.LinesStartAt1 = true;
|
{
|
||||||
options.ColumnsStartAt1 = true;
|
Console.WriteLine($"Event received: {args.EventType}");
|
||||||
options.SupportsVariableType = true;
|
};
|
||||||
options.SupportsVariablePaging = true;
|
debugProtocolHost.DispatcherError += (sender, args) =>
|
||||||
options.SupportsRunInTerminalRequest = true;
|
{
|
||||||
options.Locale = "en-us";
|
Console.WriteLine($"Dispatcher error: {args.Exception}");
|
||||||
options.PathFormat = PathFormat.Path;
|
};
|
||||||
options
|
debugProtocolHost.RequestReceived += (sender, args) =>
|
||||||
.WithInput(process.StandardOutput.BaseStream)
|
{
|
||||||
.WithOutput(process.StandardInput.BaseStream)
|
Console.WriteLine($"Request received: {args.Command}");
|
||||||
.OnStarted(async (adapterClient, token) =>
|
};
|
||||||
{
|
debugProtocolHost.RegisterEventType<OutputEvent>(@event =>
|
||||||
Console.WriteLine("Started");
|
{
|
||||||
})
|
;
|
||||||
.OnCapabilities(async (capabilitiesEvent, token) =>
|
|
||||||
{
|
|
||||||
Console.WriteLine("Capabilities");
|
|
||||||
})
|
|
||||||
.OnBreakpoint(async (breakpointEvent, token) =>
|
|
||||||
{
|
|
||||||
Console.WriteLine($"Breakpoint hit: {breakpointEvent}");
|
|
||||||
})
|
|
||||||
.OnRunInTerminal(async (arguments, token) =>
|
|
||||||
{
|
|
||||||
Console.WriteLine("Run In Terminal");
|
|
||||||
return new RunInTerminalResponse();
|
|
||||||
})
|
|
||||||
.OnStopped(async (stoppedEvent, token) =>
|
|
||||||
{
|
|
||||||
Console.WriteLine($"Notification received: {stoppedEvent}");
|
|
||||||
stoppedTcs.SetResult(stoppedEvent);
|
|
||||||
})
|
|
||||||
.OnTerminated(async (terminatedEvent, token) =>
|
|
||||||
{
|
|
||||||
Console.WriteLine($"Terminated: {terminatedEvent}");
|
|
||||||
});
|
|
||||||
//.OnNotification(EventNames., );
|
|
||||||
});
|
});
|
||||||
|
debugProtocolHost.RegisterClientRequestType<HandshakeRequest, HandshakeArguments, HandshakeResponse>(responder =>
|
||||||
|
{
|
||||||
|
var args = responder.Arguments;
|
||||||
|
var signatureResponse = VsSigner.Sign(responder.Arguments.Value);
|
||||||
|
responder.SetResponse(new HandshakeResponse(signatureResponse));
|
||||||
|
});
|
||||||
|
debugProtocolHost.RegisterEventType<StoppedEvent>(@event =>
|
||||||
|
{
|
||||||
|
;
|
||||||
|
var threadId = @event.ThreadId;
|
||||||
|
});
|
||||||
|
debugProtocolHost.VerifySynchronousOperationAllowed();
|
||||||
|
var initializeRequest = new InitializeRequest
|
||||||
|
{
|
||||||
|
ClientID = "vscode",
|
||||||
|
ClientName = "Visual Studio Code",
|
||||||
|
AdapterID = "coreclr",
|
||||||
|
Locale = "en-us",
|
||||||
|
LinesStartAt1 = true,
|
||||||
|
ColumnsStartAt1 = true,
|
||||||
|
PathFormat = InitializeArguments.PathFormatValue.Path,
|
||||||
|
SupportsVariableType = true,
|
||||||
|
SupportsVariablePaging = true,
|
||||||
|
SupportsRunInTerminalRequest = true,
|
||||||
|
SupportsHandshakeRequest = true
|
||||||
|
};
|
||||||
|
debugProtocolHost.Run();
|
||||||
|
var response = debugProtocolHost.SendRequestSync(initializeRequest);
|
||||||
|
|
||||||
await client.Initialize(cancellationToken);
|
var attachRequest = new AttachRequest
|
||||||
var breakpointsResponse = await client.SetBreakpoints(new SetBreakpointsArguments
|
|
||||||
{
|
{
|
||||||
Source = new Source { Path = @"C:\Users\Matthew\Documents\Git\BlazorCodeBreaker\src\WebApi\Program.cs" },
|
ConfigurationProperties = new Dictionary<string, JToken>
|
||||||
Breakpoints = new Container<SourceBreakpoint>(new SourceBreakpoint { Line = 7 })
|
|
||||||
}, cancellationToken);
|
|
||||||
var launchResponse = await client.Launch(new LaunchRequestArguments()
|
|
||||||
{
|
|
||||||
NoDebug = false,
|
|
||||||
ExtensionData = new Dictionary<string, object>
|
|
||||||
{
|
{
|
||||||
["name"] = "LaunchRequestName",
|
["name"] = "AttachRequestName",
|
||||||
["type"] = "coreclr",
|
["type"] = "coreclr",
|
||||||
["program"] = @"C:\Users\Matthew\Documents\Git\BlazorCodeBreaker\artifacts\bin\WebApi\debug\WebApi.dll",
|
["processId"] = debuggeeProcessId,
|
||||||
["cwd"] = ""
|
["console"] = "internalConsole", // integratedTerminal, externalTerminal, internalConsole
|
||||||
//["cwd"] = @"C:\Users\Matthew\Documents\Git\BlazorCodeBreaker\artifacts\bin\WebApi\debug", // working directory
|
|
||||||
//["stopAtEntry"] = true,
|
|
||||||
//["env"] = new Dictionary<string, string> { { "ASPNETCORE_ENVIRONMENT", "Development" } }
|
|
||||||
}
|
}
|
||||||
}, cancellationToken);
|
};
|
||||||
|
debugProtocolHost.SendRequestSync(attachRequest);
|
||||||
|
|
||||||
var configurationDoneResponse = await client.RequestConfigurationDone(new ConfigurationDoneArguments(), cancellationToken);
|
// var breakpointRequest = new SetBreakpointsRequest
|
||||||
|
// {
|
||||||
var stoppedEvent = await stoppedTcs.Task;
|
// Source = new Source { Path = @"C:\Users\Matthew\Documents\Git\BlazorCodeBreaker\src\WebApi\Program.cs" },
|
||||||
var threads = await client.RequestThreads(new ThreadsArguments(), cancellationToken);
|
// Breakpoints = [new SourceBreakpoint { Line = 7 }]
|
||||||
|
// };
|
||||||
var currentThread = threads.Threads!.Single(s => s.Id == stoppedEvent.ThreadId);
|
// var breakpointsResponse = debugProtocolHost.SendRequestSync(breakpointRequest);
|
||||||
var stackTrace = await client.RequestStackTrace(new StackTraceArguments { ThreadId = currentThread.Id }, cancellationToken);
|
new DiagnosticsClient(debuggeeProcessId).ResumeRuntime();
|
||||||
var frame = stackTrace.StackFrames!.First();
|
var configurationDoneRequest = new ConfigurationDoneRequest();
|
||||||
var scopes = await client.RequestScopes(new ScopesArguments { FrameId = frame.Id }, cancellationToken);
|
debugProtocolHost.SendRequestSync(configurationDoneRequest);
|
||||||
var scope = scopes.Scopes.First();
|
|
||||||
var variablesResponse = await client.RequestVariables(new VariablesArguments() {VariablesReference = scope.VariablesReference}, cancellationToken);
|
|
||||||
var variable = variablesResponse.Variables!.Skip(1).First();
|
|
||||||
var variables2Response = await client.RequestVariables(new VariablesArguments() {VariablesReference = variable.VariablesReference}, cancellationToken);
|
|
||||||
var variable2 = variables2Response.Variables!.First();
|
|
||||||
var variables3Response = await client.RequestVariables(new VariablesArguments() {VariablesReference = variable2.VariablesReference}, cancellationToken);
|
|
||||||
//var continueResponse = await client.RequestContinue(new ContinueArguments(){ThreadId = 1}, cancellationToken);
|
|
||||||
//await Task.Delay(1000);
|
|
||||||
//var test = await client.RequestNext(new NextArguments(), cancellationToken: cancellationToken);
|
|
||||||
|
|
||||||
//var result = await client.RequestStepIn(new StepInArguments(), cancellationToken: cancellationToken);
|
|
||||||
|
|
||||||
await process.WaitForExitAsync();
|
|
||||||
}
|
}
|
||||||
|
// Typically you would do attachRequest, configurationDoneRequest, setBreakpointsRequest, then ResumeRuntime. But netcoredbg blows up on configurationDoneRequuest if ResumeRuntime hasn't been called yet.
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
using System.Threading.Channels;
|
using System.Threading.Channels;
|
||||||
using Ardalis.GuardClauses;
|
using Ardalis.GuardClauses;
|
||||||
using AsyncReadProcess;
|
using AsyncReadProcess;
|
||||||
|
using SharpIDE.Application.Features.Debugging;
|
||||||
using SharpIDE.Application.Features.Evaluation;
|
using SharpIDE.Application.Features.Evaluation;
|
||||||
using SharpIDE.Application.Features.Events;
|
using SharpIDE.Application.Features.Events;
|
||||||
using SharpIDE.Application.Features.SolutionDiscovery.VsPersistence;
|
using SharpIDE.Application.Features.SolutionDiscovery.VsPersistence;
|
||||||
@@ -13,6 +14,7 @@ public class RunService
|
|||||||
private readonly ConcurrentDictionary<SharpIdeProjectModel, SemaphoreSlim> _projectLocks = [];
|
private readonly ConcurrentDictionary<SharpIdeProjectModel, SemaphoreSlim> _projectLocks = [];
|
||||||
public async Task RunProject(SharpIdeProjectModel project)
|
public async Task RunProject(SharpIdeProjectModel project)
|
||||||
{
|
{
|
||||||
|
var isDebug = true;
|
||||||
Guard.Against.Null(project, nameof(project));
|
Guard.Against.Null(project, nameof(project));
|
||||||
Guard.Against.NullOrWhiteSpace(project.FilePath, nameof(project.FilePath), "Project file path cannot be null or empty.");
|
Guard.Against.NullOrWhiteSpace(project.FilePath, nameof(project.FilePath), "Project file path cannot be null or empty.");
|
||||||
await Task.CompletedTask.ConfigureAwait(ConfigureAwaitOptions.ForceYielding);
|
await Task.CompletedTask.ConfigureAwait(ConfigureAwaitOptions.ForceYielding);
|
||||||
@@ -28,6 +30,7 @@ public class RunService
|
|||||||
var launchProfile = launchProfiles.FirstOrDefault();
|
var launchProfile = launchProfiles.FirstOrDefault();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
// TODO: handle running Blazor projects
|
||||||
var processStartInfo = new ProcessStartInfo2
|
var processStartInfo = new ProcessStartInfo2
|
||||||
{
|
{
|
||||||
FileName = "dotnet",
|
FileName = "dotnet",
|
||||||
@@ -46,6 +49,10 @@ public class RunService
|
|||||||
}
|
}
|
||||||
if (launchProfile.ApplicationUrl != null) processStartInfo.EnvironmentVariables["ASPNETCORE_URLS"] = launchProfile.ApplicationUrl;
|
if (launchProfile.ApplicationUrl != null) processStartInfo.EnvironmentVariables["ASPNETCORE_URLS"] = launchProfile.ApplicationUrl;
|
||||||
}
|
}
|
||||||
|
if (isDebug)
|
||||||
|
{
|
||||||
|
processStartInfo.EnvironmentVariables["DOTNET_DefaultDiagnosticPortSuspend"] = "1";
|
||||||
|
}
|
||||||
|
|
||||||
var process = new Process2
|
var process = new Process2
|
||||||
{
|
{
|
||||||
@@ -72,6 +79,13 @@ public class RunService
|
|||||||
logsDrained.TrySetResult();
|
logsDrained.TrySetResult();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (isDebug)
|
||||||
|
{
|
||||||
|
// Attach debugger (which internally uses a DiagnosticClient to resume startup)
|
||||||
|
var debuggingService = new Debugger { Project = project, ProcessId = process.ProcessId };
|
||||||
|
await debuggingService.Attach(project.RunningCancellationTokenSource.Token).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
project.Running = true;
|
project.Running = true;
|
||||||
project.OpenInRunPanel = true;
|
project.OpenInRunPanel = true;
|
||||||
GlobalEvents.InvokeProjectsRunningChanged();
|
GlobalEvents.InvokeProjectsRunningChanged();
|
||||||
|
|||||||
@@ -13,6 +13,8 @@
|
|||||||
<HintPath>.\Microsoft.CodeAnalysis.Workspaces.MSBuild.dll</HintPath>
|
<HintPath>.\Microsoft.CodeAnalysis.Workspaces.MSBuild.dll</HintPath>
|
||||||
<Private>true</Private>
|
<Private>true</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
<PackageReference Include="Microsoft.Diagnostics.NETCore.Client" Version="0.2.641201" />
|
||||||
|
<PackageReference Include="Microsoft.VisualStudio.Shared.VSCodeDebugProtocol" Version="18.0.10427.1" />
|
||||||
<!-- If any Microsoft.Build.*.dll (Excluding Locator) ends up in the output, it will be prioritised for loading by MSBuild Nodes -->
|
<!-- If any Microsoft.Build.*.dll (Excluding Locator) ends up in the output, it will be prioritised for loading by MSBuild Nodes -->
|
||||||
<PackageReference Include="OmniSharp.Extensions.DebugAdapter.Client" Version="0.19.9" />
|
<PackageReference Include="OmniSharp.Extensions.DebugAdapter.Client" Version="0.19.9" />
|
||||||
<PackageReference Include="Ardalis.GuardClauses" Version="5.0.0" />
|
<PackageReference Include="Ardalis.GuardClauses" Version="5.0.0" />
|
||||||
|
|||||||
Reference in New Issue
Block a user