async Task <CreateLaunchResult> CreateLaunchAsync(LaunchParams launchParams, ICancelable cancelable, IAction action) { Task <string> sdkCompatibilityTask = CheckSdkCompatibilityAsync( launchParams.GameletName, launchParams.SdkVersion, launchParams.GameletSdkVersion, action); LaunchGameRequest launchRequest = null; Task <ConfigStatus> parsingTask = Task.Run(() => _launchGameParamsConverter.ToLaunchGameRequest( launchParams, out launchRequest)); cancelable.ThrowIfCancellationRequested(); ConfigStatus parsingState = await parsingTask; if (parsingState.IsErrorLevel) { // Critical error occurred while parsing the configuration. // Launch can not proceed. throw new ConfigurationException(parsingState.ErrorMessage); } cancelable.ThrowIfCancellationRequested(); string sdkCompatibilityMessage = await sdkCompatibilityTask; cancelable.ThrowIfCancellationRequested(); var devEvent = new DeveloperLogEvent { GameLaunchData = new GameLaunchData { RequestId = launchRequest.RequestId } }; // Updating the event to record the RequestId in case LaunchGameAsync throws exception. action.UpdateEvent(devEvent); LaunchGameResponse response = await _gameletClient.LaunchGameAsync(launchRequest, action); IVsiGameLaunch vsiLaunch = _vsiLaunchFactory.Create(response.GameLaunchName, launchRequest.EnableDeveloperResumeOffer); devEvent.GameLaunchData.LaunchId = vsiLaunch.LaunchId; action.UpdateEvent(devEvent); parsingState.CompressMessages(); return(new CreateLaunchResult(vsiLaunch, parsingState.WarningMessage, sdkCompatibilityMessage)); }
public async Task SyncAsync(SshTarget target, string localPath, string remotePath, ICancelable task, bool force = false) { if (string.IsNullOrWhiteSpace(localPath)) { throw new ArgumentNullException(nameof(localPath), "Local path should be specified when running ggp_rsync"); } if (string.IsNullOrWhiteSpace(remotePath)) { throw new ArgumentNullException(nameof(remotePath), "Remote path should be specified when running ggp_rsync"); } ProcessManager processManager = ProcessManager.CreateForCancelableTask(task); ProcessStartInfo startInfo = BuildForGgpSync(target, localPath, remotePath, force); using (IProcess process = _remoteProcessFactory.Create(startInfo, int.MaxValue)) { processManager.AddProcess(process); process.OutputDataReceived += (sender, args) => { task.ThrowIfCancellationRequested(); if (string.IsNullOrWhiteSpace(args.Text)) { return; } string data = args.Text.Trim(); task.Progress.Report(data); }; process.ErrorDataReceived += (sender, args) => { task.ThrowIfCancellationRequested(); if (string.IsNullOrWhiteSpace(args.Text)) { return; } string data = args.Text; throw new ProcessException(data); }; await process.RunToExitWithSuccessAsync(); } // Notify client if operation was cancelled. task.ThrowIfCancellationRequested(); }
public async Task GetAsync(SshTarget target, string file, string destination, ICancelable task) { await ScpAsync(ProcessStartInfoBuilder.BuildForScpGet(file, target, destination), ProcessManager.CreateForCancelableTask(task)); // Notify client if operation was cancelled. task.ThrowIfCancellationRequested(); }
/// <summary> /// Run action repeatedly until it returns true, sleeping for the retry delay between /// retry attempts. /// </summary> /// <remarks>The action is attempted at least once, even if the timeout has been /// exceeded. /// </remarks> /// <exception cref="System.TimeoutException">Thrown when timer elapsed /// time exceeds the timeout. /// </exception> /// <exception cref="OperationCanceledException">Thrown when the task is canceled while /// sleeping. /// </exception> static void RetryWithTimeout(ICancelable task, Func <bool> action, TimeSpan retryDelay, TimeSpan timeout, Stopwatch timer) { while (!action()) { if (timer.Elapsed > timeout) { throw new TimeoutException($"Timeout exceeded: {timeout.TotalMilliseconds}ms"); } Trace.WriteLine($"Retrying in {retryDelay.TotalMilliseconds}ms"); Thread.Sleep((int)retryDelay.TotalMilliseconds); task.ThrowIfCancellationRequested(); } }
// Polling statuses until we see RunningGame or GameLaunchEnded. IncompleteLaunch, // ReadyToPlay and DelayedLaunch are transitioning states. async Task PollForLaunchStatusAsync(ICancelable task, IAction action) { int maxPollCount = (_isDeveloperResumeOfferEnabled ? _pollingTimeoutResumeOfferMs : _pollingTimeoutMs) / _pollDelayMs; int currentPollCount = 0; var devEvent = new DeveloperLogEvent { GameLaunchData = new GameLaunchData { LaunchId = LaunchId } }; action.UpdateEvent(devEvent); while (++currentPollCount <= maxPollCount) { task.ThrowIfCancellationRequested(); GgpGrpc.Models.GameLaunch launch = await GetLaunchStateAsync(action); if (launch.GameLaunchState == GameLaunchState.RunningGame) { return; } if (launch.GameLaunchState == GameLaunchState.GameLaunchEnded) { string error = LaunchUtils.GetEndReason(launch.GameLaunchEnded, launch.GameletName); devEvent.GameLaunchData.EndReason = (int)launch.GameLaunchEnded.EndReason; action.UpdateEvent(devEvent); throw new GameLaunchFailError(error); } await Task.Delay(_pollDelayMs); } if (currentPollCount > maxPollCount) { throw new TimeoutException(ErrorStrings.LaunchEndedTimeout); } }
public async Task <DeleteLaunchResult> WaitUntilGameLaunchEndedAsync( string gameLaunchName, ICancelable task, IAction action) { GgpGrpc.Models.GameLaunch launch = await _gameletClient.GetGameLaunchStateAsync(gameLaunchName, action); int maxPollCount = _pollingTimeoutMs / _pollDelayMs; int currentPollCount = 0; while (launch.GameLaunchState != GameLaunchState.GameLaunchEnded && ++currentPollCount <= maxPollCount) { task.ThrowIfCancellationRequested(); await Task.Delay(_pollDelayMs); launch = await _gameletClient.GetGameLaunchStateAsync(gameLaunchName, action); } return(new DeleteLaunchResult( launch, launch.GameLaunchState == GameLaunchState.GameLaunchEnded)); }
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(); } } }
public async Task <int> LoadModuleFilesAsync( IList <SbModule> modules, SymbolInclusionSettings symbolSettings, bool useSymbolStores, ICancelable task, IModuleFileLoadMetricsRecorder moduleFileLoadRecorder) { if (modules == null) { throw new ArgumentNullException(nameof(modules)); } if (task == null) { throw new ArgumentNullException(nameof(task)); } if (moduleFileLoadRecorder == null) { throw new ArgumentNullException(nameof(moduleFileLoadRecorder)); } // Add some metrics to the event proto before attempting to load symbols, so that they // are still recorded if the task is aborted or cancelled. moduleFileLoadRecorder.RecordBeforeLoad(modules); int result = VSConstants.S_OK; for (int i = 0; i < modules.Count; ++i) { SbModule module = modules[i]; TextWriter searchLog = new StringWriter(); string name = module.GetPlatformFileSpec()?.GetFilename() ?? "<unknown>"; try { task.ThrowIfCancellationRequested(); if (SkipModule(name, symbolSettings)) { await searchLog.WriteLineAsync( SymbolInclusionSettings.ModuleExcludedMessage); continue; } task.Progress.Report($"Loading binary for {name} ({i}/{modules.Count})"); (SbModule newModule, bool ok) = await _binaryLoader.LoadBinaryAsync(module, searchLog); if (!ok) { result = VSConstants.E_FAIL; continue; } module = newModule; task.ThrowIfCancellationRequested(); task.Progress.Report($"Loading symbols for {name} ({i}/{modules.Count})"); var loaded = await _symbolLoader.LoadSymbolsAsync(module, searchLog, useSymbolStores); if (!loaded) { result = VSConstants.E_FAIL; continue; } } finally { _moduleSearchLogHolder.SetSearchLog(module, searchLog.ToString()); modules[i] = module; } } moduleFileLoadRecorder.RecordAfterLoad(modules); return(result); }