Пример #1
0
        private void OnStartDebuggingFailed(Exception exception)
        {
            Logger.Flush();
            SendStartDebuggingError(exception);

            Dispose();
        }
Пример #2
0
        public WorkerThread(Logger logger)
        {
            Logger = logger;
            _opSet = new AutoResetEvent(false);
            _runningOpCompleteEvent = new ManualResetEvent(true);
            _postedOperations       = new Queue <Operation>();

            _thread      = new Thread(new ThreadStart(ThreadFunc));
            _thread.Name = "MIDebugger.PollThread";
            _thread.Start();
        }
Пример #3
0
        public WorkerThread(Logger logger)
        {
            Logger = logger;
            _opSet = new AutoResetEvent(false);
            _runningOpCompleteEvent = new ManualResetEvent(true);
            _postedOperations = new Queue<Operation>();

            _thread = new Thread(new ThreadStart(ThreadFunc));
            _thread.Name = "MIDebugger.PollThread";
            _thread.Start();
        }
Пример #4
0
        void IPlatformAppLauncher.Initialize(HostConfigurationStore configStore, IDeviceAppLauncherEventCallback eventCallback)
        {
            if (configStore == null)
                throw new ArgumentNullException("configStore");
            if (eventCallback == null)
                throw new ArgumentNullException("eventCallback");

            _eventCallback = eventCallback;
            RegistryRoot.Set(configStore.RegistryRoot);
            Logger = MICore.Logger.EnsureInitialized(configStore);
        }
Пример #5
0
        void IPlatformAppLauncher.Initialize(HostConfigurationStore configStore, IDeviceAppLauncherEventCallback eventCallback)
        {
            if (configStore == null)
            {
                throw new ArgumentNullException("configStore");
            }
            if (eventCallback == null)
            {
                throw new ArgumentNullException("eventCallback");
            }

            _eventCallback = eventCallback;
            RegistryRoot.Set(configStore.RegistryRoot);
            Logger = MICore.Logger.EnsureInitialized(configStore);
        }
Пример #6
0
        // Launches a process by means of the debug engine.
        // Normally, Visual Studio launches a program using the IDebugPortEx2::LaunchSuspended method and then attaches the debugger
        // to the suspended program. However, there are circumstances in which the debug engine may need to launch a program
        // (for example, if the debug engine is part of an interpreter and the program being debugged is an interpreted language),
        // in which case Visual Studio uses the IDebugEngineLaunch2::LaunchSuspended method
        // The IDebugEngineLaunch2::ResumeProcess method is called to start the process after the process has been successfully launched in a suspended state.
        int IDebugEngineLaunch2.LaunchSuspended(string pszServer, IDebugPort2 port, string exe, string args, string dir, string env, string options, enum_LAUNCH_FLAGS launchFlags, uint hStdInput, uint hStdOutput, uint hStdError, IDebugEventCallback2 ad7Callback, out IDebugProcess2 process)
        {
            Debug.Assert(_pollThread == null);
            Debug.Assert(_engineCallback == null);
            Debug.Assert(_debuggedProcess == null);
            Debug.Assert(_ad7ProgramId == Guid.Empty);

            // Check if the logger was enabled late.
            Logger.LoadMIDebugLogger(_configStore);

            process = null;

            _engineCallback = new EngineCallback(this, ad7Callback);

            Exception exception;

            try
            {
                bool noDebug = launchFlags.HasFlag(enum_LAUNCH_FLAGS.LAUNCH_NODEBUG);

                // Note: LaunchOptions.GetInstance can be an expensive operation and may push a wait message loop
                LaunchOptions launchOptions = LaunchOptions.GetInstance(_configStore, exe, args, dir, options, noDebug, _engineCallback, TargetEngine.Native, Logger);

                StartDebugging(launchOptions);

                EngineUtils.RequireOk(port.GetProcess(_debuggedProcess.Id, out process));
                return(Constants.S_OK);
            }
            catch (Exception e) when(ExceptionHelper.BeforeCatch(e, Logger, reportOnlyCorrupting: true))
            {
                exception = e;
                // Return from the catch block so that we can let the exception unwind - the stack can get kind of big
            }

            // If we just return the exception as an HRESULT, we will lose our message, so we instead send up an error event, and then
            // return E_ABORT.
            OnStartDebuggingFailed(exception);

            return(Constants.E_ABORT);
        }
