예제 #1
0
        public override void InitStreams(LaunchOptions options, out StreamReader reader, out StreamWriter writer)
        {
            PipeLaunchOptions pipeOptions = (PipeLaunchOptions)options;

            if (!LocalLaunchOptions.CheckDirectoryPath(pipeOptions.PipeCwd))
            {
                throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, MICoreResources.Error_InvalidLocalDirectoryPath, pipeOptions.PipeCwd));
            }

            if (!LocalLaunchOptions.CheckFilePath(pipeOptions.PipePath))
            {
                throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, MICoreResources.Error_InvalidLocalExePath, pipeOptions.PipePath));
            }

            _cmdArgs = pipeOptions.PipeCommandArguments;

            Process proc = new Process();

            _pipePath = pipeOptions.PipePath;
            proc.StartInfo.FileName         = pipeOptions.PipePath;
            proc.StartInfo.Arguments        = pipeOptions.PipeArguments;
            proc.StartInfo.WorkingDirectory = pipeOptions.PipeCwd;

            foreach (EnvironmentEntry entry in pipeOptions.PipeEnvironment)
            {
                proc.StartInfo.SetEnvironmentVariable(entry.Name, entry.Value);
            }

            InitProcess(proc, out reader, out writer);
        }
예제 #2
0
        public override void InitStreams(LaunchOptions options, out StreamReader reader, out StreamWriter writer)
        {
            LocalLaunchOptions localOptions  = (LocalLaunchOptions)options;
            string             miDebuggerDir = System.IO.Path.GetDirectoryName(localOptions.MIDebuggerPath);

            Process proc = new Process();

            proc.StartInfo.FileName  = localOptions.MIDebuggerPath;
            proc.StartInfo.Arguments = localOptions.GetMiDebuggerArgs();

            // LLDB has the -environment-cd mi command that is used to set the working dir for gdb/clrdbg, but it doesn't work.
            // So, set lldb's working dir to the user's requested folder before launch.
            proc.StartInfo.WorkingDirectory = options.DebuggerMIMode == MIMode.Lldb ? options.WorkingDirectory : miDebuggerDir;

            // On Windows, GDB locally requires that the directory be on the PATH, being the working directory isn't good enough
            if (PlatformUtilities.IsWindows() &&
                options.DebuggerMIMode == MIMode.Gdb)
            {
                string path = proc.StartInfo.GetEnvironmentVariable("PATH");
                path = (string.IsNullOrEmpty(path) ? miDebuggerDir : path + ";" + miDebuggerDir);
                proc.StartInfo.SetEnvironmentVariable("PATH", path);
            }

            // Only pass the environment to launch clrdbg. For other modes, there are commands that set the environment variables
            // directly for the debuggee.
            if (options.DebuggerMIMode == MIMode.Clrdbg)
            {
                foreach (EnvironmentEntry entry in localOptions.Environment)
                {
                    proc.StartInfo.SetEnvironmentVariable(entry.Name, entry.Value);
                }
            }

            InitProcess(proc, out reader, out writer);
        }
예제 #3
0
        public override void InitStreams(LaunchOptions options, out StreamReader reader, out StreamWriter writer)
        {
            LocalLaunchOptions localOptions  = (LocalLaunchOptions)options;
            string             miDebuggerDir = System.IO.Path.GetDirectoryName(localOptions.MIDebuggerPath);

            Process proc = new Process();

            proc.StartInfo.FileName         = localOptions.MIDebuggerPath;
            proc.StartInfo.Arguments        = "--interpreter=mi";
            proc.StartInfo.WorkingDirectory = miDebuggerDir;

            // On Windows, GDB locally requires that the directory be on the PATH, being the working directory isn't good enough
            if (PlatformUtilities.IsWindows() &&
                options.DebuggerMIMode == MIMode.Gdb)
            {
                string path = proc.StartInfo.GetEnvironmentVariable("PATH");
                path = (string.IsNullOrEmpty(path) ? miDebuggerDir : path + ";" + miDebuggerDir);
                proc.StartInfo.SetEnvironmentVariable("PATH", path);
            }

            foreach (EnvironmentEntry entry in localOptions.Environment)
            {
                proc.StartInfo.SetEnvironmentVariable(entry.Name, entry.Value);
            }

            InitProcess(proc, out reader, out writer);
        }
예제 #4
0
        public override void InitStreams(LaunchOptions options, out StreamReader reader, out StreamWriter writer)
        {
            LocalLaunchOptions localOptions  = (LocalLaunchOptions)options;
            string             miDebuggerDir = System.IO.Path.GetDirectoryName(localOptions.MIDebuggerPath);

            Process proc = new Process();

            proc.StartInfo.FileName         = localOptions.MIDebuggerPath;
            proc.StartInfo.Arguments        = "--interpreter=mi";
            proc.StartInfo.WorkingDirectory = miDebuggerDir;

            // On Windows, GDB locally requires that the directory be on the PATH, being the working directory isn't good enough
            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) &&
                options.DebuggerMIMode == MIMode.Gdb &&
                proc.StartInfo.Environment.ContainsKey("PATH"))
            {
                proc.StartInfo.Environment["PATH"] = proc.StartInfo.Environment["PATH"] + ";" + miDebuggerDir;
            }

            foreach (EnvironmentEntry entry in localOptions.Environment)
            {
                proc.StartInfo.Environment.Add(entry.Name, entry.Value);
            }

            InitProcess(proc, out reader, out writer);
        }
예제 #5
0
        internal static string GetDebuggerCommand(LocalLaunchOptions localOptions)
        {
            if (PlatformUtilities.IsLinux())
            {
                string debuggerPathCorrectElevation = localOptions.MIDebuggerPath;

                // If running as root, make sure the new console is also root.
                bool isRoot = UnixNativeMethods.GetEUid() == 0;

                // If the system doesn't allow a non-root process to attach to another process, try to run GDB as root
                if (localOptions.ProcessId != 0 && !isRoot && UnixUtilities.GetRequiresRootAttach(localOptions.DebuggerMIMode))
                {
                    // Prefer pkexec for a nice graphical prompt, but fall back to sudo if it's not available
                    if (File.Exists(UnixUtilities.PKExecPath))
                    {
                        debuggerPathCorrectElevation = String.Concat(UnixUtilities.PKExecPath, " ", debuggerPathCorrectElevation);
                    }
                    else if (File.Exists(UnixUtilities.SudoPath))
                    {
                        debuggerPathCorrectElevation = String.Concat(UnixUtilities.SudoPath, " ", debuggerPathCorrectElevation);
                    }
                    else
                    {
                        Debug.Fail("Root required to attach, but no means of elevating available!");
                    }
                }

                return debuggerPathCorrectElevation;
            }
            else
            {
                return localOptions.MIDebuggerPath;
            }
        }
예제 #6
0
        static internal LocalLaunchOptions CreateFromXml(Xml.LaunchOptions.LocalLaunchOptions source)
        {
            var options = new LocalLaunchOptions(RequireAttribute(source.MIDebuggerPath, "MIDebuggerPath"), source.MIDebuggerServerAddress);

            options.InitializeCommonOptions(source);

            return(options);
        }
예제 #7
0
파일: gdb.cs 프로젝트: skyformat99/MIEngine
 public override bool UseExternalConsoleForLocalLaunch(LocalLaunchOptions localLaunchOptions)
 {
     // NOTE: On Linux at least, there are issues if we try to have GDB launch the process as a child of VS
     // code -- it will cause a deadlock during debuggee launch. So we always use the external console
     // unless we are in a scenario where the debuggee will not be a child process. In the future we
     // might want to change this for other OSs.
     return(String.IsNullOrEmpty(localLaunchOptions.MIDebuggerServerAddress) && !localLaunchOptions.IsCoreDump);
 }
예제 #8
0
        public override void InitStreams(LaunchOptions options, out StreamReader reader, out StreamWriter writer)
        {
            LocalLaunchOptions localOptions = (LocalLaunchOptions)options;

            Process proc = new Process();

            proc.StartInfo.FileName         = localOptions.MIDebuggerPath;
            proc.StartInfo.Arguments        = "--interpreter=mi";
            proc.StartInfo.WorkingDirectory = System.IO.Path.GetDirectoryName(localOptions.MIDebuggerPath);

            InitProcess(proc, out reader, out writer);
        }
예제 #9
0
파일: gdb.cs 프로젝트: chuckries/MIEngine
 public override bool UseExternalConsoleForLocalLaunch(LocalLaunchOptions localLaunchOptions)
 {
     // NOTE: On Linux, there are issues if we try to have GDB launch the process as a child of VS
     // code -- it will cause a deadlock during debuggee launch. So we always use the external console
     // unless we are in a scenario where the debuggee will not be a child process.
     if (PlatformUtilities.IsLinux())
     {
         return(String.IsNullOrEmpty(localLaunchOptions.MIDebuggerServerAddress) && !localLaunchOptions.IsCoreDump);
     }
     else
     {
         return(base.UseExternalConsoleForLocalLaunch(localLaunchOptions));
     }
 }
