2025-11-30 13:37:33 -06:00
|
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
|
|
|
using Microsoft.Iris.Debug;
|
2025-12-01 00:45:01 -06:00
|
|
|
using Microsoft.Iris.Debug.Data;
|
2025-11-30 13:37:33 -06:00
|
|
|
using Microsoft.Iris.DebugAdapter.Server.Handlers;
|
2025-12-01 00:45:01 -06:00
|
|
|
using OmniSharp.Extensions.DebugAdapter.Protocol.Events;
|
2025-11-30 13:37:33 -06:00
|
|
|
using OmniSharp.Extensions.DebugAdapter.Server;
|
|
|
|
|
using System;
|
|
|
|
|
using System.IO;
|
2025-12-01 00:45:01 -06:00
|
|
|
using System.Threading;
|
2025-11-30 13:37:33 -06:00
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
|
|
|
|
|
namespace Microsoft.Iris.DebugAdapter.Server;
|
|
|
|
|
|
2025-12-01 00:45:01 -06:00
|
|
|
public class IrisDebugAdapterServer : IDebuggerServer, IRemoteDebuggerState, IDisposable
|
2025-11-30 13:37:33 -06:00
|
|
|
{
|
|
|
|
|
private readonly TaskCompletionSource<bool> _serverStopped;
|
2025-12-01 00:45:01 -06:00
|
|
|
private Stream _inputStream, _outputStream;
|
|
|
|
|
private InterpreterCommand _debuggerCommand;
|
2025-11-30 13:37:33 -06:00
|
|
|
|
2025-12-01 00:45:01 -06:00
|
|
|
public IrisDebugAdapterServer(IrisDebugServerOptions options)
|
2025-11-30 13:37:33 -06:00
|
|
|
{
|
|
|
|
|
_serverStopped = new();
|
|
|
|
|
|
|
|
|
|
Options = options;
|
2025-12-01 00:45:01 -06:00
|
|
|
ConnectionString = options.ConnectionString;
|
2025-11-30 13:37:33 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal IrisDebugServerOptions Options { get; }
|
|
|
|
|
|
|
|
|
|
internal DebugAdapterServer? Server { get; private set; }
|
|
|
|
|
|
2025-12-01 00:45:01 -06:00
|
|
|
public InterpreterCommand DebuggerCommand
|
|
|
|
|
{
|
|
|
|
|
get => _debuggerCommand;
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
_debuggerCommand = value;
|
|
|
|
|
|
|
|
|
|
if (Server is null)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (value is InterpreterCommand.Continue)
|
|
|
|
|
{
|
|
|
|
|
Server.SendContinued(new()
|
|
|
|
|
{
|
|
|
|
|
ThreadId = Environment.CurrentManagedThreadId,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
else if (value is InterpreterCommand.Break)
|
|
|
|
|
{
|
|
|
|
|
Server.SendStopped(new()
|
|
|
|
|
{
|
|
|
|
|
Reason = new("unknown"),
|
|
|
|
|
ThreadId = Environment.CurrentManagedThreadId,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public string ConnectionString { get; }
|
|
|
|
|
|
2025-11-30 13:37:33 -06:00
|
|
|
/// <summary>
|
|
|
|
|
/// Start the debug server listening.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <returns>A task that completes when the server is ready.</returns>
|
|
|
|
|
public async Task StartAsync()
|
|
|
|
|
{
|
2025-12-01 00:45:01 -06:00
|
|
|
Server = DebugAdapterServer.Create(options =>
|
2025-11-30 13:37:33 -06:00
|
|
|
{
|
|
|
|
|
// We need to let the PowerShell Context Service know that we are in a debug session
|
|
|
|
|
// so that it doesn't send the powerShell/startDebugger message.
|
|
|
|
|
//_psesHost = ServiceProvider.GetService<PsesInternalHost>();
|
|
|
|
|
//_psesHost.DebugContext.IsDebugServerActive = true;
|
|
|
|
|
|
|
|
|
|
options
|
|
|
|
|
.WithInput(_inputStream)
|
|
|
|
|
.WithOutput(_outputStream)
|
|
|
|
|
.WithServices(serviceCollection =>
|
|
|
|
|
serviceCollection
|
|
|
|
|
.AddOptions()
|
2025-12-01 00:45:01 -06:00
|
|
|
.AddIrisDebugServices(this, Options.SymbolDir, Options.SourceDir)
|
2025-11-30 13:37:33 -06:00
|
|
|
)
|
|
|
|
|
// TODO: Consider replacing all WithHandler with AddSingleton
|
|
|
|
|
//.WithHandler<AttachHandler>()
|
|
|
|
|
//.WithHandler<DisconnectHandler>()
|
|
|
|
|
.WithHandler<BreakpointHandler>()
|
|
|
|
|
//.WithHandler<ConfigurationDoneHandler>()
|
|
|
|
|
//.WithHandler<ThreadsHandler>()
|
|
|
|
|
//.WithHandler<StackTraceHandler>()
|
|
|
|
|
//.WithHandler<ScopesHandler>()
|
|
|
|
|
//.WithHandler<VariablesHandler>()
|
|
|
|
|
.WithHandler<ContinueHandler>()
|
|
|
|
|
.WithHandler<NextHandler>()
|
|
|
|
|
.WithHandler<PauseHandler>()
|
|
|
|
|
.WithHandler<StepInHandler>()
|
|
|
|
|
//.WithHandler<StepOutHandler>()
|
|
|
|
|
//.WithHandler<SourceHandler>()
|
|
|
|
|
//.WithHandler<SetVariableHandler>()
|
|
|
|
|
//.WithHandler<DebugEvaluateHandler>()
|
|
|
|
|
// The OnInitialize delegate gets run when we first receive the _Initialize_ request:
|
|
|
|
|
// https://microsoft.github.io/debug-adapter-protocol/specification#Requests_Initialize
|
|
|
|
|
.OnInitialize(async (server, _, cancellationToken) =>
|
|
|
|
|
{
|
2025-12-03 14:03:19 -06:00
|
|
|
System.Diagnostics.Debug.WriteLine("SERVER: OnInitialize called");
|
|
|
|
|
Console.WriteLine("SERVER: OnInitialize called");
|
2025-11-30 13:37:33 -06:00
|
|
|
})
|
|
|
|
|
.OnInitialized((_, _, response, _) =>
|
|
|
|
|
{
|
2025-12-03 14:03:19 -06:00
|
|
|
System.Diagnostics.Debug.WriteLine("SERVER: OnInitialized called");
|
|
|
|
|
Console.WriteLine("SERVER: OnInitialized called");
|
2025-11-30 13:37:33 -06:00
|
|
|
return Task.CompletedTask;
|
|
|
|
|
})
|
|
|
|
|
;
|
2025-12-01 00:45:01 -06:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
await Server.Initialize(default).ConfigureAwait(false);
|
|
|
|
|
|
|
|
|
|
Connected?.Invoke(this, EventArgs.Empty);
|
2025-12-03 14:03:19 -06:00
|
|
|
|
|
|
|
|
await WaitForShutdownAsync().ConfigureAwait(false);
|
2025-11-30 13:37:33 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Dispose()
|
|
|
|
|
{
|
|
|
|
|
// Note that the lifetime of the DebugContext is longer than the debug server;
|
|
|
|
|
// It represents the debugger on the PowerShell process we're in,
|
|
|
|
|
// while a new debug server is spun up for every debugging session
|
|
|
|
|
//_psesHost.DebugContext.IsDebugServerActive = false;
|
|
|
|
|
Server?.Dispose();
|
|
|
|
|
_inputStream.Dispose();
|
|
|
|
|
_outputStream.Dispose();
|
|
|
|
|
_serverStopped.SetResult(true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task WaitForShutdownAsync() => await _serverStopped.Task.ConfigureAwait(false);
|
|
|
|
|
|
|
|
|
|
public event EventHandler? SessionEnded;
|
2025-12-01 00:45:01 -06:00
|
|
|
public event Action<IDebuggerState, object> Connected;
|
2025-11-30 13:37:33 -06:00
|
|
|
|
|
|
|
|
internal void OnSessionEnded() => SessionEnded?.Invoke(this, EventArgs.Empty);
|
2025-12-01 00:45:01 -06:00
|
|
|
|
|
|
|
|
public MarkupLineNumberEntry[] OnLineNumberTableRequested(string uri)
|
|
|
|
|
{
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void LogInterpreterDecode(object context, InterpreterInstruction instruction)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void LogInterpreterExecute(object context, InterpreterEntry entry)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void LogDispatcher(string message)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void WaitForContinue()
|
|
|
|
|
{
|
|
|
|
|
while (DebuggerCommand is InterpreterCommand.Break) ;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Start()
|
|
|
|
|
{
|
2025-12-03 14:03:19 -06:00
|
|
|
ConnectionStringHelper.CreateFromString(ConnectionString, out _inputStream, out _outputStream);
|
|
|
|
|
|
2025-12-01 00:45:01 -06:00
|
|
|
Thread serverThread = new(() =>
|
|
|
|
|
{
|
|
|
|
|
_ = StartAsync();
|
|
|
|
|
//.ContinueWith(t =>
|
|
|
|
|
//{
|
|
|
|
|
// if (t.Exception is not null)
|
|
|
|
|
// System.Diagnostics.Debug.WriteLine(t.Exception);
|
|
|
|
|
//});
|
|
|
|
|
});
|
|
|
|
|
serverThread.IsBackground = true;
|
|
|
|
|
serverThread.Start();
|
|
|
|
|
}
|
2025-11-30 13:37:33 -06:00
|
|
|
}
|