Пример #7
0
 // Sets the registry root currently in use by the DE. Different installations of Visual Studio can change where their registry information is stored
 // This allows the debugger to tell the engine where that location is.
 public int SetRegistryRoot(string registryRoot)
 {
     _configStore = new HostConfigurationStore(registryRoot);
     Logger       = Logger.EnsureInitialized(_configStore);
     return(Constants.S_OK);
 }
Пример #8
0
        // Attach the debug engine to a program.
        public int Attach(IDebugProgram2[] portProgramArray, IDebugProgramNode2[] programNodeArray, uint celtPrograms, IDebugEventCallback2 ad7Callback, enum_ATTACH_REASON dwReason)
        {
            Debug.Assert(_ad7ProgramId == Guid.Empty);

            Logger.LoadMIDebugLogger(_configStore);

            if (celtPrograms != 1)
            {
                Debug.Fail("SampleEngine only expects to see one program in a process");
                throw new ArgumentException();
            }
            IDebugProgram2 portProgram = portProgramArray[0];

            Exception exception = null;

            try
            {
                IDebugProcess2 process;
                EngineUtils.RequireOk(portProgram.GetProcess(out process));

                AD_PROCESS_ID processId = EngineUtils.GetProcessId(process);

                EngineUtils.RequireOk(portProgram.GetProgramId(out _ad7ProgramId));

                // Attach can either be called to attach to a new process, or to complete an attach
                // to a launched process
                if (_pollThread == null)
                {
                    if (processId.ProcessIdType != (uint)enum_AD_PROCESS_ID.AD_PROCESS_ID_SYSTEM)
                    {
                        Debug.Fail("Invalid process to attach to");
                        throw new ArgumentException();
                    }

                    IDebugPort2 port;
                    EngineUtils.RequireOk(process.GetPort(out port));

                    Debug.Assert(_engineCallback == null);
                    Debug.Assert(_debuggedProcess == null);

                    _engineCallback = new EngineCallback(this, ad7Callback);
                    LaunchOptions launchOptions = CreateAttachLaunchOptions(processId.dwProcessId, port);
                    if (port is IDebugUnixShellPort)
                    {
                        _unixPort = (IDebugUnixShellPort)port;
                    }
                    StartDebugging(launchOptions);
                }
                else
                {
                    if (!EngineUtils.ProcIdEquals(processId, _debuggedProcess.Id))
                    {
                        Debug.Fail("Asked to attach to a process while we are debugging");
                        return(Constants.E_FAIL);
                    }
                }

                AD7EngineCreateEvent.Send(this);
                AD7ProgramCreateEvent.Send(this);
                this.ProgramCreateEventSent = true;

                return(Constants.S_OK);
            }
            catch (Exception e) when(ExceptionHelper.BeforeCatch(e, Logger, reportOnlyCorrupting: true))
            {
                exception = e;
            }

            // If we just return the exception as an HRESULT, we will lose our message, so we instead send up an error event, and
            // return that the attach was canceled
            OnStartDebuggingFailed(exception);
            return(AD7_HRESULT.E_ATTACH_USER_CANCELED);
        }