예제 #10
0
        private bool GetRequiresRootAttach(LocalLaunchOptions localOptions)
        {
            if (localOptions.DebuggerMIMode != MIMode.Clrdbg)
            {
                // If "ptrace_scope" is a value other than 0, only root can attach to arbitrary processes
                if (this.GetPtraceScope() != 0)
                {
                    return(true); // Attaching to any non-child process requires root
                }
            }

            // TODO: detect if the target is under a different user and if so return true
            return(false);
        }
예제 #11
0
파일: gdb.cs 프로젝트: rajkumar42/MIEngine
 public override bool UseExternalConsoleForLocalLaunch(LocalLaunchOptions localLaunchOptions)
 {
     // NOTE: On Linux, there are issues if we try to have GDB launch the process as a child of VS 
     // code -- it will cause a deadlock during debuggee launch. So we always use the external console 
     // unless we are in a scenario where the debuggee will not be a child process.
     if (PlatformUtilities.IsLinux())
     {
         return String.IsNullOrEmpty(localLaunchOptions.MIDebuggerServerAddress) && !localLaunchOptions.IsCoreDump;
     }
     else
     {
         return base.UseExternalConsoleForLocalLaunch(localLaunchOptions);
     }
 }
예제 #12
0
        public override void InitStreams(LaunchOptions options, out StreamReader reader, out StreamWriter writer)
        {
            LocalLaunchOptions localOptions  = (LocalLaunchOptions)options;
            string             miDebuggerDir = System.IO.Path.GetDirectoryName(localOptions.MIDebuggerPath);

            Process proc = new Process();

            proc.StartInfo.FileName         = localOptions.DebugServer;
            proc.StartInfo.Arguments        = localOptions.DebugServerArgs;
            proc.StartInfo.WorkingDirectory = miDebuggerDir;
            _startPattern  = localOptions.ServerStarted;
            _messagePrefix = Path.GetFileNameWithoutExtension(localOptions.DebugServer);

            InitProcess(proc, out reader, out writer);
        }
예제 #13
0
        static internal LocalLaunchOptions CreateFromXml(Xml.LaunchOptions.LocalLaunchOptions source)
        {
            string miDebuggerPath = source.MIDebuggerPath;

            // If no path to the debugger was specified, look for the proper binary in the user's $PATH
            if (String.IsNullOrEmpty(miDebuggerPath))
            {
                string debuggerBinary = null;
                switch (source.MIMode)
                {
                case Xml.LaunchOptions.MIMode.gdb:
                    debuggerBinary = "gdb";
                    break;

                case Xml.LaunchOptions.MIMode.lldb:
                    debuggerBinary = "lldb-mi";
                    break;
                }

                if (!String.IsNullOrEmpty(debuggerBinary))
                {
                    miDebuggerPath = LocalLaunchOptions.ResolveFromPath(debuggerBinary);
                }

                if (String.IsNullOrEmpty(miDebuggerPath))
                {
                    throw new InvalidLaunchOptionsException(MICoreResources.Error_NoMiDebuggerPath);
                }
            }

            var options = new LocalLaunchOptions(
                RequireAttribute(miDebuggerPath, "MIDebuggerPath"),
                source.MIDebuggerServerAddress,
                source.Environment);

            options.InitializeCommonOptions(source);
            options.InitializeServerOptions(source);
            options._useExternalConsole = source.ExternalConsole;

            // when using local options the core dump path must check out
            if (options.IsCoreDump && !LocalLaunchOptions.CheckPath(options.CoreDumpPath))
            {
                throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, MICoreResources.Error_InvalidLocalExePath, options.CoreDumpPath));
            }

            return(options);
        }
예제 #14
0
        public override void InitStreams(LaunchOptions options, out StreamReader reader, out StreamWriter writer)
        {
            PipeLaunchOptions pipeOptions = (PipeLaunchOptions)options;

            string workingDirectory = pipeOptions.PipeCwd;

            if (!string.IsNullOrWhiteSpace(workingDirectory))
            {
                if (!LocalLaunchOptions.CheckDirectoryPath(workingDirectory))
                {
                    throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, MICoreResources.Error_InvalidLocalDirectoryPath, workingDirectory));
                }
            }
            else
            {
                workingDirectory = Path.GetDirectoryName(pipeOptions.PipePath);
                if (!LocalLaunchOptions.CheckDirectoryPath(workingDirectory))
                {
                    // If provided PipeCwd is not an absolute path, the working directory will be set to null.
                    workingDirectory = null;
                }
            }

            if (string.IsNullOrWhiteSpace(pipeOptions.PipePath))
            {
                throw new ArgumentException(MICoreResources.Error_EmptyPipePath);
            }

            _cmdArgs = pipeOptions.PipeCommandArguments;

            Process proc = new Process();

            _pipePath = pipeOptions.PipePath;
            proc.StartInfo.FileName         = pipeOptions.PipePath;
            proc.StartInfo.Arguments        = pipeOptions.PipeArguments;
            proc.StartInfo.WorkingDirectory = workingDirectory;

            foreach (EnvironmentEntry entry in pipeOptions.PipeEnvironment)
            {
                proc.StartInfo.SetEnvironmentVariable(entry.Name, entry.Value);
            }

            InitProcess(proc, out reader, out writer);
        }
예제 #15
0
        public override void InitStreams(LaunchOptions options, out StreamReader reader, out StreamWriter writer)
        {
            LocalLaunchOptions localOptions  = (LocalLaunchOptions)options;
            string             miDebuggerDir = System.IO.Path.GetDirectoryName(localOptions.MIDebuggerPath);

            Process proc = new Process();

            proc.StartInfo.FileName         = localOptions.MIDebuggerPath;
            proc.StartInfo.Arguments        = "--interpreter=mi";
            proc.StartInfo.WorkingDirectory = miDebuggerDir;

            //GDB locally requires that the directory be on the PATH, being the working directory isn't good enough
            if (proc.StartInfo.EnvironmentVariables.ContainsKey("PATH"))
            {
                proc.StartInfo.EnvironmentVariables["PATH"] = proc.StartInfo.EnvironmentVariables["PATH"] + ";" + miDebuggerDir;
            }

            InitProcess(proc, out reader, out writer);
        }
예제 #16
0
        static internal LocalLaunchOptions CreateFromXml(Xml.LaunchOptions.LocalLaunchOptions source)
        {
            var options = new LocalLaunchOptions(
                RequireAttribute(source.MIDebuggerPath, "MIDebuggerPath"),
                source.MIDebuggerServerAddress,
                source.ProcessId,
                source.Environment);

            options.InitializeCommonOptions(source);
            options.InitializeServerOptions(source);
            options.CoreDumpPath        = source.CoreDumpPath;
            options._useExternalConsole = source.ExternalConsole;

            // Ensure that CoreDumpPath and ProcessId are not specified at the same time
            if (!String.IsNullOrEmpty(source.CoreDumpPath) && source.ProcessId != 0)
            {
                throw new InvalidLaunchOptionsException(String.Format(CultureInfo.InvariantCulture, MICoreResources.Error_CannotSpecifyBoth, nameof(source.CoreDumpPath), nameof(source.ProcessId)));
            }

            return(options);
        }
예제 #17
0
        internal static string GetDebuggerCommand(LocalLaunchOptions localOptions)
        {
            string quotedDebuggerPath = String.Format(CultureInfo.InvariantCulture, "\"{0}\"", localOptions.MIDebuggerPath);

            if (PlatformUtilities.IsLinux())
            {
                string debuggerPathCorrectElevation = quotedDebuggerPath;
                string prompt = string.Empty;

                // If running as root, make sure the new console is also root.
                bool isRoot = UnixNativeMethods.GetEUid() == 0;

                // If the system doesn't allow a non-root process to attach to another process, try to run GDB as root
                if (localOptions.ProcessId.HasValue && !isRoot && UnixUtilities.GetRequiresRootAttach(localOptions.DebuggerMIMode))
                {
                    prompt = String.Format(CultureInfo.CurrentCulture, "echo -n '{0}'; read yn; if [ \"$yn\" != 'y' ] && [ \"$yn\" != 'Y' ] ; then exit 1; fi; ", MICoreResources.Warn_AttachAsRootProcess);

                    // Prefer pkexec for a nice graphical prompt, but fall back to sudo if it's not available
                    if (File.Exists(UnixUtilities.PKExecPath))
                    {
                        debuggerPathCorrectElevation = String.Concat(UnixUtilities.PKExecPath, " ", debuggerPathCorrectElevation);
                    }
                    else if (File.Exists(UnixUtilities.SudoPath))
                    {
                        debuggerPathCorrectElevation = String.Concat(UnixUtilities.SudoPath, " ", debuggerPathCorrectElevation);
                    }
                    else
                    {
                        Debug.Fail("Root required to attach, but no means of elevating available!");
                    }
                }

                return(String.Concat(prompt, debuggerPathCorrectElevation));
            }
            else
            {
                return(quotedDebuggerPath);
            }
        }
