public async Task <ILldbAttachedProgram> LaunchAsync( ICancelable task, IDebugProcess2 process, Guid programId, uint?attachPid, DebuggerOptions.DebuggerOptions debuggerOptions, HashSet <string> libPaths, GrpcConnection grpcConnection, int localDebuggerPort, string targetIpAddress, int targetPort, IDebugEventCallback2 callback) { var launchSucceeded = false; Stopwatch launchTimer = Stopwatch.StartNew(); // This should be the first request to the DebuggerGrpcServer. Providing a retry wait // time allows us to connect to a DebuggerGrpcServer that is slow to start. Note that // we postpone sourcing .lldbinit until we are done with our initialization so that // the users can override our defaults. var lldbDebugger = _lldbDebuggerFactory.Create(grpcConnection, false, TimeSpan.FromSeconds(10)); if (lldbDebugger == null) { throw new AttachException(VSConstants.E_ABORT, ErrorStrings.FailedToCreateDebugger); } if (debuggerOptions[DebuggerOption.CLIENT_LOGGING] == DebuggerOptionState.ENABLED) { lldbDebugger.EnableLog("lldb", new List <string> { "default", "module" }); // TODO: Disable 'dwarf' logs until we can determine why this // causes LLDB to hang. // lldbDebugger.EnableLog("dwarf", new List<string> { "default" }); } if (_fastExpressionEvaluation) { lldbDebugger.EnableFastExpressionEvaluation(); } lldbDebugger.SetDefaultLLDBSettings(); // Apply .lldbinit after we set our settings so that the user can override our // defaults with a custom .lldbinit. LoadLocalLldbInit(lldbDebugger); // Add exec search paths, so that LLDB can find the executable and any dependent // libraries. If LLDB is able to find the files locally, it won't try to download // them from the remote server, saving valuable time on attach. foreach (string path in libPaths) { lldbDebugger.SetLibrarySearchPath(path); } lldbDebugger.SetAsync(true); SbPlatform lldbPlatform; switch (_launchOption) { case LaunchOption.AttachToGame: // Fall through. case LaunchOption.LaunchGame: lldbPlatform = CreateRemotePlatform(grpcConnection, lldbDebugger); if (lldbPlatform == null) { throw new AttachException(VSConstants.E_FAIL, ErrorStrings.FailedToCreateLldbPlatform); } task.ThrowIfCancellationRequested(); Trace.WriteLine("Attempting to connect debugger"); task.Progress.Report("Connecting to debugger"); string connectRemoteUrl = $"{_lldbConnectUrl}:{localDebuggerPort}"; string connectRemoteArgument = CreateConnectRemoteArgument(connectRemoteUrl, targetIpAddress, targetPort); SbPlatformConnectOptions lldbConnectOptions = _lldbPlatformConnectOptionsFactory.Create(connectRemoteArgument); IAction debugerWaitAction = _actionRecorder.CreateToolAction(ActionType.DebugWaitDebugger); bool TryConnectRemote() { if (lldbPlatform.ConnectRemote(lldbConnectOptions).Success()) { return(true); } VerifyGameIsReady(debugerWaitAction); return(false); } try { debugerWaitAction.Record(() => RetryWithTimeout( task, TryConnectRemote, _launchRetryDelay, _launchTimeout, launchTimer)); } catch (TimeoutException e) { throw new AttachException( VSConstants.E_ABORT, ErrorStrings.FailedToConnectDebugger(lldbConnectOptions.GetUrl()), e); } Trace.WriteLine("LLDB successfully connected"); break; case LaunchOption.AttachToCore: lldbPlatform = _lldbPlatformFactory.Create(_localLldbPlatformName, grpcConnection); if (lldbPlatform == null) { throw new AttachException(VSConstants.E_FAIL, ErrorStrings.FailedToCreateLldbPlatform); } break; default: throw new AttachException(VSConstants.E_ABORT, ErrorStrings.InvalidLaunchOption( _launchOption.ToString())); } lldbDebugger.SetSelectedPlatform(lldbPlatform); task.ThrowIfCancellationRequested(); task.Progress.Report("Debugger is attaching (this can take a while)"); RemoteTarget lldbTarget = null; if (_launchOption == LaunchOption.LaunchGame && !string.IsNullOrEmpty(_executableFullPath)) { var createExecutableTargetAction = _actionRecorder.CreateToolAction(ActionType.DebugCreateExecutableTarget); createExecutableTargetAction.Record( () => lldbTarget = CreateTarget(lldbDebugger, _executableFullPath)); } else { lldbTarget = CreateTarget(lldbDebugger, ""); } var lldbListener = CreateListener(grpcConnection); // This is required to catch breakpoint change events. lldbTarget.AddListener(lldbListener, EventType.STATE_CHANGED); var listenerSubscriber = new LldbListenerSubscriber(lldbListener); var eventHandler = new EventHandler <FileUpdateReceivedEventArgs>( (s, e) => ListenerSubscriberOnFileUpdateReceived(task, e)); listenerSubscriber.FileUpdateReceived += eventHandler; listenerSubscriber.Start(); try { if (_launchOption == LaunchOption.AttachToCore) { var loadCoreAction = _actionRecorder.CreateToolAction(ActionType.DebugLoadCore); SbProcess lldbDebuggerProcess = null; loadCoreAction.Record(() => lldbDebuggerProcess = LoadCore(lldbTarget, loadCoreAction)); await _taskContext.Factory.SwitchToMainThreadAsync(); return(_attachedProgramFactory.Create( process, programId, _debugEngine, callback, lldbDebugger, lldbTarget, listenerSubscriber, lldbDebuggerProcess, lldbDebugger.GetCommandInterpreter(), true, new NullExceptionManager(), _moduleSearchLogHolder, remotePid: 0)); } // Get process ID. uint processId = 0; switch (_launchOption) { case LaunchOption.AttachToGame: if (!attachPid.HasValue) { throw new AttachException(VSConstants.E_ABORT, ErrorStrings.FailedToRetrieveProcessId); } processId = attachPid.Value; break; case LaunchOption.LaunchGame: // Since we have no way of knowing when the remote process actually // starts, try a few times to get the pid. IAction debugWaitAction = _actionRecorder.CreateToolAction(ActionType.DebugWaitProcess); bool TryGetRemoteProcessId() { if (GetRemoteProcessId(_executableFileName, lldbPlatform, out processId)) { return(true); } VerifyGameIsReady(debugWaitAction); return(false); } try { debugWaitAction.Record(() => RetryWithTimeout( task, TryGetRemoteProcessId, _launchRetryDelay, _launchTimeout, launchTimer)); } catch (TimeoutException e) { throw new AttachException(VSConstants.E_ABORT, ErrorStrings.FailedToRetrieveProcessId, e); } break; } Trace.WriteLine("Attaching to pid " + processId); var debugAttachAction = _actionRecorder.CreateToolAction(ActionType.DebugAttach); SbProcess debuggerProcess = null; debugAttachAction.Record(() => { var moduleFileLoadRecorder = _moduleFileLoadRecorderFactory.Create(debugAttachAction); moduleFileLoadRecorder.RecordBeforeLoad(Array.Empty <SbModule>()); debuggerProcess = lldbTarget.AttachToProcessWithID(lldbListener, processId, out SbError lldbError); if (lldbError.Fail()) { throw new AttachException( VSConstants.E_ABORT, GetLldbAttachErrorDetails(lldbError, lldbPlatform, processId)); } RecordModules(lldbTarget, moduleFileLoadRecorder); }); var exceptionManager = _exceptionManagerFactory.Create(debuggerProcess); await _taskContext.Factory.SwitchToMainThreadAsync(); ILldbAttachedProgram attachedProgram = _attachedProgramFactory.Create( process, programId, _debugEngine, callback, lldbDebugger, lldbTarget, listenerSubscriber, debuggerProcess, lldbDebugger.GetCommandInterpreter(), false, exceptionManager, _moduleSearchLogHolder, processId); launchSucceeded = true; return(attachedProgram); } finally { // clean up the SBListener subscriber listenerSubscriber.FileUpdateReceived -= eventHandler; // stop the SBListener subscriber completely if the game failed to launch if (!launchSucceeded) { listenerSubscriber.Stop(); } } }