in src/OpenDebugAD7/AD7DebugSession.cs [930:1118]
protected override void HandleLaunchRequestAsync(IRequestResponder<LaunchArguments> responder)
{
const string telemetryEventName = DebuggerTelemetry.TelemetryLaunchEventName;
int hr;
DateTime launchStartTime = DateTime.Now;
string mimode = responder.Arguments.ConfigurationProperties.GetValueAsString("MIMode");
string program = responder.Arguments.ConfigurationProperties.GetValueAsString("program")?.Trim();
if (string.IsNullOrEmpty(program))
{
responder.SetError(CreateProtocolExceptionAndLogTelemetry(telemetryEventName, 1001, "launch: property 'program' is missing or empty"));
return;
}
// If program is still in the default state, raise error
if (program.EndsWith(">", StringComparison.Ordinal) && program.Contains('<', StringComparison.Ordinal))
{
responder.SetError(CreateProtocolExceptionAndLogTelemetry(telemetryEventName, 1001, "launch: launch.json must be configured. Change 'program' to the path to the executable file that you would like to debug."));
return;
}
// Should not have a pid in launch
if (responder.Arguments.ConfigurationProperties.ContainsKey("processId"))
{
responder.SetError(CreateProtocolExceptionAndLogTelemetry(telemetryEventName, 1001, "launch: The parameter: 'processId' should not be specified on Launch. Please use request type: 'attach'"));
return;
}
JToken pipeTransport = responder.Arguments.ConfigurationProperties.GetValueAsObject("pipeTransport");
string miDebuggerServerAddress = responder.Arguments.ConfigurationProperties.GetValueAsString("miDebuggerServerAddress");
// Pipe trasport can talk to remote machines so paths and files should not be checked in this case.
bool skipFilesystemChecks = (pipeTransport != null || miDebuggerServerAddress != null);
// For a remote scenario, we assume whatever input user has provided is correct.
// The target remote could be any OS, so we don't try to change anything.
if (!skipFilesystemChecks)
{
if (!ValidateProgramPath(ref program, mimode))
{
responder.SetError(CreateProtocolExceptionAndLogTelemetry(telemetryEventName, 1002, String.Format(CultureInfo.CurrentCulture, "launch: program '{0}' does not exist", program)));
return;
}
}
string workingDirectory = responder.Arguments.ConfigurationProperties.GetValueAsString("cwd");
if (string.IsNullOrEmpty(workingDirectory))
{
responder.SetError(CreateProtocolExceptionAndLogTelemetry(telemetryEventName, 1003, "launch: property 'cwd' is missing or empty"));
return;
}
if (!skipFilesystemChecks)
{
workingDirectory = m_pathConverter.ConvertLaunchPathForVsCode(workingDirectory);
if (!Directory.Exists(workingDirectory))
{
responder.SetError(CreateProtocolExceptionAndLogTelemetry(telemetryEventName, 1004, String.Format(CultureInfo.CurrentCulture, "launch: workingDirectory '{0}' does not exist", workingDirectory)));
return;
}
}
SetCommonDebugSettings(responder.Arguments.ConfigurationProperties);
bool success = false;
try
{
lock (m_lock)
{
Debug.Assert(m_currentLaunchState == null, "Concurrent launches??");
m_currentLaunchState = new CurrentLaunchState();
}
var eb = new ErrorBuilder(() => AD7Resources.Error_Scenario_Launch);
// Don't convert the workingDirectory string if we are a pipeTransport connection. We are assuming that the user has the correct directory separaters for their target OS
string workingDirectoryString = pipeTransport != null ? workingDirectory : m_pathConverter.ConvertClientPathToDebugger(workingDirectory);
m_sessionConfig.StopAtEntrypoint = responder.Arguments.ConfigurationProperties.GetValueAsBool("stopAtEntry").GetValueOrDefault(false);
m_processId = Constants.InvalidProcessId;
m_processName = program;
enum_LAUNCH_FLAGS flags = enum_LAUNCH_FLAGS.LAUNCH_DEBUG;
if (responder.Arguments.NoDebug.GetValueOrDefault(false))
{
flags = enum_LAUNCH_FLAGS.LAUNCH_NODEBUG;
}
SetCommonMISettings(responder.Arguments.ConfigurationProperties);
string launchJson = JsonConvert.SerializeObject(responder.Arguments.ConfigurationProperties);
// Then attach
hr = m_engineLaunch.LaunchSuspended(null,
m_port,
program,
null,
null,
null,
launchJson,
flags,
0,
0,
0,
this,
out m_process);
if (hr != HRConstants.S_OK)
{
// If the engine raised a message via an error event, fire that instead
if (hr == HRConstants.E_ABORT)
{
string message;
lock (m_lock)
{
message = m_currentLaunchState?.CurrentError?.Item2;
m_currentLaunchState = null;
}
if (message != null)
{
responder.SetError(CreateProtocolExceptionAndLogTelemetry(telemetryEventName, 1005, message));
return;
}
}
eb.ThrowHR(hr);
}
hr = m_engineLaunch.ResumeProcess(m_process);
if (hr < 0)
{
// try to terminate the process if we can
try
{
m_engineLaunch.TerminateProcess(m_process);
}
catch
{
// Ignore failures since we are already dealing with an error
}
eb.ThrowHR(hr);
}
var properties = new Dictionary<string, object>(StringComparer.Ordinal);
if (flags.HasFlag(enum_LAUNCH_FLAGS.LAUNCH_NODEBUG))
{
properties.Add(DebuggerTelemetry.TelemetryIsNoDebug, true);
}
properties.Add(DebuggerTelemetry.TelemetryMIMode, mimode);
properties.Add(DebuggerTelemetry.TelemetryFrameworkVersion, GetFrameworkVersionAttributeValue());
DebuggerTelemetry.ReportTimedEvent(telemetryEventName, DateTime.Now - launchStartTime, properties);
success = true;
}
catch (Exception e)
{
// Instead of failing to launch with the exception, try and wrap it better so that the information is useful for the user.
responder.SetError(CreateProtocolExceptionAndLogTelemetry(telemetryEventName, 1007, string.Format(CultureInfo.CurrentCulture, AD7Resources.Error_ExceptionOccured, e.InnerException?.ToString() ?? e.ToString())));
return;
}
finally
{
// Clear _currentLaunchState
CurrentLaunchState currentLaunchState;
lock (m_lock)
{
currentLaunchState = m_currentLaunchState;
m_currentLaunchState = null;
}
if (!success)
{
m_process = null;
}
// If we had an error event that we didn't wind up returning as an exception, raise it as an event
Tuple<MessagePrefix, string> currentError = currentLaunchState?.CurrentError;
if (currentError != null)
{
SendMessageEvent(currentError.Item1, currentError.Item2);
}
}
responder.SetResponse(new LaunchResponse());
}