예제 #18
0
        internal static string GetDebuggerCommand(LocalLaunchOptions localOptions)
        {
            if (PlatformUtilities.IsLinux())
            {
                string debuggerPathCorrectElevation = localOptions.MIDebuggerPath;
                string prompt = string.Empty;

                // If running as root, make sure the new console is also root. 
                bool isRoot = UnixNativeMethods.GetEUid() == 0;

                // If the system doesn't allow a non-root process to attach to another process, try to run GDB as root
                if (localOptions.ProcessId != 0 && !isRoot && UnixUtilities.GetRequiresRootAttach(localOptions.DebuggerMIMode))
                {
                    prompt = String.Format(CultureInfo.CurrentCulture, "read -n 1 -p \\\"{0}\\\" yn; if [[ ! $yn =~ ^[Yy]$ ]] ; then exit 0; fi; ", MICoreResources.Warn_AttachAsRootProcess);

                    // Prefer pkexec for a nice graphical prompt, but fall back to sudo if it's not available
                    if (File.Exists(UnixUtilities.PKExecPath))
                    {
                        debuggerPathCorrectElevation = String.Concat(UnixUtilities.PKExecPath, " ", debuggerPathCorrectElevation);
                    }
                    else if (File.Exists(UnixUtilities.SudoPath))
                    {
                        debuggerPathCorrectElevation = String.Concat(UnixUtilities.SudoPath, " ", debuggerPathCorrectElevation);
                    }
                    else
                    {
                        Debug.Fail("Root required to attach, but no means of elevating available!");
                    }
                }

                return String.Concat(prompt, debuggerPathCorrectElevation);
            }
            else
            {
                return localOptions.MIDebuggerPath;
            }
        }
예제 #19
0
        public override void InitStreams(LaunchOptions options, out StreamReader reader, out StreamWriter writer)
        {
            LocalLaunchOptions localOptions  = (LocalLaunchOptions)options;
            string             miDebuggerDir = System.IO.Path.GetDirectoryName(localOptions.MIDebuggerPath);

            Process proc = new Process();

            proc.StartInfo.FileName  = localOptions.MIDebuggerPath;
            proc.StartInfo.Arguments = localOptions.GetMiDebuggerArgs();

            // LLDB has the -environment-cd mi command that is used to set the working dir for gdb, but it doesn't work.
            // So, set lldb's working dir to the user's requested folder before launch.
            proc.StartInfo.WorkingDirectory = options.DebuggerMIMode == MIMode.Lldb ? options.WorkingDirectory : miDebuggerDir;

            // On Windows, GDB locally requires that the directory be on the PATH, being the working directory isn't good enough
            if (PlatformUtilities.IsWindows() &&
                options.DebuggerMIMode == MIMode.Gdb)
            {
                string path = proc.StartInfo.GetEnvironmentVariable("PATH");
                path = (string.IsNullOrEmpty(path) ? miDebuggerDir : path + ";" + miDebuggerDir);
                proc.StartInfo.SetEnvironmentVariable("PATH", path);
            }

            // Allow to execute custom commands before launching debugger.
            // For ex., instructing GDB not to break for certain signals
            if (options.DebuggerMIMode == MIMode.Gdb && !string.IsNullOrWhiteSpace(options.WorkingDirectory))
            {
                var gdbInitFile = Path.Combine(options.WorkingDirectory, ".gdbinit");
                if (File.Exists(gdbInitFile))
                {
                    proc.StartInfo.Arguments += " -x \"" + gdbInitFile + "\"";
                }
            }

            InitProcess(proc, out reader, out writer);
        }
예제 #20
0
 /// <summary>
 /// Determines if a new external console should be spawned on non-Windows platforms for the debugger+app
 /// </summary>
 /// <param name="localLaunchOptions">[required] local launch options</param>
 /// <returns>True if an external console should be used</returns>
 public virtual bool UseExternalConsoleForLocalLaunch(LocalLaunchOptions localLaunchOptions)
 {
     return localLaunchOptions.UseExternalConsole && String.IsNullOrEmpty(localLaunchOptions.MIDebuggerServerAddress) && !localLaunchOptions.IsCoreDump;
 }
예제 #21
0
        public static LaunchOptions GetInstance(string registryRoot, string exePath, string args, string dir, string options, IDeviceAppLauncherEventCallback eventCallback, TargetEngine targetEngine)
        {
            if (string.IsNullOrWhiteSpace(exePath))
            {
                throw new ArgumentNullException("exePath");
            }

            if (string.IsNullOrWhiteSpace(options))
            {
                throw new InvalidLaunchOptionsException(MICoreResources.Error_StringIsNullOrEmpty);
            }

            if (string.IsNullOrEmpty(registryRoot))
            {
                throw new ArgumentNullException("registryRoot");
            }

            Logger.WriteTextBlock("LaunchOptions", options);

            LaunchOptions launchOptions      = null;
            Guid          clsidLauncher      = Guid.Empty;
            object        launcherXmlOptions = null;

            try
            {
                using (XmlReader reader = OpenXml(options))
                {
                    switch (reader.LocalName)
                    {
                    case "LocalLaunchOptions":
                    {
                        var serializer       = new Microsoft.Xml.Serialization.GeneratedAssembly.LocalLaunchOptionsSerializer();
                        var xmlLaunchOptions = (Xml.LaunchOptions.LocalLaunchOptions)Deserialize(serializer, reader);
                        launchOptions = LocalLaunchOptions.CreateFromXml(xmlLaunchOptions);
                    }
                    break;

                    case "SerialPortLaunchOptions":
                    {
                        var serializer       = new Microsoft.Xml.Serialization.GeneratedAssembly.SerialPortLaunchOptionsSerializer();
                        var xmlLaunchOptions = (Xml.LaunchOptions.SerialPortLaunchOptions)Deserialize(serializer, reader);
                        launchOptions = SerialLaunchOptions.CreateFromXml(xmlLaunchOptions);
                    }
                    break;

                    case "PipeLaunchOptions":
                    {
                        var serializer       = new Microsoft.Xml.Serialization.GeneratedAssembly.PipeLaunchOptionsSerializer();
                        var xmlLaunchOptions = (Xml.LaunchOptions.PipeLaunchOptions)Deserialize(serializer, reader);
                        launchOptions = PipeLaunchOptions.CreateFromXml(xmlLaunchOptions);
                    }
                    break;

                    case "TcpLaunchOptions":
                    {
                        var serializer       = new Microsoft.Xml.Serialization.GeneratedAssembly.TcpLaunchOptionsSerializer();
                        var xmlLaunchOptions = (Xml.LaunchOptions.TcpLaunchOptions)Deserialize(serializer, reader);
                        launchOptions = TcpLaunchOptions.CreateFromXml(xmlLaunchOptions);
                    }
                    break;

                    case "IOSLaunchOptions":
                    {
                        var serializer = new Microsoft.Xml.Serialization.GeneratedAssembly.IOSLaunchOptionsSerializer();
                        launcherXmlOptions = Deserialize(serializer, reader);
                        clsidLauncher      = new Guid("316783D1-1824-4847-B3D3-FB048960EDCF");
                    }
                    break;

                    case "AndroidLaunchOptions":
                    {
                        var serializer = new Microsoft.Xml.Serialization.GeneratedAssembly.AndroidLaunchOptionsSerializer();
                        launcherXmlOptions = Deserialize(serializer, reader);
                        clsidLauncher      = new Guid("C9A403DA-D3AA-4632-A572-E81FF6301E9B");
                    }
                    break;

                    default:
                    {
                        throw new XmlException(string.Format(CultureInfo.CurrentCulture, MICoreResources.Error_UnknownXmlElement, reader.LocalName));
                    }
                    }

                    // Read any remaining bits of XML to catch other errors
                    while (reader.NodeType != XmlNodeType.None)
                    {
                        reader.Read();
                    }
                }
            }
            catch (XmlException e)
            {
                throw new InvalidLaunchOptionsException(e.Message);
            }

            if (clsidLauncher != Guid.Empty)
            {
                launchOptions = ExecuteLauncher(registryRoot, clsidLauncher, exePath, args, dir, launcherXmlOptions, eventCallback, targetEngine);
            }

            if (targetEngine == TargetEngine.Native)
            {
                if (launchOptions.ExePath == null)
                {
                    launchOptions.ExePath = exePath;
                }
            }

            if (string.IsNullOrEmpty(launchOptions.ExeArguments))
            {
                launchOptions.ExeArguments = args;
            }

            if (string.IsNullOrEmpty(launchOptions.WorkingDirectory))
            {
                launchOptions.WorkingDirectory = dir;
            }

            if (launchOptions._setupCommands == null)
            {
                launchOptions._setupCommands = new List <LaunchCommand>(capacity: 0).AsReadOnly();
            }

            launchOptions._initializationComplete = true;
            return(launchOptions);
        }
