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); }
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); }
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); }
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); }
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; } }
static internal LocalLaunchOptions CreateFromXml(Xml.LaunchOptions.LocalLaunchOptions source) { var options = new LocalLaunchOptions(RequireAttribute(source.MIDebuggerPath, "MIDebuggerPath"), source.MIDebuggerServerAddress); options.InitializeCommonOptions(source); return(options); }
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); }
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); }
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)); } }
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); }
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); } }
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); }
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); }
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); }
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); }
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); }
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); } }
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; } }
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); }
/// <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; }
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); }
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); }
/// <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); }
internal static LocalLaunchOptions CreateFromXml(Xml.LaunchOptions.LocalLaunchOptions source) { var options = new LocalLaunchOptions(RequireAttribute(source.MIDebuggerPath, "MIDebuggerPath"), source.MIDebuggerServerAddress); options.InitializeCommonOptions(source); return options; }
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); }
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; } }
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; } }
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); } }
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); }