Пример #1
0
 public virtual IEventManager Create(IDebugEngineHandler debugEngineHandler,
                                     IBreakpointManager breakpointManager,
                                     IGgpDebugProgram program, SbProcess process,
                                     LldbListenerSubscriber listenerSubscriber)
 {
     return(new LldbEventManager(debugEngineHandler, breakpointManager,
                                 _boundBreakpointEnumFactory, program, process,
                                 listenerSubscriber, _taskContext));
 }
Пример #2
0
 LldbEventManager(IDebugEngineHandler debugEngineHandler,
                  IBreakpointManager breakpointManager,
                  BoundBreakpointEnumFactory boundBreakpointEnumFactory,
                  IGgpDebugProgram program, SbProcess process,
                  LldbListenerSubscriber listenerSubscriber, JoinableTaskContext taskContext)
 {
     _debugEngineHandler         = debugEngineHandler;
     _lldbBreakpointManager      = breakpointManager;
     _boundBreakpointEnumFactory = boundBreakpointEnumFactory;
     _program                = program;
     _lldbProcess            = process;
     _lldbListenerSubscriber = listenerSubscriber;
     _taskContext            = taskContext;
 }
Пример #3
0
        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();
                }
            }
        }
Пример #4
0
            public ILldbAttachedProgram Create(
                IDebugProcess2 debugProcess, Guid programId, IDebugEngine2 debugEngine,
                IDebugEventCallback2 callback, SbDebugger debugger, RemoteTarget target,
                LldbListenerSubscriber listenerSubscriber, SbProcess process,
                SbCommandInterpreter commandInterpreter, bool isCoreAttach,
                IExceptionManager exceptionManager, IModuleSearchLogHolder moduleSearchLogHolder,
                uint remotePid)
            {
                // Required due to an issue triggered by the proxy used to wrap debugProgramFactory.
                // TODO: Remove assertion once the issue with Castle.DynamicProxy is
                // fixed.
                _taskContext.ThrowIfNotOnMainThread();

                var debugEngineHandler = _debugEngineHandlerFactory.Create(debugEngine, callback);

                var binaryLoader     = _binaryLoaderFactory.Create(target);
                var symbolLoader     = _symbolLoaderFactory.Create(commandInterpreter);
                var moduleFileLoader = _moduleFileLoaderFactory.Create(symbolLoader, binaryLoader,
                                                                       moduleSearchLogHolder);

                var debugModuleCache = _debugModuleCacheFactory.Create(
                    (lldbModule, loadOrder, ggpProgram) => _debugModuleFactory.Create(
                        moduleFileLoader, moduleSearchLogHolder, lldbModule, loadOrder,
                        debugEngineHandler, ggpProgram));
                var ad7FrameInfoCreator = new AD7FrameInfoCreator(debugModuleCache);

                var stackFrameCreator = new StackFramesProvider.StackFrameCreator(
                    (frame, thread, program) => _debugStackFrameCreator(
                        ad7FrameInfoCreator, frame, thread, debugEngineHandler, program));
                var threadCreator = new DebugProgram.ThreadCreator(
                    (thread, program) => _debugThreadCreatorDelegate(
                        ad7FrameInfoCreator, stackFrameCreator, thread, program));
                var debugProgram = _debugProgramFactory.Create(
                    debugEngineHandler, threadCreator, debugProcess, programId, process, target,
                    debugModuleCache, isCoreAttach);

                _taskExecutor.StartAsyncTasks(
                    ex => debugEngineHandler.Abort(debugProgram, ExitInfo.Error(ex)));

                var breakpointManager =
                    _breakpointManagerFactory.Create(debugEngineHandler, debugProgram);
                var eventManager =
                    _eventManagerFactory.Create(debugEngineHandler, breakpointManager, debugProgram,
                                                process, listenerSubscriber);

                // TODO: Listen for module load/unload events from LLDB
                binaryLoader.LldbModuleReplaced += (o, args) =>
                {
                    debugModuleCache.GetOrCreate(args.AddedModule, debugProgram);
                    debugModuleCache.Remove(args.RemovedModule);
                };
                debugModuleCache.ModuleAdded += (o, args) =>
                                                debugEngineHandler.OnModuleLoad(args.Module, debugProgram);
                debugModuleCache.ModuleRemoved += (o, args) =>
                                                  debugEngineHandler.OnModuleUnload(args.Module, debugProgram);

                return(new LldbAttachedProgram(breakpointManager, eventManager, _lldbShell,
                                               moduleFileLoader, debugEngineHandler, _taskExecutor,
                                               debugProgram, debugger, target, process,
                                               exceptionManager, debugModuleCache,
                                               listenerSubscriber, remotePid));
            }