예제 #22
0
        public override void InitStreams(LaunchOptions options, out StreamReader reader, out StreamWriter writer)
        {
            LocalLaunchOptions localOptions = (LocalLaunchOptions)options;

            if (!this.IsValidMiDebuggerPath(localOptions.MIDebuggerPath))
            {
                throw new Exception(MICoreResources.Error_InvalidMiDebuggerPath);
            }

            // Default working directory is next to the app
            string debuggeeDir;

            if (Path.IsPathRooted(options.ExePath) && File.Exists(options.ExePath))
            {
                debuggeeDir = System.IO.Path.GetDirectoryName(options.ExePath);
            }
            else
            {
                // If we don't know where the app is, default to HOME, and if we somehow can't get that, go with the root directory.
                debuggeeDir = Environment.GetEnvironmentVariable("HOME");
                if (string.IsNullOrEmpty(debuggeeDir))
                {
                    debuggeeDir = "/";
                }
            }

            _gdbStdInName  = Path.Combine(Path.GetTempPath(), FifoPrefix + Path.GetRandomFileName());
            _gdbStdOutName = Path.Combine(Path.GetTempPath(), FifoPrefix + Path.GetRandomFileName());

            MakeGdbFifo(_gdbStdInName);
            MakeGdbFifo(_gdbStdOutName);

            _fifoWatcher                     = new FileSystemWatcher(Path.GetTempPath(), FifoPrefix + "*");
            _fifoWatcher.Deleted            += FifoWatcher_Deleted;
            _fifoWatcher.EnableRaisingEvents = true;

            // Setup the streams on the fifos as soon as possible.
            System.IO.FileStream gdbStdInStream  = new FileStream(_gdbStdInName, FileMode.Open);
            System.IO.FileStream gdbStdOutStream = new FileStream(_gdbStdOutName, FileMode.Open);

            // If running as root, make sure the new console is also root.
            bool isRoot = LinuxNativeMethods.GetEUid() == 0;

            // Check and see if gnome-terminal exists. If not, fall back to xterm
            string terminalCmd, bashCommandPrefix;

            if (File.Exists(GnomeTerminalPath))
            {
                terminalCmd       = GnomeTerminalPath;
                bashCommandPrefix = "--title DebuggerTerminal -x";
            }
            else
            {
                terminalCmd       = XTermPath;
                bashCommandPrefix = "-title DebuggerTerminal -e";
            }

            // Spin up a new bash shell, cd to the working dir, execute a tty command to get the shell tty and store it
            // start the debugger in mi mode setting the tty to the terminal defined earlier and redirect stdin/stdout
            // to the correct pipes. After gdb exits, cleanup the FIFOs. This is done using the trap command to add a
            // signal handler for SIGHUP on the console (executing the two rm commands)
            //
            // NOTE: sudo launch requires sudo or the terminal will fail to launch. The first argument must then be the terminal path
            // TODO: this should be configurable in launch options to allow for other terminals with a default of gnome-terminal so the user can change the terminal
            // command. Note that this is trickier than it sounds since each terminal has its own set of parameters. For now, rely on remote for those scenarios
            Process terminalProcess = new Process();

            terminalProcess.StartInfo.CreateNoWindow   = false;
            terminalProcess.StartInfo.UseShellExecute  = false;
            terminalProcess.StartInfo.WorkingDirectory = debuggeeDir;
            terminalProcess.StartInfo.FileName         = !isRoot ? terminalCmd : SudoPath;

            string debuggerCmd = localOptions.MIDebuggerPath;

            // If the system doesn't allow a non-root process to attach to another process, try to run GDB as root
            if (localOptions.ProcessId != 0 && !isRoot && GetRequiresRootAttach(localOptions))
            {
                // Prefer pkexec for a nice graphical prompt, but fall back to sudo if it's not available
                if (File.Exists(LocalLinuxTransport.PKExecPath))
                {
                    debuggerCmd = String.Concat(LocalLinuxTransport.PKExecPath, " ", debuggerCmd);
                }
                else if (File.Exists(LocalLinuxTransport.SudoPath))
                {
                    debuggerCmd = String.Concat(LocalLinuxTransport.SudoPath, " ", debuggerCmd);
                }
                else
                {
                    Debug.Fail("Root required to attach, but no means of elevating available!");
                }
            }

            string argumentString = string.Format(CultureInfo.InvariantCulture,
                                                  "{4} bash -c \"cd {0}; DbgTerm=`tty`; trap 'rm {2}; rm {3}' EXIT; {1} --interpreter=mi --tty=$DbgTerm < {2} > {3};\"",
                                                  debuggeeDir,
                                                  debuggerCmd,
                                                  _gdbStdInName,
                                                  _gdbStdOutName,
                                                  bashCommandPrefix
                                                  );

            terminalProcess.StartInfo.Arguments = !isRoot ? argumentString : String.Concat(terminalCmd, " ", argumentString);
            Logger?.WriteLine("LocalLinuxTransport command: " + terminalProcess.StartInfo.FileName + " " + terminalProcess.StartInfo.Arguments);

            if (localOptions.Environment != null)
            {
                foreach (EnvironmentEntry entry in localOptions.Environment)
                {
                    terminalProcess.StartInfo.Environment.Add(entry.Name, entry.Value);
                }
            }

            terminalProcess.Start();

            // The in/out names are confusing in this case as they are relative to gdb.
            // What that means is the names are backwards wrt miengine hence the reader
            // being the writer and vice-versa
            writer = new StreamWriter(gdbStdInStream);
            reader = new StreamReader(gdbStdOutStream);
        }
예제 #23
0
 /// <summary>
 /// Determines if a new external console should be spawned on non-Windows platforms for the debugger+app
 /// </summary>
 /// <param name="localLaunchOptions">[required] local launch options</param>
 /// <returns>True if an external console should be used</returns>
 public virtual bool UseExternalConsoleForLocalLaunch(LocalLaunchOptions localLaunchOptions)
 {
     return(localLaunchOptions.UseExternalConsole && String.IsNullOrEmpty(localLaunchOptions.MIDebuggerServerAddress) && !localLaunchOptions.IsCoreDump);
 }
예제 #24
0
        internal static LocalLaunchOptions CreateFromXml(Xml.LaunchOptions.LocalLaunchOptions source)
        {
            var options = new LocalLaunchOptions(RequireAttribute(source.MIDebuggerPath, "MIDebuggerPath"), source.MIDebuggerServerAddress);
            options.InitializeCommonOptions(source);

            return options;
        }