Пример #9
0
        // Launches a process by means of the debug engine.
        // Normally, Visual Studio launches a program using the IDebugPortEx2::LaunchSuspended method and then attaches the debugger
        // to the suspended program. However, there are circumstances in which the debug engine may need to launch a program
        // (for example, if the debug engine is part of an interpreter and the program being debugged is an interpreted language),
        // in which case Visual Studio uses the IDebugEngineLaunch2::LaunchSuspended method
        // The IDebugEngineLaunch2::ResumeProcess method is called to start the process after the process has been successfully launched in a suspended state.
        int IDebugEngineLaunch2.LaunchSuspended(string pszServer, IDebugPort2 port, string exe, string args, string dir, string env, string options, enum_LAUNCH_FLAGS launchFlags, uint hStdInput, uint hStdOutput, uint hStdError, IDebugEventCallback2 ad7Callback, out IDebugProcess2 process)
        {
            Debug.Assert(_pollThread == null);
            Debug.Assert(_engineCallback == null);
            Debug.Assert(_debuggedProcess == null);
            Debug.Assert(_ad7ProgramId == Guid.Empty);

            // Check if the logger was enabled late.
            Logger.LoadMIDebugLogger(_configStore);

            process = null;

            _engineCallback = new EngineCallback(this, ad7Callback);

            Exception exception;

            try
            {
                // Note: LaunchOptions.GetInstance can be an expensive operation and may push a wait message loop
                LaunchOptions launchOptions = LaunchOptions.GetInstance(_configStore, exe, args, dir, options, _engineCallback, TargetEngine.Native, Logger);

                // We are being asked to debug a process when we currently aren't debugging anything
                _pollThread = new WorkerThread(Logger);
                var cancellationTokenSource = new CancellationTokenSource();

                using (cancellationTokenSource)
                {
                    _pollThread.RunOperation(ResourceStrings.InitializingDebugger, cancellationTokenSource, (HostWaitLoop waitLoop) =>
                    {
                        try
                        {
                            _debuggedProcess = new DebuggedProcess(true, launchOptions, _engineCallback, _pollThread, _breakpointManager, this, _configStore);
                        }
                        finally
                        {
                            // If there is an exception from the DebuggeedProcess constructor, it is our responsibility to dispose the DeviceAppLauncher,
                            // otherwise the DebuggedProcess object takes ownership.
                            if (_debuggedProcess == null && launchOptions.DeviceAppLauncher != null)
                            {
                                launchOptions.DeviceAppLauncher.Dispose();
                            }
                        }

                        _pollThread.PostedOperationErrorEvent += _debuggedProcess.OnPostedOperationError;

                        return(_debuggedProcess.Initialize(waitLoop, cancellationTokenSource.Token));
                    });
                }

                EngineUtils.RequireOk(port.GetProcess(_debuggedProcess.Id, out process));

                return(Constants.S_OK);
            }
            catch (Exception e) when(ExceptionHelper.BeforeCatch(e, Logger, reportOnlyCorrupting: true))
            {
                exception = e;
                // Return from the catch block so that we can let the exception unwind - the stack can get kind of big
            }

            // If we just return the exception as an HRESULT, we will loose our message, so we instead send up an error event, and then
            // return E_ABORT.
            Logger.Flush();
            SendStartDebuggingError(exception);

            Dispose();

            return(Constants.E_ABORT);
        }
