in src/MICore/Transports/RunInTerminalTransport.cs [40:195]
public override async void Init(ITransportCallback transportCallback, LaunchOptions options, Logger logger, HostWaitLoop waitLoop = null)
{
LocalLaunchOptions localOptions = options as LocalLaunchOptions;
Encoding encNoBom = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false);
string commandPipeName;
string outputPipeName;
string pidPipeName;
List<string> cmdArgs = new List<string>();
string windowtitle = FormattableString.Invariant($"cppdbg: {Path.GetFileName(options.ExePath)}");
if (PlatformUtilities.IsWindows())
{
// Create Windows Named pipes
commandPipeName = Utilities.GetMIEngineTemporaryFilename("In");
outputPipeName = Utilities.GetMIEngineTemporaryFilename("Out");
pidPipeName = Utilities.GetMIEngineTemporaryFilename("Pid");
string errorPipeName = Utilities.GetMIEngineTemporaryFilename("Error");
NamedPipeServerStream inputToDebugger = new NamedPipeServerStream(commandPipeName, PipeDirection.Out, 1, PipeTransmissionMode.Byte);
NamedPipeServerStream outputFromDebugger = new NamedPipeServerStream(outputPipeName, PipeDirection.In, 1, PipeTransmissionMode.Byte);
NamedPipeServerStream errorFromDebugger = new NamedPipeServerStream(errorPipeName, PipeDirection.In, 1, PipeTransmissionMode.Byte);
NamedPipeServerStream pidPipe = new NamedPipeServerStream(pidPipeName, PipeDirection.In, 1, PipeTransmissionMode.Byte);
_pidReader = new StreamReader(pidPipe, encNoBom, false, UnixUtilities.StreamBufferSize);
string thisModulePath = typeof(RunInTerminalTransport).GetTypeInfo().Assembly.ManifestModule.FullyQualifiedName;
string launchCommand = Path.Combine(Path.GetDirectoryName(thisModulePath), "WindowsDebugLauncher.exe");
if (!File.Exists(launchCommand))
{
string errorMessage = string.Format(CultureInfo.CurrentCulture, MICoreResources.Error_InternalFileMissing, launchCommand);
transportCallback.OnStdErrorLine(errorMessage);
transportCallback.OnDebuggerProcessExit(null);
return;
}
cmdArgs.Add(launchCommand);
cmdArgs.Add("--stdin=" + commandPipeName);
cmdArgs.Add("--stdout=" + outputPipeName);
cmdArgs.Add("--stderr=" + errorPipeName);
cmdArgs.Add("--pid=" + pidPipeName);
cmdArgs.Add("--dbgExe=" + localOptions.MIDebuggerPath);
cmdArgs.Add(localOptions.GetMiDebuggerArgs());
_waitForConnection = Task.WhenAll(
inputToDebugger.WaitForConnectionAsync(),
outputFromDebugger.WaitForConnectionAsync(),
errorFromDebugger.WaitForConnectionAsync(),
pidPipe.WaitForConnectionAsync());
_commandStream = new StreamWriter(inputToDebugger, encNoBom);
_outputStream = new StreamReader(outputFromDebugger, encNoBom, false, UnixUtilities.StreamBufferSize);
_errorStream = new StreamReader(errorFromDebugger, encNoBom, false, UnixUtilities.StreamBufferSize);
}
else
{
// Do Linux style pipes
commandPipeName = UnixUtilities.MakeFifo(identifier: "In", logger: logger);
outputPipeName = UnixUtilities.MakeFifo(identifier: "Out", logger: logger);
pidPipeName = UnixUtilities.MakeFifo(identifier: "Pid", logger: logger);
// Create filestreams
FileStream stdInStream = new FileStream(commandPipeName, FileMode.Open);
FileStream stdOutStream = new FileStream(outputPipeName, FileMode.Open);
_pidReader = new StreamReader(new FileStream(pidPipeName, FileMode.Open), encNoBom, false, UnixUtilities.StreamBufferSize);
string debuggerCmd = UnixUtilities.GetDebuggerCommand(localOptions);
// Default working directory is next to the app
string debuggeeDir;
if (Path.IsPathRooted(options.ExePath) && File.Exists(options.ExePath))
{
debuggeeDir = Path.GetDirectoryName(options.ExePath);
}
else
{
// If we don't know where the app is, default to HOME, and if we somehow can't get that, go with the root directory.
debuggeeDir = Environment.GetEnvironmentVariable("HOME");
if (string.IsNullOrEmpty(debuggeeDir))
debuggeeDir = "/";
}
string dbgCmdScript = Path.Combine(Path.GetTempPath(), Utilities.GetMIEngineTemporaryFilename(identifier: "Cmd"));
string launchDebuggerCommand = UnixUtilities.LaunchLocalDebuggerCommand(
debuggeeDir,
commandPipeName,
outputPipeName,
pidPipeName,
dbgCmdScript,
debuggerCmd,
localOptions.GetMiDebuggerArgs());
logger?.WriteTextBlock("DbgCmd:", launchDebuggerCommand);
using (FileStream dbgCmdStream = new FileStream(dbgCmdScript, FileMode.CreateNew))
using (StreamWriter dbgCmdWriter = new StreamWriter(dbgCmdStream, encNoBom) { AutoFlush = true })
{
dbgCmdWriter.WriteLine("#!/usr/bin/env sh");
dbgCmdWriter.Write(launchDebuggerCommand);
dbgCmdWriter.Flush();
}
if (PlatformUtilities.IsOSX())
{
string osxLaunchScript = GetOSXLaunchScript();
// Call osascript with a path to the AppleScript. The apple script takes 2 parameters: a title for the terminal and the launch script.
cmdArgs.Add("/usr/bin/osascript");
cmdArgs.Add(osxLaunchScript);
cmdArgs.Add(FormattableString.Invariant($"\"{windowtitle}\""));
cmdArgs.Add(FormattableString.Invariant($"sh {dbgCmdScript} ;")); // needs a semicolon because this command is running through the launchscript.
}
else
{
cmdArgs.Add("/bin/sh");
cmdArgs.Add(dbgCmdScript);
}
_outputStream = new StreamReader(stdOutStream, encNoBom, false, UnixUtilities.StreamBufferSize);
_commandStream = new StreamWriter(stdInStream, encNoBom);
}
// Do not pass the launchOptions Environment entries as those are used for the debuggee only.
RunInTerminalLauncher launcher = new RunInTerminalLauncher(windowtitle, new List<EnvironmentEntry>(0).AsReadOnly());
launcher.Launch(
cmdArgs,
localOptions.UseExternalConsole,
LaunchSuccess,
(error) =>
{
transportCallback.OnStdErrorLine(error);
throw new InvalidOperationException(error);
},
logger);
logger?.WriteLine("Wait for connection completion.");
if (_waitForConnection != null)
{
// Add a timeout for waiting for connection - 20 seconds
Task waitOrTimeout = Task.WhenAny(_waitForConnection, Task.Delay(20000));
await waitOrTimeout;
if (waitOrTimeout.Status != TaskStatus.RanToCompletion)
{
string errorMessage = String.Format(CultureInfo.CurrentCulture, MICoreResources.Error_DebuggerInitializeFailed_NoStdErr, "WindowsDebugLauncher.exe");
transportCallback.OnStdErrorLine(errorMessage);
transportCallback.OnDebuggerProcessExit(null);
return;
}
}
base.Init(transportCallback, options, logger, waitLoop);
}