예제 #25
0
        public override void InitStreams(LaunchOptions options, out StreamReader reader, out StreamWriter writer)
        {
            LocalLaunchOptions localOptions = (LocalLaunchOptions)options;

            string debuggeeDir = System.IO.Path.GetDirectoryName(options.ExePath);

            string gdbStdInName  = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
            string gdbStdOutName = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());

            MakeGdbFifo(gdbStdInName);
            MakeGdbFifo(gdbStdOutName);

            // Setup the streams on the fifos as soon as possible.
            System.IO.FileStream gdbStdInStream  = new FileStream(gdbStdInName, FileMode.Open);
            System.IO.FileStream gdbStdOutStream = new FileStream(gdbStdOutName, FileMode.Open);

            // If running as root, make sure the new console is also root.
            bool isRoot = NativeMethods.GetEUid() == 0;

            // Spin up a new bash shell, cd to the working dir, execute a tty command to get the shell tty and store it
            // start the debugger in mi mode setting the tty to the terminal defined earlier and redirect stdin/stdout
            // to the correct pipes. After gdb exits, cleanup the FIFOs.
            //
            // NOTE: sudo launch requires sudo or the terminal will fail to launch. The first argument must then be the terminal path
            // TODO: this should be configurable in launch options to allow for other terminals with a default of gnome-terminal so the user can change the terminal
            // command. Note that this is trickier than it sounds since each terminal has its own set of parameters. For now, rely on remote for those scenarios
            string  terminalPath    = "/usr/bin/gnome-terminal";
            string  sudoPath        = "/usr/bin/sudo";
            Process terminalProcess = new Process();

            terminalProcess.StartInfo.CreateNoWindow   = false;
            terminalProcess.StartInfo.UseShellExecute  = false;
            terminalProcess.StartInfo.WorkingDirectory = debuggeeDir;
            terminalProcess.StartInfo.FileName         = !isRoot ? terminalPath : sudoPath;

            string argumentString = string.Format(System.Globalization.CultureInfo.InvariantCulture,
                                                  "--title DebuggerTerminal -x bash -c \"cd {0}; DbgTerm=`tty`; {1} --interpreter=mi --tty=$DbgTerm < {2} > {3}; rm {2}; rm {3} ;\"",
                                                  debuggeeDir,
                                                  localOptions.MIDebuggerPath,
                                                  gdbStdInName,
                                                  gdbStdOutName
                                                  );

            terminalProcess.StartInfo.Arguments = !isRoot ? argumentString : String.Concat(terminalPath, " ", argumentString);
            Logger.WriteLine(terminalProcess.StartInfo.Arguments);

            if (localOptions.Environment != null)
            {
                foreach (EnvironmentEntry entry in localOptions.Environment)
                {
                    terminalProcess.StartInfo.Environment.Add(entry.Name, entry.Value);
                }
            }

            terminalProcess.Start();

            // The in/out names are confusing in this case as they are relative to gdb.
            // What that means is the names are backwards wrt miengine hence the reader
            // being the writer and vice-versa
            writer = new StreamWriter(gdbStdInStream);
            reader = new StreamReader(gdbStdOutStream);
        }
        public override void InitStreams(LaunchOptions options, out StreamReader reader, out StreamWriter writer)
        {
            LocalLaunchOptions localOptions = (LocalLaunchOptions)options;

            // Default working directory is next to the app
            string debuggeeDir;

            if (Path.IsPathRooted(options.ExePath) && File.Exists(options.ExePath))
            {
                debuggeeDir = Path.GetDirectoryName(options.ExePath);
            }
            else
            {
                // If we don't know where the app is, default to HOME, and if we somehow can't get that, go with the root directory.
                debuggeeDir = Environment.GetEnvironmentVariable("HOME");
                if (string.IsNullOrEmpty(debuggeeDir))
                {
                    debuggeeDir = "/";
                }
            }

            _dbgStdInName  = UnixUtilities.MakeFifo(Logger);
            _dbgStdOutName = UnixUtilities.MakeFifo(Logger);
            string pidFifo = UnixUtilities.MakeFifo(Logger);

            // Used for testing
            Logger?.WriteLine(string.Concat("TempFile=", _dbgStdInName));
            Logger?.WriteLine(string.Concat("TempFile=", _dbgStdOutName));
            Logger?.WriteLine(string.Concat("TempFile=", pidFifo));

            // Setup the streams on the fifos as soon as possible.
            FileStream dbgStdInStream  = new FileStream(_dbgStdInName, FileMode.Open);
            FileStream dbgStdOutStream = new FileStream(_dbgStdOutName, FileMode.Open);
            FileStream pidStream       = new FileStream(pidFifo, FileMode.Open);

            string debuggerCmd           = UnixUtilities.GetDebuggerCommand(localOptions);
            string launchDebuggerCommand = UnixUtilities.LaunchLocalDebuggerCommand(
                debuggeeDir,
                _dbgStdInName,
                _dbgStdOutName,
                pidFifo,
                debuggerCmd);

            // Only pass the environment to launch clrdbg. For other modes, there are commands that set the environment variables
            // directly for the debuggee.
            ReadOnlyCollection <EnvironmentEntry> environmentForDebugger = localOptions.DebuggerMIMode == MIMode.Clrdbg ?
                                                                           localOptions.Environment :
                                                                           new ReadOnlyCollection <EnvironmentEntry>(new EnvironmentEntry[] { });;

            TerminalLauncher terminal = TerminalLauncher.MakeTerminal("DebuggerTerminal", launchDebuggerCommand, localOptions.Environment);

            terminal.Launch(debuggeeDir);

            int shellPid = -1;

            using (StreamReader pidReader = new StreamReader(pidStream, Encoding.UTF8, true, UnixUtilities.StreamBufferSize))
            {
                Task <string> readShellPidTask = pidReader.ReadLineAsync();
                if (readShellPidTask.Wait(TimeSpan.FromSeconds(10)))
                {
                    shellPid = int.Parse(readShellPidTask.Result, CultureInfo.InvariantCulture);
                    // Used for testing
                    Logger?.WriteLine(string.Concat("ShellPid=", shellPid));
                }
                else
                {
                    // Something is wrong because we didn't get the pid of shell
                    ForceDisposeStreamReader(pidReader);
                    Close();
                    throw new TimeoutException(MICoreResources.Error_LocalUnixTerminalDebuggerInitializationFailed);
                }

                _shellProcessMonitor = new ProcessMonitor(shellPid);
                _shellProcessMonitor.ProcessExited += ShellExited;
                _shellProcessMonitor.Start();

                Task <string> readDebuggerPidTask = pidReader.ReadLineAsync();
                try
                {
                    readDebuggerPidTask.Wait(_streamReadPidCancellationTokenSource.Token);
                    _debuggerPid = int.Parse(readDebuggerPidTask.Result, CultureInfo.InvariantCulture);
                }
                catch (OperationCanceledException)
                {
                    // Something is wrong because we didn't get the pid of the debugger
                    ForceDisposeStreamReader(pidReader);
                    Close();
                    throw new OperationCanceledException(MICoreResources.Error_LocalUnixTerminalDebuggerInitializationFailed);
                }
            }

            // The in/out names are confusing in this case as they are relative to gdb.
            // What that means is the names are backwards wrt miengine hence the reader
            // being the writer and vice-versa
            // Mono seems to hang when the debugger sends a large response unless we specify a larger buffer here
            writer = new StreamWriter(dbgStdInStream, new UTF8Encoding(false, true), UnixUtilities.StreamBufferSize);
            reader = new StreamReader(dbgStdOutStream, Encoding.UTF8, true, UnixUtilities.StreamBufferSize);
        }
