Skip to content

Commit

Permalink
Interprocess debugger now uses named pipes instead of anonymous pipes.
Browse files Browse the repository at this point in the history
  • Loading branch information
arklumpus committed Feb 19, 2021
1 parent a993e04 commit e2534f8
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 30 deletions.
2 changes: 1 addition & 1 deletion CSharpEditor/CSharpEditor.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<Company>University of Bristol</Company>
<Description>A C# source code editor with syntax highlighting, intelligent code completion and real-time compilation error checking.</Description>
<PackageLicenseExpression>GPL-3.0-only</PackageLicenseExpression>
<Version>1.0.1</Version>
<Version>1.1.0</Version>
<PackageIcon>icon.png</PackageIcon>
<PackageProjectUrl>https://github.com/arklumpus/CSharpEditor</PackageProjectUrl>
</PropertyGroup>
Expand Down
102 changes: 73 additions & 29 deletions CSharpEditor/InterprocessDebugger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,12 @@ public class InterprocessDebuggerServer : IDisposable
{
private Process ClientProcess;
private string ClientExePath;
AnonymousPipeServerStream PipeServerOut;
AnonymousPipeServerStream PipeServerIn;
private List<string> InitialArguments;
NamedPipeServerStream PipeServerOut;
NamedPipeServerStream PipeServerIn;
StreamWriter PipeServerOutWriter;
StreamReader PipeServerInReader;
private Func<int, int> GetClientPid;
private bool disposedValue;

/// <summary>
Expand All @@ -52,39 +54,84 @@ public class InterprocessDebuggerServer : IDisposable
/// <param name="clientExePath">The path to the executable of the client process.</param>
public InterprocessDebuggerServer(string clientExePath)
{
this.InitializeServer(clientExePath);
this.InitializeServer(clientExePath, new List<string>(), null);
}

private void InitializeServer(string clientExePath)
/// <summary>
/// Initializes a new <see cref="InterprocessDebuggerServer"/>, starting the client process and establishing pipes to communicate with it.
/// </summary>
/// <param name="clientExePath">The path to the executable of the client process.</param>
/// <param name="initialArguments">The arguments that will be used to start the client process. The additional arguments specific to the <see cref="InterprocessDebuggerServer"/> will be appended after these.</param>
public InterprocessDebuggerServer(string clientExePath, IEnumerable<string> initialArguments)
{
this.InitializeServer(clientExePath, initialArguments.ToList(), null);
}

/// <summary>
/// Initializes a new <see cref="InterprocessDebuggerServer"/>, starting the client process and establishing pipes to communicate with it.
/// </summary>
/// <param name="clientExePath">The path to the executable of the client process.</param>
/// <param name="initialArguments">The arguments that will be used to start the client process. The additional arguments specific to the <see cref="InterprocessDebuggerServer"/> will be appended after these.</param>
/// <param name="getClientPid">A method that returns the process identifier (PID) of the client debugger process. The argument of this method is the PID of the process that has been started by the server. If this is <see langword="null"/>, it is assumed that the process started by the server is the client debugger process.</param>
public InterprocessDebuggerServer(string clientExePath, IEnumerable<string> initialArguments, Func<int, int> getClientPid)
{
this.InitializeServer(clientExePath, initialArguments.ToList(), getClientPid);
}

private void InitializeServer(string clientExePath, List<string> initialArguments, Func<int, int> getClientPid)
{
ClientExePath = clientExePath;

ClientProcess = new Process();
ClientProcess.StartInfo.FileName = clientExePath;

PipeServerOut = new AnonymousPipeServerStream(PipeDirection.Out, HandleInheritability.Inheritable);
PipeServerIn = new AnonymousPipeServerStream(PipeDirection.In, HandleInheritability.Inheritable);
InitialArguments = initialArguments;

ClientProcess.StartInfo.Arguments = Process.GetCurrentProcess().Id.ToString() + " " + PipeServerOut.GetClientHandleAsString() + " " + PipeServerIn.GetClientHandleAsString();
string pipeNameOut = Guid.NewGuid().ToString("N");
string pipeNameIn = Guid.NewGuid().ToString("N");

ClientProcess.StartInfo.UseShellExecute = false;
ClientProcess.Start();
PipeServerOut?.Dispose();
PipeServerIn?.Dispose();

PipeServerOut = new NamedPipeServerStream(pipeNameOut, PipeDirection.Out);
PipeServerIn = new NamedPipeServerStream(pipeNameIn, PipeDirection.In);

PipeServerOut.DisposeLocalCopyOfClientHandle();
PipeServerIn.DisposeLocalCopyOfClientHandle();
PipeServerOutWriter = new StreamWriter(PipeServerOut);
PipeServerInReader = new StreamReader(PipeServerIn);

foreach (string arg in initialArguments)
{
ClientProcess.StartInfo.ArgumentList.Add(arg);
}

ClientProcess.StartInfo.ArgumentList.Add(Process.GetCurrentProcess().Id.ToString());
ClientProcess.StartInfo.ArgumentList.Add(pipeNameOut);
ClientProcess.StartInfo.ArgumentList.Add(pipeNameIn);

ClientProcess.StartInfo.UseShellExecute = false;
ClientProcess.Start();

PipeServerOut.WaitForConnection();
PipeServerIn.WaitForConnection();

this.GetClientPid = getClientPid;

if (GetClientPid != null)
{
int newPid = GetClientPid(ClientProcess.Id);

ClientProcess = Process.GetProcessById(newPid);
}

string guid = System.Guid.NewGuid().ToString("N");
PipeServerOutWriter.WriteLine(guid);
PipeServerOutWriter.Flush();
//PipeServerOut.WaitForPipeDrain();
PipeServerOutWriter.Flush();

string input = PipeServerInReader.ReadLine();

if (input != guid)
{
throw new ApplicationException("The client debugger process answered incorrectly!");
throw new ApplicationException("The client debugger process answered incorrectly!\n" + input);
}
}

Expand All @@ -104,11 +151,10 @@ public Func<BreakpointInfo, bool> SynchronousBreak(Editor editor)
{
if (ClientProcess.HasExited)
{
InitializeServer(ClientExePath);
InitializeServer(ClientExePath, InitialArguments, GetClientPid);
}

PipeServerOutWriter.WriteLine("Init");
//PipeServerOut.WaitForPipeDrain();

Dictionary<string, object> objectCache = new Dictionary<string, object>();
Dictionary<string, (string, VariableTypes, string)> localVariables = new Dictionary<string, (string, VariableTypes, string)>();
Expand Down Expand Up @@ -140,7 +186,6 @@ public Func<BreakpointInfo, bool> SynchronousBreak(Editor editor)

PipeServerOutWriter.WriteLine(message);
PipeServerOutWriter.Flush();
//PipeServerOut.WaitForPipeDrain();

while (!ClientProcess.HasExited)
{
Expand Down Expand Up @@ -169,7 +214,6 @@ public Func<BreakpointInfo, bool> SynchronousBreak(Editor editor)

PipeServerOutWriter.WriteLine(JsonSerializer.Serialize(items));
PipeServerOutWriter.Flush();
//PipeServerOut.WaitForPipeDrain();
}
else if (inputMessage[0] == "GetProperty")
{
Expand Down Expand Up @@ -203,7 +247,6 @@ public Func<BreakpointInfo, bool> SynchronousBreak(Editor editor)

PipeServerOutWriter.WriteLine(JsonSerializer.Serialize(new string[] { guid, JsonSerializer.Serialize(variableType), valueJSON }));
PipeServerOutWriter.Flush();
//PipeServerOut.WaitForPipeDrain();
}
else if (inputMessage[0] == "Resume")
{
Expand Down Expand Up @@ -409,8 +452,8 @@ public void Dispose()
/// </summary>
public class InterprocessDebuggerClient : UserControl, IDisposable
{
readonly AnonymousPipeClientStream PipeClientIn;
readonly AnonymousPipeClientStream PipeClientOut;
readonly NamedPipeClientStream PipeClientIn;
readonly NamedPipeClientStream PipeClientOut;
readonly StreamReader PipeClientInReader;
readonly StreamWriter PipeClientOutWriter;
readonly Process ParentProcess;
Expand Down Expand Up @@ -456,17 +499,21 @@ public InterprocessDebuggerClient(string[] args)
};
}

PipeClientIn = new AnonymousPipeClientStream(PipeDirection.In, args[1]);
PipeClientInReader = new StreamReader(PipeClientIn);
PipeClientIn = new NamedPipeClientStream(".", args[1], PipeDirection.In);

PipeClientOut = new NamedPipeClientStream(".", args[2], PipeDirection.Out);

PipeClientOut = new AnonymousPipeClientStream(PipeDirection.Out, args[2]);
PipeClientOutWriter = new StreamWriter(PipeClientOut);
PipeClientIn.Connect();
PipeClientOut.Connect();

PipeClientOutWriter = new StreamWriter(PipeClientOut);
PipeClientInReader = new StreamReader(PipeClientIn);


string message = PipeClientInReader.ReadLine();

PipeClientOutWriter.WriteLine(message);
PipeClientOutWriter.Flush();
//PipeClientOut.WaitForPipeDrain();
}

/// <summary>
Expand Down Expand Up @@ -547,7 +594,6 @@ protected override async void OnAttachedToLogicalTree(LogicalTreeAttachmentEvent
{
PipeClientOutWriter.WriteLine(JsonSerializer.Serialize(new string[] { "GetProperty", variableId, propertyName, isProperty.ToString() }));
PipeClientOutWriter.Flush();
//PipeClientOut.WaitForPipeDrain();

string message = PipeClientInReader.ReadLine();

Expand All @@ -572,7 +618,6 @@ protected override async void OnAttachedToLogicalTree(LogicalTreeAttachmentEvent
{
PipeClientOutWriter.WriteLine(JsonSerializer.Serialize(new string[] { "GetItems", variableId }));
PipeClientOutWriter.Flush();
//PipeClientOut.WaitForPipeDrain();

string message = PipeClientInReader.ReadLine();

Expand Down Expand Up @@ -609,7 +654,6 @@ protected override async void OnAttachedToLogicalTree(LogicalTreeAttachmentEvent
bool shouldSuppress = await Editor.AsynchronousBreak(info);
PipeClientOutWriter.WriteLine(JsonSerializer.Serialize(new string[] { "Resume", shouldSuppress.ToString() }));
PipeClientOutWriter.Flush();
//PipeClientOut.WaitForPipeDrain();
BreakpointResumed?.Invoke(this, new EventArgs());
}
}
Expand Down

0 comments on commit e2534f8

Please sign in to comment.