Пример #10
0
        /// <summary>
        /// Resolves the various file paths used by the AndroidDebugLauncher and returns an initialized InstallPaths object
        /// </summary>
        /// <param name="token">token to check for cancelation</param>
        /// <param name="launchOptions">[Required] launch options object</param>
        /// <param name="logger">logger object</param>
        /// <param name="targetEngine">target engine</param>
        /// <returns>[Required] Created InstallPaths object</returns>
        public static InstallPaths Resolve(CancellationToken token, AndroidLaunchOptions launchOptions, MICore.Logger logger, TargetEngine targetEngine)
        {
            var result = new InstallPaths();

            if (launchOptions.SDKRoot != null)
            {
                result.SDKRoot = launchOptions.SDKRoot;
            }
            else
            {
                result.SDKRoot = GetDirectoryFromRegistry(@"SOFTWARE\Android SDK Tools", "Path", checkBothBitnesses: true, externalProductName: LauncherResources.ProductName_SDK);
            }

            if (targetEngine != TargetEngine.Java)
            {
                string ndkRoot = launchOptions.NDKRoot;
                if (ndkRoot == null)
                {
                    ndkRoot = GetDirectoryFromRegistry(RegistryRoot.Value + @"\Setup\VS\SecondaryInstaller\AndroidNDK", "NDK_HOME", checkBothBitnesses: false, externalProductName: LauncherResources.ProductName_NDK);
                }

                NdkReleaseId ndkReleaseId            = new NdkReleaseId();
                string       ndkReleaseVersionFile   = Path.Combine(ndkRoot, "RELEASE.TXT");
                string       ndkSourcePropertiesFile = Path.Combine(ndkRoot, "source.properties");

                // NDK releases >= r11 have a source.properties file
                if (File.Exists(ndkSourcePropertiesFile))
                {
                    NdkReleaseId.TryParsePropertiesFile(ndkSourcePropertiesFile, out ndkReleaseId);
                }
                // NDK releases < r11 have a RELEASE.txt file
                else if (File.Exists(ndkReleaseVersionFile))
                {
                    NdkReleaseId.TryParseFile(ndkReleaseVersionFile, out ndkReleaseId);
                }
                else
                {
                    ThrowExternalFileNotFoundException(ndkReleaseVersionFile, LauncherResources.ProductName_NDK);
                }

                logger.WriteLine("Using NDK '{0}' from path '{1}'", ndkReleaseId, ndkRoot);

                // 32 vs 64-bit doesn't matter when comparing
                var r11 = new NdkReleaseId(11, 'a');
                // In NDK r11 and later, gdb is multi-arch and there's only one binary
                // in the prebuilt directory
                bool usePrebuiltGDB = ndkReleaseId.CompareVersion(r11) >= 0;
                IEnumerable <INDKFilePath> prebuiltGDBPath = NDKPrebuiltFilePath.GDBPaths();

                string targetArchitectureName = launchOptions.TargetArchitecture.ToNDKArchitectureName();
                IEnumerable <INDKFilePath> possibleGDBPaths;

                switch (launchOptions.TargetArchitecture)
                {
                case MICore.TargetArchitecture.X86:
                    possibleGDBPaths = usePrebuiltGDB ? prebuiltGDBPath : NDKToolChainFilePath.x86_GDBPaths();
                    break;

                case MICore.TargetArchitecture.X64:
                    possibleGDBPaths = usePrebuiltGDB ? prebuiltGDBPath : NDKToolChainFilePath.x64_GDBPaths();
                    break;

                case MICore.TargetArchitecture.ARM:
                    possibleGDBPaths = usePrebuiltGDB ? prebuiltGDBPath : NDKToolChainFilePath.ARM_GDBPaths();
                    break;

                case MICore.TargetArchitecture.ARM64:
                    possibleGDBPaths = usePrebuiltGDB ? prebuiltGDBPath : NDKToolChainFilePath.ARM64_GDBPaths();
                    break;

                default:
                    Debug.Fail("Should be impossible");
                    throw new InvalidOperationException();
                }

                INDKFilePath gdbMatchedPath;
                result.GDBPath = GetNDKFilePath(
                    string.Concat("Android-", targetArchitectureName, "-GDBPath"),
                    ndkRoot,
                    possibleGDBPaths,
                    out gdbMatchedPath
                    );
                if (launchOptions.TargetArchitecture == MICore.TargetArchitecture.X86 && gdbMatchedPath != null)
                {
                    var r10b = new NdkReleaseId(10, 'b');

                    // Before r10b, the 'windows-x86_64' ndk didn't support x86 debugging
                    if (ndkReleaseId.IsValid && ndkReleaseId.CompareVersion(r10b) < 0 && gdbMatchedPath.PartialFilePath.Contains(@"\windows-x86_64\"))
                    {
                        throw new LauncherException(Telemetry.LaunchFailureCode.NoReport, LauncherResources.Error_64BitNDKNotSupportedForX86);
                    }
                }

                IEnumerable <INDKFilePath> gdbServerPath = NDKPrebuiltFilePath.GDBServerPaths(targetArchitectureName);
                INDKFilePath gdbServerMatchedPath;
                result.GDBServerPath = GetNDKFilePath(
                    string.Concat("Android-", targetArchitectureName, "-GDBServerPath"),
                    ndkRoot,
                    gdbServerPath,
                    out gdbServerMatchedPath // not used
                    );

                token.ThrowIfCancellationRequested();
            }

            return(result);
        }
Пример #11
0
 // Sets the registry root currently in use by the DE. Different installations of Visual Studio can change where their registry information is stored
 // This allows the debugger to tell the engine where that location is.
 int IDebugEngine2.SetRegistryRoot(string registryRoot)
 {
     _configStore = new HostConfigurationStore(registryRoot, EngineConstants.EngineId);
     Logger       = Logger.EnsureInitialized(_configStore);
     return(Constants.S_OK);
 }