예제 #27
0
        private LocalLaunchOptions SetupForDebuggingWorker(CancellationToken token)
        {
            CancellationTokenRegistration onCancelRegistration = token.Register(() =>
            {
                _gdbServerExecCancellationSource.Cancel();
            });

            using (onCancelRegistration)
            {
                // TODO: Adb exception messages should be improved. Example, if ADB is not started, this is returned:
                //    +		[libadb.AdbException]	{"Could not connect to the adb.exe server. See InnerException for details."}	libadb.AdbException
                // 'See InnerException for details.' should not be there. It should just add the inner exception message:
                //    [System.Net.Sockets.SocketException]	{"No connection could be made because the target machine actively refused it 127.0.0.1:5037"}	System.Net.Sockets.SocketException

                Device device = null;
                string workingDirectory = null;
                string gdbServerRemotePath = null;
                string gdbServerSocketDescription = null;
                Task taskGdbServer = null;
                int gdbPortNumber = 0;
                int progressCurrentIndex = 0;
                int progressStepCount = 0;

                List<NamedAction> actions = new List<NamedAction>();

                actions.Add(new NamedAction(LauncherResources.Step_ResolveInstallPaths, () =>
                {
                    _installPaths = InstallPaths.Resolve(token, _launchOptions);
                }));

                actions.Add(new NamedAction(LauncherResources.Step_ConnectToDevice, () =>
                {
                    Adb adb;
                    try
                    {
                        adb = new Adb(_installPaths.SDKRoot);
                    }
                    catch (ArgumentException)
                    {
                        throw new LauncherException(Telemetry.LaunchFailureCode.InvalidAndroidSDK, string.Format(CultureInfo.CurrentCulture, LauncherResources.Error_InvalidAndroidSDK, _installPaths.SDKRoot));
                    }

                    try
                    {
                        adb.Start();
                        device = adb.GetDeviceById(_launchOptions.DeviceId);

                        // There is a rare case, which we have seen it a few times now with the Android emulator where the device will be initially
                        // in the offline state. But after a very short amount of time it comes online. Retry waiting for this.
                        if (device.GetState().HasFlag(DeviceState.Offline))
                        {
                            // Add in an extra progress step and update the dialog
                            progressStepCount++;

                            _waitLoop.SetProgress(progressStepCount, progressCurrentIndex, LauncherResources.Step_WaitingForDeviceToComeOnline);
                            progressCurrentIndex++;

                            const int waitTimePerIteration = 50;
                            const int maxTries = 5000 / waitTimePerIteration; // We will wait for up to 5 seconds

                            // NOTE: libadb has device discovery built in which we could allegedly use instead of a retry loop,
                            // but I couldn't get this to work (though the problem is so rare, I had a lot of trouble testing it), so just using
                            // a retry loop.
                            for (int cTry = 0; true; cTry++)
                            {
                                if (cTry == maxTries)
                                {
                                    throw new LauncherException(Telemetry.LaunchFailureCode.DeviceOffline, LauncherResources.Error_DeviceOffline);
                                }

                                // Sleep for a little while unless this operation is canceled
                                if (token.WaitHandle.WaitOne(waitTimePerIteration))
                                {
                                    throw new OperationCanceledException();
                                }

                                if (!device.GetState().HasFlag(DeviceState.Offline))
                                {
                                    break; // we are no longer offline
                                }
                            }
                        }
                    }
                    catch (AdbException)
                    {
                        throw new LauncherException(Telemetry.LaunchFailureCode.DeviceNotResponding, LauncherResources.Error_DeviceNotResponding);
                    }
                }));

                actions.Add(new NamedAction(LauncherResources.Step_InspectingDevice, () =>
                {
                    try
                    {
                        DeviceAbi deviceAbi = device.Abi;
                        if (deviceAbi == DeviceAbi.unknown)
                            deviceAbi = device.Abi2;

                        switch (_launchOptions.TargetArchitecture)
                        {
                            case TargetArchitecture.ARM:
                                if (deviceAbi != DeviceAbi.armeabi && deviceAbi != DeviceAbi.armeabiv7a)
                                {
                                    throw GetBadDeviceAbiException(deviceAbi);
                                }
                                break;

                            case TargetArchitecture.X86:
                                if (deviceAbi != DeviceAbi.x86)
                                {
                                    throw GetBadDeviceAbiException(deviceAbi);
                                }
                                break;

                            default:
                                Debug.Fail("New target architucture support added without updating this code???");
                                throw new InvalidOperationException();
                        }

                        if (_launchOptions.TargetArchitecture == TargetArchitecture.ARM && device.IsEmulator)
                        {
                            _isUsingArmEmulator = true;
                        }

                        _shell = device.Shell;
                    }
                    catch (AdbException)
                    {
                        throw new LauncherException(Telemetry.LaunchFailureCode.DeviceNotResponding, LauncherResources.Error_DeviceNotResponding);
                    }

                    VerifySdkVersion();

                    string pwdCommand = string.Concat("run-as ", _launchOptions.Package, " /system/bin/sh -c pwd");
                    ExecCommand(pwdCommand);
                    workingDirectory = ParsePwdOutput(_shell.Out);

                    // Kill old processes to make sure we aren't confused and think an old process is still arround
                    gdbServerRemotePath = workingDirectory + "/lib/gdbserver";
                    KillOldInstances(gdbServerRemotePath);

                    string lsCommand = string.Format(CultureInfo.InvariantCulture, "ls {0}", gdbServerRemotePath);
                    string output = ExecCommand(lsCommand);
                    if (string.Compare(output, gdbServerRemotePath, StringComparison.OrdinalIgnoreCase) != 0)
                    {
                        throw new LauncherException(Telemetry.LaunchFailureCode.NoGdbServer, string.Format(CultureInfo.CurrentCulture, LauncherResources.Error_NoGdbServer, gdbServerRemotePath));
                    }
                }));

                if (!_launchOptions.IsAttach)
                {
                    actions.Add(new NamedAction(LauncherResources.Step_StartingApp, () =>
                    {
                        string activateCommand = string.Concat("am start -D -n ", _launchOptions.Package, "/", _launchOptions.LaunchActivity);
                        ExecCommand(activateCommand);
                        ValidateActivityManagerOutput(activateCommand, _shell.Out);
                    }));
                }

                actions.Add(new NamedAction(LauncherResources.Step_GettingAppProcessId, () =>
                {
                    _appProcessId = GetAppProcessId();
                }));

                actions.Add(new NamedAction(LauncherResources.Step_StartGDBServer, () =>
                {
                    // We will default to using a unix socket with gdbserver as this is what the ndk-gdb script uses. Though we have seen
                    // some machines where this doesn't work and we fall back to TCP instead.
                    const bool useUnixSocket = true;

                    taskGdbServer = StartGdbServer(gdbServerRemotePath, workingDirectory, useUnixSocket, out gdbServerSocketDescription);
                }));

                actions.Add(new NamedAction(LauncherResources.Step_PortForwarding, () =>
                {
                    // TODO: Use a dynamic socket
                    gdbPortNumber = 5039;
                    _jdbPortNumber = 65534;

                    device.Forward(string.Format(CultureInfo.InvariantCulture, "tcp:{0}", gdbPortNumber), gdbServerSocketDescription);
                    if (!_launchOptions.IsAttach)
                    {
                        device.Forward(string.Format(CultureInfo.InvariantCulture, "tcp:{0}", _jdbPortNumber), string.Format(CultureInfo.InvariantCulture, "jdwp:{0}", _appProcessId));
                    }
                }));

                actions.Add(new NamedAction(LauncherResources.Step_DownloadingFiles, () =>
                {
                    //pull binaries from the emulator/device
                    var fileSystem = device.FileSystem;

                    fileSystem.Download(@"/system/bin/app_process", Path.Combine(_launchOptions.IntermediateDirectory, "app_process"), true);
                    fileSystem.Download(@"/system/bin/linker", Path.Combine(_launchOptions.IntermediateDirectory, "linker"), true);
                    fileSystem.Download(@"/system/lib/libc.so", Path.Combine(_launchOptions.IntermediateDirectory, "libc.so"), true);
                }));

                progressStepCount = actions.Count;

                foreach (NamedAction namedAction in actions)
                {
                    token.ThrowIfCancellationRequested();

                    _waitLoop.SetProgress(progressStepCount, progressCurrentIndex, namedAction.Name);
                    progressCurrentIndex++;
                    namedAction.Action();
                }

                _waitLoop.SetProgress(progressStepCount, progressStepCount, string.Empty);

                if (taskGdbServer.IsCompleted)
                {
                    token.ThrowIfCancellationRequested();
                    throw new LauncherException(Telemetry.LaunchFailureCode.GDBServerFailed, LauncherResources.Error_GDBServerFailed);
                }

                if (_launchOptions.LogcatServiceId != Guid.Empty)
                {
                    _eventCallback.OnCustomDebugEvent(_launchOptions.LogcatServiceId, new Guid(LogcatServiceMessage_SourceId), LogcatServiceMessage_NewProcess, _appProcessId, null);
                }

                var launchOptions = new LocalLaunchOptions(_installPaths.GDBPath, string.Format(CultureInfo.InvariantCulture, ":{0}", gdbPortNumber));
                launchOptions.AdditionalSOLibSearchPath = _launchOptions.AdditionalSOLibSearchPath;
                launchOptions.TargetArchitecture = _launchOptions.TargetArchitecture;
                launchOptions.WorkingDirectory = _launchOptions.IntermediateDirectory;
                launchOptions.ExePath = Path.Combine(_launchOptions.IntermediateDirectory, "app_process");
                launchOptions.DebuggerMIMode = MIMode.Gdb;
                launchOptions.VisualizerFile = "Microsoft.Android.natvis";

                return launchOptions;
            }
        }
예제 #28
0
파일: Launcher.cs 프로젝트: yeaicc/MIEngine
        private LaunchOptions SetupForDebuggingWorker(CancellationToken token)
        {
            CancellationTokenRegistration onCancelRegistration = token.Register(() =>
            {
                _gdbServerExecCancellationSource.Cancel();
            });

            using (onCancelRegistration)
            {
                // TODO: Adb exception messages should be improved. Example, if ADB is not started, this is returned:
                //    +		[libadb.AdbException]	{"Could not connect to the adb.exe server. See InnerException for details."}	libadb.AdbException
                // 'See InnerException for details.' should not be there. It should just add the inner exception message:
                //    [System.Net.Sockets.SocketException]	{"No connection could be made because the target machine actively refused it 127.0.0.1:5037"}	System.Net.Sockets.SocketException

                Device device = null;
                string workingDirectory = null;
                string gdbServerRemotePath = null;
                string gdbServerSocketDescription = null;
                string exePath = null;
                Task taskGdbServer = null;
                int gdbPortNumber = 0;
                int progressCurrentIndex = 0;
                int progressStepCount = 0;

                List<NamedAction> actions = new List<NamedAction>();

                actions.Add(new NamedAction(LauncherResources.Step_ResolveInstallPaths, () =>
                {
                    _installPaths = InstallPaths.Resolve(token, _launchOptions, Logger);
                }));

                actions.Add(new NamedAction(LauncherResources.Step_ConnectToDevice, () =>
                {
                    Adb adb;
                    try
                    {
                        adb = new Adb(_installPaths.SDKRoot);
                    }
                    catch (ArgumentException)
                    {
                        throw new LauncherException(Telemetry.LaunchFailureCode.InvalidAndroidSDK, string.Format(CultureInfo.CurrentCulture, LauncherResources.Error_InvalidAndroidSDK, _installPaths.SDKRoot));
                    }

                    try
                    {
                        adb.Start();
                        device = adb.GetDeviceById(_launchOptions.DeviceId);

                        // There is a rare case, which we have seen it a few times now with the Android emulator where the device will be initially
                        // in the offline state. But after a very short amount of time it comes online. Retry waiting for this.
                        if (device.GetState().HasFlag(DeviceState.Offline))
                        {
                            // Add in an extra progress step and update the dialog
                            progressStepCount++;

                            _waitLoop.SetProgress(progressStepCount, progressCurrentIndex, LauncherResources.Step_WaitingForDeviceToComeOnline);
                            progressCurrentIndex++;

                            const int waitTimePerIteration = 50;
                            const int maxTries = 5000 / waitTimePerIteration; // We will wait for up to 5 seconds

                            // NOTE: libadb has device discovery built in which we could allegedly use instead of a retry loop,
                            // but I couldn't get this to work (though the problem is so rare, I had a lot of trouble testing it), so just using
                            // a retry loop.
                            for (int cTry = 0; true; cTry++)
                            {
                                if (cTry == maxTries)
                                {
                                    throw new LauncherException(Telemetry.LaunchFailureCode.DeviceOffline, LauncherResources.Error_DeviceOffline);
                                }

                                // Sleep for a little while unless this operation is canceled
                                if (token.WaitHandle.WaitOne(waitTimePerIteration))
                                {
                                    throw new OperationCanceledException();
                                }

                                if (!device.GetState().HasFlag(DeviceState.Offline))
                                {
                                    break; // we are no longer offline
                                }
                            }
                        }
                    }
                    catch (AdbException)
                    {
                        throw new LauncherException(Telemetry.LaunchFailureCode.DeviceNotResponding, LauncherResources.Error_DeviceNotResponding);
                    }
                }));

                actions.Add(new NamedAction(LauncherResources.Step_InspectingDevice, () =>
                {
                    try
                    {
                        DeviceAbi[] allowedAbis;
                        switch (_launchOptions.TargetArchitecture)
                        {
                            case TargetArchitecture.ARM:
                                allowedAbis = new DeviceAbi[] { DeviceAbi.armeabi, DeviceAbi.armeabiv7a };
                                break;

                            case TargetArchitecture.ARM64:
                                allowedAbis = new DeviceAbi[] { DeviceAbi.arm64v8a };
                                break;

                            case TargetArchitecture.X86:
                                allowedAbis = new DeviceAbi[] { DeviceAbi.x86 };
                                break;

                            case TargetArchitecture.X64:
                                allowedAbis = new DeviceAbi[] { DeviceAbi.x64 };
                                break;

                            default:
                                Debug.Fail("New target architucture support added without updating this code???");
                                throw new InvalidOperationException();
                        }

                        if (!DoesDeviceSupportAnyAbi(device, allowedAbis))
                        {
                            throw GetBadDeviceAbiException(device.Abi);
                        }

                        if (_launchOptions.TargetArchitecture == TargetArchitecture.ARM && device.IsEmulator)
                        {
                            _isUsingArmEmulator = true;
                        }

                        _shell = device.Shell;
                    }
                    catch (AdbException)
                    {
                        throw new LauncherException(Telemetry.LaunchFailureCode.DeviceNotResponding, LauncherResources.Error_DeviceNotResponding);
                    }

                    VerifySdkVersion();

                    if (_targetEngine == TargetEngine.Native)
                    {
                        string pwdCommand = string.Concat("run-as ", _launchOptions.Package, " /system/bin/sh -c pwd");
                        ExecCommand(pwdCommand);
                        workingDirectory = PwdOutputParser.ExtractWorkingDirectory(_shell.Out, _launchOptions.Package);
                        gdbServerRemotePath = GetGdbServerPath(workingDirectory, device);

                        KillOldInstances(gdbServerRemotePath);
                    }
                }));

                if (!_launchOptions.IsAttach)
                {
                    actions.Add(new NamedAction(LauncherResources.Step_StartingApp, () =>
                    {
                        string activateCommand = string.Concat("am start -D -n ", _launchOptions.Package, "/", _launchOptions.LaunchActivity);
                        ExecCommand(activateCommand);
                        ValidateActivityManagerOutput(activateCommand, _shell.Out);
                    }));
                }

                actions.Add(new NamedAction(LauncherResources.Step_GettingAppProcessId, () =>
                {
                    _appProcessId = GetAppProcessId();
                }));

                if (_targetEngine == TargetEngine.Native)
                {
                    actions.Add(new NamedAction(LauncherResources.Step_StartGDBServer, () =>
                    {
                        // We will default to using a unix socket with gdbserver as this is what the ndk-gdb script uses. Though we have seen
                        // some machines where this doesn't work and we fall back to TCP instead.
                        const bool useUnixSocket = true;

                        taskGdbServer = StartGdbServer(gdbServerRemotePath, workingDirectory, useUnixSocket, out gdbServerSocketDescription);
                    }));
                }

                actions.Add(new NamedAction(LauncherResources.Step_PortForwarding, () =>
                {
                    // TODO: Use a dynamic socket
                    gdbPortNumber = 5039;
                    _jdbPortNumber = 65534;

                    if (_targetEngine == TargetEngine.Native)
                    {
                        device.Forward(string.Format(CultureInfo.InvariantCulture, "tcp:{0}", gdbPortNumber), gdbServerSocketDescription);
                    }

                    device.Forward(string.Format(CultureInfo.InvariantCulture, "tcp:{0}", _jdbPortNumber), string.Format(CultureInfo.InvariantCulture, "jdwp:{0}", _appProcessId));
                }));

                if (_targetEngine == TargetEngine.Native)
                {
                    actions.Add(new NamedAction(LauncherResources.Step_DownloadingFiles, () =>
                    {
                        //pull binaries from the emulator/device
                        var fileSystem = device.FileSystem;

                        string app_process_suffix = String.Empty;
                        switch (_launchOptions.TargetArchitecture)
                        {
                            case TargetArchitecture.X86:
                            case TargetArchitecture.ARM:
                                app_process_suffix = "32";
                                break;
                            case TargetArchitecture.X64:
                            case TargetArchitecture.ARM64:
                                app_process_suffix = "64";
                                break;
                            default:
                                Debug.Fail("Unsupported Target Architecture!");
                                break;
                        }

                        string app_process = String.Concat("app_process", app_process_suffix);
                        exePath = Path.Combine(_launchOptions.IntermediateDirectory, app_process);

                        bool retry = false;
                        try
                        {
                            fileSystem.Download(@"/system/bin/" + app_process, exePath, true);
                        }
                        catch (AdbException) when (String.Compare(app_process_suffix, "32", StringComparison.OrdinalIgnoreCase) == 0)
                        {
                            // Older devices don't have an 'app_process32', only an 'app_process', so retry
                            // NOTE: libadb doesn't have an error code property to verify that this is caused
                            // by the file not being found.
                            retry = true;
                        }

                        if (retry)
                        {
                            app_process = "app_process";
                            exePath = Path.Combine(_launchOptions.IntermediateDirectory, app_process);
                            fileSystem.Download(@"/system/bin/app_process", exePath, true);
                        }

                        //on 64 bit, 'linker64' is the 64bit version and 'linker' is the 32 bit version
                        string suffix64bit = String.Empty;
                        if (_launchOptions.TargetArchitecture == TargetArchitecture.X64 || _launchOptions.TargetArchitecture == TargetArchitecture.ARM64)
                        {
                            suffix64bit = "64";
                        }

                        string linker = String.Concat("linker", suffix64bit);
                        fileSystem.Download(String.Concat(@"/system/bin/", linker), Path.Combine(_launchOptions.IntermediateDirectory, linker), true);

                        //on 64 bit, libc.so lives in /system/lib64/, on 32 bit it lives in simply /system/lib/
                        fileSystem.Download(@"/system/lib" + suffix64bit + "/libc.so", Path.Combine(_launchOptions.IntermediateDirectory, "libc.so"), true);
                    }));
                }

                progressStepCount = actions.Count;

                foreach (NamedAction namedAction in actions)
                {
                    token.ThrowIfCancellationRequested();

                    _waitLoop.SetProgress(progressStepCount, progressCurrentIndex, namedAction.Name);
                    progressCurrentIndex++;
                    namedAction.Action();
                }

                _waitLoop.SetProgress(progressStepCount, progressStepCount, string.Empty);

                if (_targetEngine == TargetEngine.Native && taskGdbServer.IsCompleted)
                {
                    token.ThrowIfCancellationRequested();
                    throw new LauncherException(Telemetry.LaunchFailureCode.GDBServerFailed, LauncherResources.Error_GDBServerFailed);
                }

                if (_launchOptions.LogcatServiceId != Guid.Empty)
                {
                    _eventCallback.OnCustomDebugEvent(_launchOptions.LogcatServiceId, new Guid(LogcatServiceMessage_SourceId), LogcatServiceMessage_NewProcess, _appProcessId, null);
                }

                LaunchOptions launchOptions = null;
                if (_targetEngine == TargetEngine.Native)
                {
                    launchOptions = new LocalLaunchOptions(_installPaths.GDBPath, string.Format(CultureInfo.InvariantCulture, ":{0}", gdbPortNumber), 0, null);
                    launchOptions.ExePath = exePath;
                }
                else
                {
                    launchOptions = new JavaLaunchOptions(_launchOptions.JVMHost, _launchOptions.JVMPort, _launchOptions.SourceRoots, _launchOptions.Package);
                }

                launchOptions.AdditionalSOLibSearchPath = _launchOptions.AdditionalSOLibSearchPath;
                launchOptions.AbsolutePrefixSOLibSearchPath = _launchOptions.AbsolutePrefixSOLibSearchPath;

                // The default ABI is 'Cygwin' in the Android NDK >= r11 for Windows.
                launchOptions.SetupCommands = new ReadOnlyCollection<LaunchCommand>( new LaunchCommand[]
                {
                    new LaunchCommand("-gdb-set osabi GNU/Linux")
                });

                launchOptions.TargetArchitecture = _launchOptions.TargetArchitecture;
                launchOptions.WorkingDirectory = _launchOptions.IntermediateDirectory;

                launchOptions.DebuggerMIMode = MIMode.Gdb;

                launchOptions.VisualizerFile = "Microsoft.Android.natvis";
                launchOptions.WaitDynamicLibLoad = _launchOptions.WaitDynamicLibLoad;

                return launchOptions;
            }
        }
예제 #29
0
        private void CheckCygwin(List<LaunchCommand> commands, LocalLaunchOptions localLaunchOptions)
        {
            // If running locally on windows, determine if gdb is running from cygwin
            if (localLaunchOptions != null && PlatformUtilities.IsWindows() && this.MICommandFactory.Mode == MIMode.Gdb)
            {
                // mingw will not implement this command, but to be safe, also check if the results contains the string cygwin.
                LaunchCommand lc = new LaunchCommand("show configuration", null, true, null, new Action<string>((string resStr) =>
                {
                    if (resStr.Contains("cygwin"))
                    {
                        this.IsCygwin = true;
                        this.CygwinFilePathMapper = new CygwinFilePathMapper(this);

                        _engineTelemetry.SendWindowsRuntimeEnvironment(EngineTelemetry.WindowsRuntimeEnvironment.Cygwin);
                    }
                    else
                    {
                        // Gdb on windows and not cygwin implies mingw
                        _engineTelemetry.SendWindowsRuntimeEnvironment(EngineTelemetry.WindowsRuntimeEnvironment.MinGW);
                    }
                }));
                commands.Add(lc);
            }
        }
예제 #30
0
        public override async void Init(ITransportCallback transportCallback, LaunchOptions options, Logger logger, HostWaitLoop waitLoop = null)
        {
            LocalLaunchOptions localOptions = options as LocalLaunchOptions;

            Encoding encNoBom = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false);

            string        commandPipeName;
            string        outputPipeName;
            string        pidPipeName;
            List <string> cmdArgs = new List <string>();

            string windowtitle = FormattableString.Invariant($"cppdbg: {Path.GetFileName(options.ExePath)}");

            if (PlatformUtilities.IsWindows())
            {
                // Create Windows Named pipes
                commandPipeName = Utilities.GetMIEngineTemporaryFilename("In");
                outputPipeName  = Utilities.GetMIEngineTemporaryFilename("Out");
                pidPipeName     = Utilities.GetMIEngineTemporaryFilename("Pid");
                string errorPipeName = Utilities.GetMIEngineTemporaryFilename("Error");

                NamedPipeServerStream inputToDebugger    = new NamedPipeServerStream(commandPipeName, PipeDirection.Out, 1, PipeTransmissionMode.Byte);
                NamedPipeServerStream outputFromDebugger = new NamedPipeServerStream(outputPipeName, PipeDirection.In, 1, PipeTransmissionMode.Byte);
                NamedPipeServerStream errorFromDebugger  = new NamedPipeServerStream(errorPipeName, PipeDirection.In, 1, PipeTransmissionMode.Byte);
                NamedPipeServerStream pidPipe            = new NamedPipeServerStream(pidPipeName, PipeDirection.In, 1, PipeTransmissionMode.Byte);

                _pidReader = new StreamReader(pidPipe, encNoBom, false, UnixUtilities.StreamBufferSize);

                string thisModulePath = typeof(RunInTerminalTransport).GetTypeInfo().Assembly.ManifestModule.FullyQualifiedName;
                string launchCommand  = Path.Combine(Path.GetDirectoryName(thisModulePath), "WindowsDebugLauncher.exe");

                if (!File.Exists(launchCommand))
                {
                    string errorMessage = string.Format(CultureInfo.CurrentCulture, MICoreResources.Error_InternalFileMissing, launchCommand);
                    transportCallback.OnStdErrorLine(errorMessage);
                    transportCallback.OnDebuggerProcessExit(null);
                    return;
                }

                cmdArgs.Add(launchCommand);
                cmdArgs.Add("--stdin=" + commandPipeName);
                cmdArgs.Add("--stdout=" + outputPipeName);
                cmdArgs.Add("--stderr=" + errorPipeName);
                cmdArgs.Add("--pid=" + pidPipeName);
                cmdArgs.Add("--dbgExe=" + localOptions.MIDebuggerPath);
                cmdArgs.Add(localOptions.GetMiDebuggerArgs());

                _waitForConnection = Task.WhenAll(
                    inputToDebugger.WaitForConnectionAsync(),
                    outputFromDebugger.WaitForConnectionAsync(),
                    errorFromDebugger.WaitForConnectionAsync(),
                    pidPipe.WaitForConnectionAsync());

                _commandStream = new StreamWriter(inputToDebugger, encNoBom);
                _outputStream  = new StreamReader(outputFromDebugger, encNoBom, false, UnixUtilities.StreamBufferSize);
                _errorStream   = new StreamReader(errorFromDebugger, encNoBom, false, UnixUtilities.StreamBufferSize);
            }
            else
            {
                // Do Linux style pipes
                commandPipeName = UnixUtilities.MakeFifo(identifier: "In", logger: logger);
                outputPipeName  = UnixUtilities.MakeFifo(identifier: "Out", logger: logger);
                pidPipeName     = UnixUtilities.MakeFifo(identifier: "Pid", logger: logger);

                // Create filestreams
                FileStream stdInStream  = new FileStream(commandPipeName, FileMode.Open);
                FileStream stdOutStream = new FileStream(outputPipeName, FileMode.Open);
                _pidReader = new StreamReader(new FileStream(pidPipeName, FileMode.Open), encNoBom, false, UnixUtilities.StreamBufferSize);

                string debuggerCmd = UnixUtilities.GetDebuggerCommand(localOptions);

                // Default working directory is next to the app
                string debuggeeDir;
                if (Path.IsPathRooted(options.ExePath) && File.Exists(options.ExePath))
                {
                    debuggeeDir = Path.GetDirectoryName(options.ExePath);
                }
                else
                {
                    // If we don't know where the app is, default to HOME, and if we somehow can't get that, go with the root directory.
                    debuggeeDir = Environment.GetEnvironmentVariable("HOME");
                    if (string.IsNullOrEmpty(debuggeeDir))
                    {
                        debuggeeDir = "/";
                    }
                }

                string dbgCmdScript          = Path.Combine(Path.GetTempPath(), Utilities.GetMIEngineTemporaryFilename(identifier: "Cmd"));
                string launchDebuggerCommand = UnixUtilities.LaunchLocalDebuggerCommand(
                    debuggeeDir,
                    commandPipeName,
                    outputPipeName,
                    pidPipeName,
                    dbgCmdScript,
                    debuggerCmd,
                    localOptions.GetMiDebuggerArgs());

                logger?.WriteTextBlock("DbgCmd:", launchDebuggerCommand);

                using (FileStream dbgCmdStream = new FileStream(dbgCmdScript, FileMode.CreateNew))
                    using (StreamWriter dbgCmdWriter = new StreamWriter(dbgCmdStream, encNoBom)
                    {
                        AutoFlush = true
                    })
                    {
                        dbgCmdWriter.WriteLine("#!/bin/sh");
                        dbgCmdWriter.Write(launchDebuggerCommand);
                        dbgCmdWriter.Flush();
                    }

                if (PlatformUtilities.IsOSX())
                {
                    string osxLaunchScript = GetOSXLaunchScript();

                    // Call osascript with a path to the AppleScript. The apple script takes 2 parameters: a title for the terminal and the launch script.
                    cmdArgs.Add("/usr/bin/osascript");
                    cmdArgs.Add(osxLaunchScript);
                    cmdArgs.Add(FormattableString.Invariant($"\"{windowtitle}\""));
                    cmdArgs.Add(FormattableString.Invariant($"sh {dbgCmdScript} ;")); // needs a semicolon because this command is running through the launchscript.
                }
                else
                {
                    cmdArgs.Add("sh");
                    cmdArgs.Add(dbgCmdScript);
                }

                _outputStream  = new StreamReader(stdOutStream, encNoBom, false, UnixUtilities.StreamBufferSize);
                _commandStream = new StreamWriter(stdInStream, encNoBom);
            }

            RunInTerminalLauncher launcher = new RunInTerminalLauncher(windowtitle, localOptions.Environment);

            launcher.Launch(
                cmdArgs,
                localOptions.UseExternalConsole,
                LaunchSuccess,
                (error) =>
            {
                transportCallback.OnStdErrorLine(error);
                throw new InvalidOperationException(error);
            },
                logger);
            logger?.WriteLine("Wait for connection completion.");

            if (_waitForConnection != null)
            {
                // Add a timeout for waiting for connection - 20 seconds
                Task  waitOrTimeout = Task.WhenAny(_waitForConnection, Task.Delay(20000));
                await waitOrTimeout;
                if (waitOrTimeout.Status != TaskStatus.RanToCompletion)
                {
                    string errorMessage = String.Format(CultureInfo.CurrentCulture, MICoreResources.Error_DebuggerInitializeFailed_NoStdErr, "WindowsDebugLauncher.exe");
                    transportCallback.OnStdErrorLine(errorMessage);
                    transportCallback.OnDebuggerProcessExit(null);
                    return;
                }
            }

            base.Init(transportCallback, options, logger, waitLoop);
        }