public void Init(ITransportCallback transportCallback, LaunchOptions options) { SerialLaunchOptions serialOptions = (SerialLaunchOptions)options; string line; bool bLoggedIn = false; _bQuit = false; _callback = transportCallback; _thread = new Thread(TransportLoop); _port.Open(); Echo(""); for (;;) { line = GetLine(); if (line != null) { if (line.EndsWith("login: "******"root"); Debug.WriteLine("DBG:Logged into device"); bLoggedIn = true; } else if (line.EndsWith("$ ", StringComparison.Ordinal)) { Debug.WriteLine("DBG:Command prompt detected"); break; } } } Debug.WriteLine("DBG:GDB is starting"); _thread.Start(); }
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 virtual void Init(ITransportCallback transportCallback, LaunchOptions options, Logger logger) { Logger = logger; _callback = transportCallback; InitStreams(options, out _reader, out _writer); StartThread(GetThreadName()); }
void IPlatformAppLauncher.SetupForDebugging(out LaunchOptions debuggerLaunchOptions) { if (_launchOptions == null) { Debug.Fail("Why is SetupForDebugging being called before ParseLaunchOptions?"); throw new InvalidOperationException(); } _client = VcRemoteClient.GetInstance(_launchOptions); _remotePorts = _client.StartDebugListener(); if (_launchOptions.IOSDebugTarget == IOSDebugTarget.Device) { _appRemotePath = _client.GetRemoteAppPath(); } debuggerLaunchOptions = new TcpLaunchOptions(_launchOptions.RemoteMachineName, _remotePorts.DebugListenerPort, _launchOptions.Secure); (debuggerLaunchOptions as TcpLaunchOptions).ServerCertificateValidationCallback = _client.ServerCertificateValidationCallback; debuggerLaunchOptions.TargetArchitecture = _launchOptions.TargetArchitecture; debuggerLaunchOptions.AdditionalSOLibSearchPath = _launchOptions.AdditionalSOLibSearchPath; debuggerLaunchOptions.DebuggerMIMode = MIMode.Lldb; debuggerLaunchOptions.CustomLaunchSetupCommands = GetCustomLaunchSetupCommands(); debuggerLaunchOptions.LaunchCompleteCommand = GetLaunchCompleteCommand(); }
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"; // 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 void Init(ITransportCallback transportCallback, LaunchOptions options, Logger logger, HostWaitLoop waitLoop = null) { _launchOptions = (UnixShellPortLaunchOptions)options; _callback = transportCallback; _logger = logger; _startRemoteDebuggerCommand = _launchOptions.StartRemoteDebuggerCommand; if (_launchOptions.DebuggerMIMode == MIMode.Clrdbg) { if (!UnixShellPortLaunchOptions.HasSuccessfulPreviousLaunch(_launchOptions)) { waitLoop?.SetText(MICoreResources.Info_InstallingDebuggerOnRemote); try { Task.Run(() => DownloadAndCopyFileToRemote(_launchOptions.DebuggerInstallationDirectory, _launchOptions.GetClrDbgUrl)).Wait(); } catch (Exception e) { // Even if downloading & copying to remote fails, we will still try to invoke the script as it might already exist. string message = String.Format(CultureInfo.CurrentUICulture, MICoreResources.Warning_DownloadingClrDbgToRemote, e.Message); _callback.AppendToInitializationLog(message); } } } _callback.AppendToInitializationLog(string.Format(CultureInfo.CurrentUICulture, MICoreResources.Info_StartingUnixCommand, _startRemoteDebuggerCommand)); _launchOptions.UnixPort.BeginExecuteAsyncCommand(_startRemoteDebuggerCommand, this, out _asyncCommand); }
void IPlatformAppLauncher.SetupForDebugging(out LaunchOptions result) { if (_launchOptions == null) { Debug.Fail("Why is SetupForDebugging being called before ParseLaunchOptions?"); throw new InvalidOperationException(); } ManualResetEvent doneEvent = new ManualResetEvent(false); var cancellationTokenSource = new CancellationTokenSource(); ExceptionDispatchInfo exceptionDispatchInfo = null; LaunchOptions localLaunchOptions = null; _waitLoop = new HostWaitLoop(LauncherResources.WaitDialogText); // Do the work on a worker thread to avoid blocking the UI. Use ThreadPool.QueueUserWorkItem instead // of Task.Run to avoid needing to unwrap the AggregateException. ThreadPool.QueueUserWorkItem((object o) => { string launchErrorTelemetryResult = null; try { localLaunchOptions = SetupForDebuggingWorker(cancellationTokenSource.Token); launchErrorTelemetryResult = "None"; } catch (Exception e) { exceptionDispatchInfo = ExceptionDispatchInfo.Capture(e); if (!(e is OperationCanceledException)) { launchErrorTelemetryResult = Telemetry.GetLaunchErrorResultValue(e); } } doneEvent.Set(); if (launchErrorTelemetryResult != null) { Telemetry.SendLaunchError(launchErrorTelemetryResult, _targetEngine.ToString()); } } ); _waitLoop.Wait(doneEvent, cancellationTokenSource); if (exceptionDispatchInfo != null) { exceptionDispatchInfo.Throw(); } if (localLaunchOptions == null) { Debug.Fail("No result provided? Should be impossible."); throw new InvalidOperationException(); } result = localLaunchOptions; }
public void Init(ITransportCallback transportCallback, LaunchOptions options, Logger logger, HostWaitLoop waitLoop = null) { _launchTimeout = ((LocalLaunchOptions)options).ServerLaunchTimeout; _serverTransport.Init(transportCallback, options, logger, waitLoop); WaitForStart(); if (!_clientTransport.IsClosed) { _clientTransport.Init(transportCallback, options, logger, waitLoop); } }
public void Init(ITransportCallback transportCallback, LaunchOptions options) { _launchTimeout = ((LocalLaunchOptions)options).ServerLaunchTimeout; _serverTransport.Init(transportCallback, options); WaitForStart(); if (!_clientTransport.IsClosed) { _clientTransport.Init(transportCallback, options); } }
public void Init(ITransportCallback transportCallback, LaunchOptions options) { _bQuit = false; _callback = transportCallback; _commandEvent = new AutoResetEvent(false); _reader = new StreamReader(File.OpenRead(_filename)); _thread = new Thread(TransportLoop); _nextCommand = null; _thread.Start(); }
public override void InitStreams(LaunchOptions options, out StreamReader reader, out StreamWriter writer) { PipeLaunchOptions pipeOptions = (PipeLaunchOptions)options; Process proc = new Process(); proc.StartInfo.FileName = pipeOptions.PipePath; proc.StartInfo.Arguments = pipeOptions.PipeArguments; proc.StartInfo.WorkingDirectory = System.IO.Path.GetDirectoryName(pipeOptions.PipePath); InitProcess(proc, out reader, out writer); }
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 void Init(ITransportCallback transportCallback, LaunchOptions options, Logger logger, HostWaitLoop waitLoop = null) { _launchOptions = (UnixShellPortLaunchOptions)options; _callback = transportCallback; _logger = logger; _startRemoteDebuggerCommand = _launchOptions.StartRemoteDebuggerCommand; waitLoop?.SetText(MICoreResources.Info_InstallingDebuggerOnRemote); _callback.AppendToInitializationLog("Starting unix command: " + _startRemoteDebuggerCommand); _launchOptions.UnixPort.BeginExecuteAsyncCommand(_startRemoteDebuggerCommand, this, out _asyncCommand); }
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); }
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 watcherDirectory = _dbgStdInName.Substring(0, _dbgStdInName.IndexOf(UnixUtilities.FifoPrefix, StringComparison.OrdinalIgnoreCase)); _fifoWatcher = new FileSystemWatcher(watcherDirectory, UnixUtilities.FifoPrefix + "*"); _fifoWatcher.Deleted += FifoWatcher_Deleted; _fifoWatcher.EnableRaisingEvents = true; // Setup the streams on the fifos as soon as possible. FileStream dbgStdInStream = new FileStream(_dbgStdInName, FileMode.Open); FileStream dbgStdOutStream = new FileStream(_dbgStdOutName, FileMode.Open); string debuggerCmd = UnixUtilities.GetDebuggerCommand(localOptions); string launchDebuggerCommand = UnixUtilities.LaunchLocalDebuggerCommand( debuggeeDir, debuggerCmd, _dbgStdInName, _dbgStdOutName ); TerminalLauncher terminal = TerminalLauncher.MakeTerminal("DebuggerTerminal", launchDebuggerCommand, localOptions.Environment); terminal.Launch(debuggeeDir); // 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), 1024 * 4); reader = new StreamReader(dbgStdOutStream, Encoding.UTF8, true, 1024 * 4); }
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); } // Allow to execute custom commands before launching debugger. // For ex., instructing GDB not to break for certain signals if (options.DebuggerMIMode == MIMode.Gdb) { var gdbInitFile = Path.Combine(options.WorkingDirectory, ".gdbinit"); if (File.Exists(gdbInitFile)) { proc.StartInfo.Arguments += " -x \"" + gdbInitFile + "\""; } } // 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) { TcpLaunchOptions tcpOptions = (TcpLaunchOptions)options; _client = new TcpClient(); _client.Connect(tcpOptions.Hostname, tcpOptions.Port); if (tcpOptions.Secure) { RemoteCertificateValidationCallback callback; if (tcpOptions.ServerCertificateValidationCallback == null) { //if no callback specified, accept any certificate callback = delegate (object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) { return sslPolicyErrors == SslPolicyErrors.None; }; } else { //else use the callback specified callback = tcpOptions.ServerCertificateValidationCallback; } var certStore = new X509Store(StoreName.My, StoreLocation.CurrentUser); certStore.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly); SslStream sslStream = new SslStream( _client.GetStream(), false /* leaveInnerStreamOpen */, callback, null /*UserCertificateSelectionCallback */ ); sslStream.AuthenticateAsClient(tcpOptions.Hostname, certStore.Certificates, System.Security.Authentication.SslProtocols.Tls, false /* checkCertificateRevocation */); reader = new StreamReader(sslStream); writer = new StreamWriter(sslStream); } else { reader = new StreamReader(_client.GetStream()); writer = new StreamWriter(_client.GetStream()); } }
public override void InitStreams(LaunchOptions options, out StreamReader reader, out StreamWriter writer) { TcpLaunchOptions tcpOptions = (TcpLaunchOptions)options; _client = new TcpClient(); _client.ConnectAsync(tcpOptions.Hostname, tcpOptions.Port).Wait(); if (tcpOptions.Secure) { RemoteCertificateValidationCallback callback; if (tcpOptions.ServerCertificateValidationCallback == null) { //if no callback specified, accept any certificate callback = delegate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) { return(sslPolicyErrors == SslPolicyErrors.None); }; } else { //else use the callback specified callback = (object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) => tcpOptions.ServerCertificateValidationCallback(sender, certificate, chain, sslPolicyErrors); } var certStore = new X509Store(StoreName.My, StoreLocation.CurrentUser); certStore.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly); SslStream sslStream = new SslStream( _client.GetStream(), false /* leaveInnerStreamOpen */, callback, null /*UserCertificateSelectionCallback */ ); sslStream.AuthenticateAsClientAsync(tcpOptions.Hostname, certStore.Certificates, System.Security.Authentication.SslProtocols.Tls, false /* checkCertificateRevocation */).Wait(); reader = new StreamReader(sslStream); writer = new StreamWriter(sslStream); } else { reader = new StreamReader(_client.GetStream()); writer = new StreamWriter(_client.GetStream()); } }
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); }
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); }
void IPlatformAppLauncher.SetupForDebugging(out LaunchOptions debuggerLaunchOptions) { if (_launchOptions == null) { Debug.Fail("Why is SetupForDebugging being called before ParseLaunchOptions?"); throw new InvalidOperationException(); } string targetMachineName = LaunchOptions.RequireAttribute(_launchOptions.TargetMachine, "TargetMachine"); var port = new AD7Port(new AD7PortSupplier(), targetMachineName, isInAddPort: false); // NOTE: this may put up a dialog and/or throw an AD7ConnectCanceledException port.EnsureConnected(); debuggerLaunchOptions = new UnixShellPortLaunchOptions(_launchOptions.StartRemoteDebuggerCommand, port, LaunchOptions.ConvertMIModeAttribute(_launchOptions.MIMode), _launchOptions); }
public void Init(ITransport transport, LaunchOptions options) { _lastCommandId = 1000; _transport = transport; FlushBreakStateData(); _transport.Init(this, options); switch (options.TargetArchitecture) { case TargetArchitecture.ARM: MaxInstructionSize = 4; Is64BitArch = false; break; case TargetArchitecture.ARM64: MaxInstructionSize = 8; Is64BitArch = true; break; case TargetArchitecture.X86: MaxInstructionSize = 20; Is64BitArch = false; break; case TargetArchitecture.X64: MaxInstructionSize = 26; Is64BitArch = true; break; case TargetArchitecture.Mips: MaxInstructionSize = 4; Is64BitArch = false; break; default: throw new ArgumentOutOfRangeException("options.TargetArchitecture"); } }
public abstract void InitStreams(LaunchOptions options, out StreamReader reader, out StreamWriter writer);
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); }
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 DebuggedProcess(bool bLaunched, LaunchOptions launchOptions, ISampleEngineCallback callback, WorkerThread worker, BreakpointManager bpman, AD7Engine engine, HostConfigurationStore configStore, HostWaitLoop waitLoop = null) : base(launchOptions, engine.Logger) { uint processExitCode = 0; _pendingMessages = new StringBuilder(400); _worker = worker; _breakpointManager = bpman; Engine = engine; _libraryLoaded = new List<string>(); _loadOrder = 0; MICommandFactory = MICommandFactory.GetInstance(launchOptions.DebuggerMIMode, this); _waitDialog = (MICommandFactory.SupportsStopOnDynamicLibLoad() && launchOptions.WaitDynamicLibLoad) ? new HostWaitDialog(ResourceStrings.LoadingSymbolMessage, ResourceStrings.LoadingSymbolCaption) : null; Natvis = new Natvis.Natvis(this, launchOptions.ShowDisplayString); // we do NOT have real Win32 process IDs, so we use a guid AD_PROCESS_ID pid = new AD_PROCESS_ID(); pid.ProcessIdType = (int)enum_AD_PROCESS_ID.AD_PROCESS_ID_GUID; pid.guidProcessId = Guid.NewGuid(); this.Id = pid; SourceLineCache = new SourceLineCache(this); _callback = callback; _moduleList = new List<DebuggedModule>(); ThreadCache = new ThreadCache(callback, this); Disassembly = new Disassembly(this); ExceptionManager = new ExceptionManager(MICommandFactory, _worker, _callback, configStore); VariablesToDelete = new List<string>(); this.ActiveVariables = new List<IVariableInformation>(); _fileTimestampWarnings = new HashSet<Tuple<string, string>>(); OutputStringEvent += delegate (object o, string message) { // We can get messages before we have started the process // but we can't send them on until it is if (_connected) { _callback.OnOutputString(message); } else { _pendingMessages.Append(message); } }; LibraryLoadEvent += delegate (object o, EventArgs args) { ResultEventArgs results = args as MICore.Debugger.ResultEventArgs; string file = results.Results.TryFindString("host-name"); if (!string.IsNullOrEmpty(file) && MICommandFactory.SupportsStopOnDynamicLibLoad()) { _libraryLoaded.Add(file); if (_waitDialog != null) { _waitDialog.ShowWaitDialog(file); } } else if (this.MICommandFactory.Mode == MIMode.Clrdbg) { string id = results.Results.FindString("id"); ulong baseAddr = results.Results.FindAddr("base-address"); uint size = results.Results.FindUint("size"); bool symbolsLoaded = results.Results.FindInt("symbols-loaded") != 0; var module = new DebuggedModule(id, file, baseAddr, size, symbolsLoaded, string.Empty, _loadOrder++); lock (_moduleList) { _moduleList.Add(module); } _callback.OnModuleLoad(module); } else if (!string.IsNullOrEmpty(file)) { string addr = results.Results.TryFindString("loaded_addr"); if (string.IsNullOrEmpty(addr) || addr == "-") { return; // identifies the exe, not a real load } // generate module string id = results.Results.TryFindString("name"); bool symsLoaded = true; string symPath = null; if (results.Results.Contains("symbols-path")) { symPath = results.Results.FindString("symbols-path"); if (string.IsNullOrEmpty(symPath)) { symsLoaded = false; } } else { symPath = file; } ulong loadAddr = results.Results.FindAddr("loaded_addr"); uint size = results.Results.FindUint("size"); if (String.IsNullOrEmpty(id)) { id = file; } var module = FindModule(id); if (module == null) { module = new DebuggedModule(id, file, loadAddr, size, symsLoaded, symPath, _loadOrder++); lock (_moduleList) { _moduleList.Add(module); } _callback.OnModuleLoad(module); } } }; if (_launchOptions is LocalLaunchOptions) { LocalLaunchOptions localLaunchOptions = (LocalLaunchOptions)_launchOptions; if (!localLaunchOptions.IsValidMiDebuggerPath()) { throw new Exception(MICoreResources.Error_InvalidMiDebuggerPath); } if (PlatformUtilities.IsOSX() && localLaunchOptions.DebuggerMIMode != MIMode.Clrdbg && localLaunchOptions.DebuggerMIMode != MIMode.Lldb && !UnixUtilities.IsBinarySigned(localLaunchOptions.MIDebuggerPath)) { string message = String.Format(CultureInfo.CurrentCulture, ResourceStrings.Warning_DarwinDebuggerUnsigned, localLaunchOptions.MIDebuggerPath); _callback.OnOutputMessage(new OutputMessage( message + Environment.NewLine, enum_MESSAGETYPE.MT_MESSAGEBOX, OutputMessage.Severity.Warning)); } ITransport localTransport = null; // For local Linux and OS X launch, use the local Unix transport which creates a new terminal and // uses fifos for debugger (e.g., gdb) communication. if (this.MICommandFactory.UseExternalConsoleForLocalLaunch(localLaunchOptions) && (PlatformUtilities.IsLinux() || (PlatformUtilities.IsOSX() && localLaunchOptions.DebuggerMIMode != MIMode.Lldb))) { localTransport = new LocalUnixTerminalTransport(); // Only need to clear terminal for Linux and OS X local launch _needTerminalReset = (localLaunchOptions.ProcessId == 0 && _launchOptions.DebuggerMIMode == MIMode.Gdb); } else { localTransport = new LocalTransport(); } if (localLaunchOptions.ShouldStartServer()) { this.Init( new MICore.ClientServerTransport( localTransport, new ServerTransport(killOnClose: true, filterStdout: localLaunchOptions.FilterStdout, filterStderr: localLaunchOptions.FilterStderr) ), _launchOptions); } else { this.Init(localTransport, _launchOptions); } // Only need to know the debugger pid on Linux and OS X local launch to detect whether // the debugger is closed. If the debugger is not running anymore, the response (^exit) // to the -gdb-exit command is faked to allow MIEngine to shut down. SetDebuggerPid(localTransport.DebuggerPid); } else if (_launchOptions is PipeLaunchOptions) { this.Init(new MICore.PipeTransport(), _launchOptions); } else if (_launchOptions is TcpLaunchOptions) { this.Init(new MICore.TcpTransport(), _launchOptions); } else if (_launchOptions is UnixShellPortLaunchOptions) { this.Init(new MICore.UnixShellPortTransport(), _launchOptions, waitLoop); } else { throw new ArgumentOutOfRangeException("LaunchInfo.options"); } MIDebugCommandDispatcher.AddProcess(this); // When the debuggee exits, we need to exit the debugger ProcessExitEvent += delegate (object o, EventArgs args) { // NOTE: Exceptions leaked from this method may cause VS to crash, be careful ResultEventArgs results = args as MICore.Debugger.ResultEventArgs; if (results.Results.Contains("exit-code")) { // GDB sometimes returns exit codes, which don't fit into uint, like "030000000472". // And we can't throw from here, because it crashes VS. // Full exit code will still usually be reported in the Output window, // but here let's return "uint.MaxValue" just to indicate that something went wrong. if (!uint.TryParse(results.Results.FindString("exit-code"), out processExitCode)) { processExitCode = uint.MaxValue; } } // quit MI Debugger if (!this.IsClosed) { _worker.PostOperation(CmdExitAsync); } else { // If we are already closed, make sure that something sends program destroy _callback.OnProcessExit(processExitCode); } if (_waitDialog != null) { _waitDialog.EndWaitDialog(); } }; // When the debugger exits, we tell AD7 we are done DebuggerExitEvent += delegate (object o, EventArgs args) { // NOTE: Exceptions leaked from this method may cause VS to crash, be careful // this is the last AD7 Event we can ever send // Also the transport is closed when this returns _callback.OnProcessExit(processExitCode); Dispose(); }; DebuggerAbortedEvent += delegate (object o, DebuggerAbortedEventArgs eventArgs) { // NOTE: Exceptions leaked from this method may cause VS to crash, be careful // The MI debugger process unexpectedly exited. _worker.PostOperation(() => { _engineTelemetry.SendDebuggerAborted(MICommandFactory, GetLastSentCommandName(), eventArgs.ExitCode); // If the MI Debugger exits before we get a resume call, we have no way of sending program destroy. So just let start debugging fail. if (!_connected) { return; } _callback.OnError(string.Concat(eventArgs.Message, " ", ResourceStrings.DebuggingWillAbort)); _callback.OnProcessExit(uint.MaxValue); Dispose(); }); }; ModuleLoadEvent += async delegate (object o, EventArgs args) { // NOTE: This is an async void method, so make sure exceptions are caught and somehow reported if (_needTerminalReset) { _needTerminalReset = false; // This is to work around a GDB bug of warning "Failed to set controlling terminal: Operation not permitted" // Reset debuggee terminal after the first module load. await ResetConsole(); } if (this.MICommandFactory.SupportsStopOnDynamicLibLoad() && !_launchOptions.WaitDynamicLibLoad) { await CmdAsync("-gdb-set stop-on-solib-events 0", ResultClass.None); } await this.EnsureModulesLoaded(); if (_waitDialog != null) { _waitDialog.EndWaitDialog(); } if (MICommandFactory.SupportsStopOnDynamicLibLoad()) { // Do not continue if debugging core dump if (!this.IsCoreDump) { CmdContinueAsync(); } } }; // When we break we need to gather information BreakModeEvent += async delegate (object o, EventArgs args) { // NOTE: This is an async void method, so make sure exceptions are caught and somehow reported StoppingEventArgs results = args as MICore.Debugger.StoppingEventArgs; if (_waitDialog != null) { _waitDialog.EndWaitDialog(); } if (!this._connected) { _initialBreakArgs = results; return; } try { await HandleBreakModeEvent(results, results.AsyncRequest); } catch (Exception e) when (ExceptionHelper.BeforeCatch(e, Logger, reportOnlyCorrupting: true)) { if (this.IsStopDebuggingInProgress) { return; // ignore exceptions after the process has exited } string exceptionDescription = EngineUtils.GetExceptionDescription(e); string message = string.Format(CultureInfo.CurrentCulture, MICoreResources.Error_FailedToEnterBreakState, exceptionDescription); _callback.OnError(message); Terminate(); } }; ErrorEvent += delegate (object o, EventArgs args) { // NOTE: Exceptions leaked from this method may cause VS to crash, be careful ResultEventArgs result = (ResultEventArgs)args; _callback.OnError(result.Results.FindString("msg")); }; ThreadCreatedEvent += delegate (object o, EventArgs args) { ResultEventArgs result = (ResultEventArgs)args; ThreadCache.ThreadCreatedEvent(result.Results.FindInt("id"), result.Results.TryFindString("group-id")); _childProcessHandler?.ThreadCreatedEvent(result.Results); }; ThreadExitedEvent += delegate (object o, EventArgs args) { ResultEventArgs result = (ResultEventArgs)args; ThreadCache.ThreadExitedEvent(result.Results.FindInt("id")); }; ThreadGroupExitedEvent += delegate (object o, EventArgs args) { ResultEventArgs result = (ResultEventArgs)args; ThreadCache.ThreadGroupExitedEvent(result.Results.FindString("id")); }; MessageEvent += (object o, ResultEventArgs args) => { OutputMessage outputMessage = DecodeOutputEvent(args.Results); if (outputMessage != null) { _callback.OnOutputMessage(outputMessage); } }; TelemetryEvent += (object o, ResultEventArgs args) => { string eventName; KeyValuePair<string, object>[] properties; if (_engineTelemetry.DecodeTelemetryEvent(args.Results, out eventName, out properties)) { HostTelemetry.SendEvent(eventName, properties); } }; BreakChangeEvent += _breakpointManager.BreakpointModified; }
public void Init(ITransportCallback transportCallback, LaunchOptions options) { _callback = transportCallback; InitStreams(options, out _reader, out _writer); StartThread(GetThreadName()); }
public Debugger(LaunchOptions launchOptions) { _launchOptions = launchOptions; _debuggeePids = new Dictionary <string, int>(); }
public DebuggedProcess(bool bLaunched, LaunchOptions launchOptions, ISampleEngineCallback callback, WorkerThread worker, BreakpointManager bpman, AD7Engine engine, HostConfigurationStore configStore) : base(launchOptions) { uint processExitCode = 0; _pendingMessages = new StringBuilder(400); _worker = worker; _breakpointManager = bpman; Engine = engine; _libraryLoaded = new List<string>(); _loadOrder = 0; MICommandFactory = MICommandFactory.GetInstance(launchOptions.DebuggerMIMode, this); _waitDialog = MICommandFactory.SupportsStopOnDynamicLibLoad() ? new HostWaitDialog(ResourceStrings.LoadingSymbolMessage, ResourceStrings.LoadingSymbolCaption) : null; Natvis = new Natvis.Natvis(this, launchOptions.ShowDisplayString); // we do NOT have real Win32 process IDs, so we use a guid AD_PROCESS_ID pid = new AD_PROCESS_ID(); pid.ProcessIdType = (int)enum_AD_PROCESS_ID.AD_PROCESS_ID_GUID; pid.guidProcessId = Guid.NewGuid(); this.Id = pid; SourceLineCache = new SourceLineCache(this); _callback = callback; _moduleList = new List<DebuggedModule>(); ThreadCache = new ThreadCache(callback, this); Disassembly = new Disassembly(this); ExceptionManager = new ExceptionManager(MICommandFactory, _worker, _callback, configStore); VariablesToDelete = new List<string>(); OutputStringEvent += delegate (object o, string message) { // We can get messages before we have started the process // but we can't send them on until it is if (_connected) { _callback.OnOutputString(message); } else { _pendingMessages.Append(message); } }; LibraryLoadEvent += delegate (object o, EventArgs args) { ResultEventArgs results = args as MICore.Debugger.ResultEventArgs; string file = results.Results.TryFindString("host-name"); if (!string.IsNullOrEmpty(file) && MICommandFactory.SupportsStopOnDynamicLibLoad()) { _libraryLoaded.Add(file); if (_waitDialog != null) { _waitDialog.ShowWaitDialog(file); } } else if (this.MICommandFactory.Mode == MIMode.Clrdbg) { string id = results.Results.FindString("id"); ulong baseAddr = results.Results.FindAddr("base-address"); uint size = results.Results.FindUint("size"); bool symbolsLoaded = results.Results.FindInt("symbols-loaded") != 0; var module = new DebuggedModule(id, file, baseAddr, size, symbolsLoaded, string.Empty, _loadOrder++); lock (_moduleList) { _moduleList.Add(module); } _callback.OnModuleLoad(module); } else if (!string.IsNullOrEmpty(file)) { string addr = results.Results.TryFindString("loaded_addr"); if (string.IsNullOrEmpty(addr) || addr == "-") { return; // identifies the exe, not a real load } // generate module string id = results.Results.TryFindString("name"); bool symsLoaded = true; string symPath = null; if (results.Results.Contains("symbols-path")) { symPath = results.Results.FindString("symbols-path"); if (string.IsNullOrEmpty(symPath)) { symsLoaded = false; } } else { symPath = file; } ulong loadAddr = results.Results.FindAddr("loaded_addr"); uint size = results.Results.FindUint("size"); if (String.IsNullOrEmpty(id)) { id = file; } var module = FindModule(id); if (module == null) { module = new DebuggedModule(id, file, loadAddr, size, symsLoaded, symPath, _loadOrder++); lock (_moduleList) { _moduleList.Add(module); } _callback.OnModuleLoad(module); } } }; if (_launchOptions is LocalLaunchOptions) { LocalLaunchOptions localLaunchOptions = (LocalLaunchOptions)_launchOptions; if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) && _launchOptions.DebuggerMIMode == MIMode.Gdb && String.IsNullOrEmpty(localLaunchOptions.MIDebuggerServerAddress) ) { // For local linux launch, use the local linux transport which creates a new terminal and uses fifos for gdb communication. // CONSIDER: add new flag and only do this if new terminal is true? Note that setting this to false on linux will cause a deadlock // during debuggee launch if (localLaunchOptions.ShouldStartServer()) { this.Init(new MICore.ClientServerTransport ( new LocalLinuxTransport(), new ServerTransport(killOnClose: true, filterStdout: localLaunchOptions.FilterStdout, filterStderr: localLaunchOptions.FilterStderr) ), _launchOptions ); } else { this.Init(new MICore.LocalLinuxTransport(), _launchOptions); } } else { if (localLaunchOptions.ShouldStartServer()) { this.Init(new MICore.ClientServerTransport ( new LocalTransport(), new ServerTransport(killOnClose: true, filterStdout: localLaunchOptions.FilterStdout, filterStderr: localLaunchOptions.FilterStderr) ), _launchOptions ); } else { this.Init(new MICore.LocalTransport(), _launchOptions); } } } else if (_launchOptions is PipeLaunchOptions) { this.Init(new MICore.PipeTransport(), _launchOptions); } else if (_launchOptions is TcpLaunchOptions) { this.Init(new MICore.TcpTransport(), _launchOptions); } else { throw new ArgumentOutOfRangeException("LaunchInfo.options"); } MIDebugCommandDispatcher.AddProcess(this); // When the debuggee exits, we need to exit the debugger ProcessExitEvent += delegate (object o, EventArgs args) { // NOTE: Exceptions leaked from this method may cause VS to crash, be careful ResultEventArgs results = args as MICore.Debugger.ResultEventArgs; if (results.Results.Contains("exit-code")) { // GDB sometimes returns exit codes, which don't fit into uint, like "030000000472". // And we can't throw from here, because it crashes VS. // Full exit code will still usually be reported in the Output window, // but here let's return "uint.MaxValue" just to indicate that something went wrong. if (!uint.TryParse(results.Results.FindString("exit-code"), out processExitCode)) { processExitCode = uint.MaxValue; } } // quit MI Debugger _worker.PostOperation(CmdExitAsync); if (_waitDialog != null) { _waitDialog.EndWaitDialog(); } }; // When the debugger exits, we tell AD7 we are done DebuggerExitEvent += delegate (object o, EventArgs args) { // NOTE: Exceptions leaked from this method may cause VS to crash, be careful // this is the last AD7 Event we can ever send // Also the transport is closed when this returns _callback.OnProcessExit(processExitCode); Dispose(); }; DebuggerAbortedEvent += delegate (object o, /*OPTIONAL*/ string debuggerExitCode) { // NOTE: Exceptions leaked from this method may cause VS to crash, be careful // The MI debugger process unexpectedly exited. _worker.PostOperation(() => { // If the MI Debugger exits before we get a resume call, we have no way of sending program destroy. So just let start debugging fail. if (!_connected) { return; } string message; if (string.IsNullOrEmpty(debuggerExitCode)) message = string.Format(CultureInfo.CurrentCulture, MICoreResources.Error_MIDebuggerExited_UnknownCode, MICommandFactory.Name); else message = string.Format(CultureInfo.CurrentCulture, MICoreResources.Error_MIDebuggerExited_WithCode, MICommandFactory.Name, debuggerExitCode); _callback.OnError(message); _callback.OnProcessExit(uint.MaxValue); Dispose(); }); }; ModuleLoadEvent += async delegate (object o, EventArgs args) { // NOTE: This is an async void method, so make sure exceptions are caught and somehow reported if (_libraryLoaded.Count != 0) { string moduleNames = string.Join(", ", _libraryLoaded); try { _libraryLoaded.Clear(); SourceLineCache.OnLibraryLoad(); await _breakpointManager.BindAsync(); await CheckModules(); _bLastModuleLoadFailed = false; } catch (Exception e) { if (this.ProcessState == MICore.ProcessState.Exited) { return; // ignore exceptions after the process has exited } string exceptionDescription = EngineUtils.GetExceptionDescription(e); string message = string.Format(CultureInfo.CurrentCulture, MICoreResources.Error_ExceptionProcessingModules, moduleNames, exceptionDescription); // to avoid spamming the user, if the last module failed, we send the next failure to the output windiw instead of a message box if (!_bLastModuleLoadFailed) { _callback.OnError(message); _bLastModuleLoadFailed = true; } else { _callback.OnOutputMessage(new OutputMessage(message, enum_MESSAGETYPE.MT_OUTPUTSTRING, OutputMessage.Severity.Warning)); } } } if (_waitDialog != null) { _waitDialog.EndWaitDialog(); } if (MICommandFactory.SupportsStopOnDynamicLibLoad()) { CmdContinueAsync(); } }; // When we break we need to gather information BreakModeEvent += async delegate (object o, EventArgs args) { // NOTE: This is an async void method, so make sure exceptions are caught and somehow reported ResultEventArgs results = args as MICore.Debugger.ResultEventArgs; if (_waitDialog != null) { _waitDialog.EndWaitDialog(); } if (!this._connected) { _initialBreakArgs = results; return; } try { await HandleBreakModeEvent(results); } catch (Exception e) { if (this.ProcessState == MICore.ProcessState.Exited) { return; // ignore exceptions after the process has exited } string exceptionDescription = EngineUtils.GetExceptionDescription(e); string message = string.Format(CultureInfo.CurrentCulture, MICoreResources.Error_FailedToEnterBreakState, exceptionDescription); _callback.OnError(message); Terminate(); } }; ErrorEvent += delegate (object o, EventArgs args) { // NOTE: Exceptions leaked from this method may cause VS to crash, be careful ResultEventArgs result = (ResultEventArgs)args; _callback.OnError(result.Results.FindString("msg")); }; ThreadCreatedEvent += delegate (object o, EventArgs args) { ResultEventArgs result = (ResultEventArgs)args; ThreadCache.ThreadEvent(result.Results.FindInt("id"), /*deleted */false); }; ThreadExitedEvent += delegate (object o, EventArgs args) { ResultEventArgs result = (ResultEventArgs)args; ThreadCache.ThreadEvent(result.Results.FindInt("id"), /*deleted*/true); }; MessageEvent += (object o, ResultEventArgs args) => { OutputMessage outputMessage = DecodeOutputEvent(args.Results); if (outputMessage != null) { _callback.OnOutputMessage(outputMessage); } }; TelemetryEvent += (object o, ResultEventArgs args) => { string eventName; KeyValuePair<string, object>[] properties; if (DecodeTelemetryEvent(args.Results, out eventName, out properties)) { HostTelemetry.SendEvent(eventName, properties); } }; BreakChangeEvent += _breakpointManager.BreakpointModified; }
public DebuggedProcess(bool bLaunched, LaunchOptions launchOptions, ISampleEngineCallback callback, WorkerThread worker, BreakpointManager bpman, AD7Engine engine, string registryRoot) { uint processExitCode = 0; g_Process = this; _bStarted = false; _pendingMessages = new StringBuilder(400); _worker = worker; _launchOptions = launchOptions; _breakpointManager = bpman; Engine = engine; _libraryLoaded = new List<string>(); _loadOrder = 0; MICommandFactory = MICommandFactory.GetInstance(launchOptions.DebuggerMIMode, this); _waitDialog = MICommandFactory.SupportsStopOnDynamicLibLoad() ? new WaitDialog(ResourceStrings.LoadingSymbolMessage, ResourceStrings.LoadingSymbolCaption) : null; Natvis = new Natvis.Natvis(this); // we do NOT have real Win32 process IDs, so we use a guid AD_PROCESS_ID pid = new AD_PROCESS_ID(); pid.ProcessIdType = (int)enum_AD_PROCESS_ID.AD_PROCESS_ID_GUID; pid.guidProcessId = Guid.NewGuid(); this.Id = pid; SourceLineCache = new SourceLineCache(this); _callback = callback; _moduleList = new List<DebuggedModule>(); ThreadCache = new ThreadCache(callback, this); Disassembly = new Disassembly(this); ExceptionManager = new ExceptionManager(MICommandFactory, _worker, _callback, registryRoot); VariablesToDelete = new List<string>(); MessageEvent += delegate (object o, string message) { // We can get messages before we have started the process // but we can't send them on until it is if (_bStarted) { _callback.OnOutputString(message); } else { _pendingMessages.Append(message); } }; LibraryLoadEvent += delegate (object o, EventArgs args) { ResultEventArgs results = args as MICore.Debugger.ResultEventArgs; string file = results.Results.TryFindString("host-name"); if (!string.IsNullOrEmpty(file) && MICommandFactory.SupportsStopOnDynamicLibLoad()) { _libraryLoaded.Add(file); if (_waitDialog != null) { _waitDialog.ShowWaitDialog(file); } } else if (this.MICommandFactory.Mode == MIMode.Clrdbg) { string id = results.Results.FindString("id"); ulong baseAddr = results.Results.FindAddr("base-address"); uint size = results.Results.FindUint("size"); bool symbolsLoaded = results.Results.FindInt("symbols-loaded") != 0; var module = new DebuggedModule(id, file, baseAddr, size, symbolsLoaded, string.Empty, _loadOrder++); lock (_moduleList) { _moduleList.Add(module); } _callback.OnModuleLoad(module); } else if (!string.IsNullOrEmpty(file)) { string addr = results.Results.TryFindString("loaded_addr"); if (string.IsNullOrEmpty(addr) || addr == "-") { return; // identifies the exe, not a real load } // generate module string id = results.Results.TryFindString("name"); bool symsLoaded = true; string symPath = null; if (results.Results.Contains("symbols-path")) { symPath = results.Results.FindString("symbols-path"); if (string.IsNullOrEmpty(symPath)) { symsLoaded = false; } } else { symPath = file; } ulong loadAddr = results.Results.FindAddr("loaded_addr"); uint size = results.Results.FindUint("size"); if (String.IsNullOrEmpty(id)) { id = file; } var module = FindModule(id); if (module == null) { module = new DebuggedModule(id, file, loadAddr, size, symsLoaded, symPath, _loadOrder++); lock (_moduleList) { _moduleList.Add(module); } _callback.OnModuleLoad(module); } } }; if (_launchOptions is LocalLaunchOptions) { this.Init(new MICore.LocalTransport(), _launchOptions); } else if (_launchOptions is PipeLaunchOptions) { this.Init(new MICore.PipeTransport(), _launchOptions); } else if (_launchOptions is TcpLaunchOptions) { this.Init(new MICore.TcpTransport(), _launchOptions); } else if (_launchOptions is SerialLaunchOptions) { string port = ((SerialLaunchOptions)_launchOptions).Port; this.Init(new MICore.SerialTransport(port), _launchOptions); } else { throw new ArgumentOutOfRangeException("LaunchInfo.options"); } MIDebugCommandDispatcher.AddProcess(this); // When the debuggee exits, we need to exit the debugger ProcessExitEvent += delegate (object o, EventArgs args) { // NOTE: Exceptions leaked from this method may cause VS to crash, be careful ResultEventArgs results = args as MICore.Debugger.ResultEventArgs; if (results.Results.Contains("exit-code")) { processExitCode = results.Results.FindUint("exit-code"); } // quit MI Debugger _worker.PostOperation(CmdExitAsync); if (_waitDialog != null) { _waitDialog.EndWaitDialog(); } }; // When the debugger exits, we tell AD7 we are done DebuggerExitEvent += delegate (object o, EventArgs args) { // NOTE: Exceptions leaked from this method may cause VS to crash, be careful // this is the last AD7 Event we can ever send // Also the transport is closed when this returns _callback.OnProcessExit(processExitCode); Dispose(); }; DebuggerAbortedEvent += delegate (object o, EventArgs args) { // NOTE: Exceptions leaked from this method may cause VS to crash, be careful // The MI debugger process unexpectedly exited. _worker.PostOperation(() => { // If the MI Debugger exits before we get a resume call, we have no way of sending program destroy. So just let start debugging fail. if (!_connected) { return; } _callback.OnError(MICoreResources.Error_MIDebuggerExited); _callback.OnProcessExit(uint.MaxValue); Dispose(); }); }; ModuleLoadEvent += async delegate (object o, EventArgs args) { // NOTE: This is an async void method, so make sure exceptions are caught and somehow reported if (_libraryLoaded.Count != 0) { string moduleNames = string.Join(", ", _libraryLoaded); try { _libraryLoaded.Clear(); SourceLineCache.OnLibraryLoad(); await _breakpointManager.BindAsync(); await CheckModules(); _bLastModuleLoadFailed = false; } catch (Exception e) { if (this.ProcessState == MICore.ProcessState.Exited) { return; // ignore exceptions after the process has exited } string exceptionDescription = EngineUtils.GetExceptionDescription(e); string message = string.Format(CultureInfo.CurrentCulture, MICoreResources.Error_ExceptionProcessingModules, moduleNames, exceptionDescription); // to avoid spamming the user, if the last module failed, we send the next failure to the output windiw instead of a message box if (!_bLastModuleLoadFailed) { _callback.OnError(message); _bLastModuleLoadFailed = true; } else { _callback.OnOutputMessage(message, enum_MESSAGETYPE.MT_OUTPUTSTRING); } } } if (_waitDialog != null) { _waitDialog.EndWaitDialog(); } if (MICommandFactory.SupportsStopOnDynamicLibLoad()) { CmdContinueAsync(); } }; // When we break we need to gather information BreakModeEvent += async delegate (object o, EventArgs args) { // NOTE: This is an async void method, so make sure exceptions are caught and somehow reported ResultEventArgs results = args as MICore.Debugger.ResultEventArgs; if (_waitDialog != null) { _waitDialog.EndWaitDialog(); } if (!this._connected) { _initialBreakArgs = results; return; } try { await HandleBreakModeEvent(results); } catch (Exception e) { if (this.ProcessState == MICore.ProcessState.Exited) { return; // ignore exceptions after the process has exited } string exceptionDescription = EngineUtils.GetExceptionDescription(e); string message = string.Format(CultureInfo.CurrentCulture, MICoreResources.Error_FailedToEnterBreakState, exceptionDescription); _callback.OnError(message); Terminate(); } }; RunModeEvent += delegate (object o, EventArgs args) { // NOTE: Exceptions leaked from this method may cause VS to crash, be careful if (!_bStarted) { _bStarted = true; // Send any strings we got before the process came up if (_pendingMessages.Length != 0) { try { _callback.OnOutputString(_pendingMessages.ToString()); } catch { // If something goes wrong sending the output, lets not crash VS } } _pendingMessages = null; } }; ErrorEvent += delegate (object o, EventArgs args) { // NOTE: Exceptions leaked from this method may cause VS to crash, be careful ResultEventArgs result = (ResultEventArgs)args; _callback.OnError(result.Results.FindString("msg")); }; ThreadCreatedEvent += delegate (object o, EventArgs args) { ResultEventArgs result = (ResultEventArgs)args; ThreadCache.ThreadEvent(result.Results.FindInt("id"), /*deleted */false); }; ThreadExitedEvent += delegate (object o, EventArgs args) { ResultEventArgs result = (ResultEventArgs)args; ThreadCache.ThreadEvent(result.Results.FindInt("id"), /*deleted*/true); }; BreakChangeEvent += _breakpointManager.BreakpointModified; }
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); 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); }
public override void InitStreams(LaunchOptions options, out StreamReader reader, out StreamWriter writer) { // Mono seems to stop responding when the debugger sends a large response unless we specify a larger buffer here writer = _commandStream; reader = _outputStream; }
static internal TcpLaunchOptions CreateFromXml(Xml.LaunchOptions.TcpLaunchOptions source) { var options = new TcpLaunchOptions(RequireAttribute(source.Hostname, "Hostname"), LaunchOptions.RequirePortAttribute(source.Port, "Port"), source.Secure); options.InitializeCommonOptions(source); return(options); }
public void Init(ITransport transport, LaunchOptions options) { _lastCommandId = 1000; _transport = transport; FlushBreakStateData(); _transport.Init(this, options, Logger); }
public static bool ShouldStartServer(LaunchOptions options) { return(!string.IsNullOrWhiteSpace((options as LocalLaunchOptions)?.DebugServer)); }
public Debugger(LaunchOptions launchOptions) { _launchOptions = launchOptions; _debuggeePids = new Dictionary<string, int>(); }
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); }
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 = LinuxNativeMethods.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. 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 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`; trap 'rm {2}; rm {3}' EXIT; {1} --interpreter=mi --tty=$DbgTerm < {2} > {3};", debuggeeDir, localOptions.MIDebuggerPath, gdbStdInName, gdbStdOutName ); terminalProcess.StartInfo.Arguments = !isRoot ? argumentString : String.Concat(terminalPath, " ", 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); }
public static bool ShouldStartServer(LaunchOptions options) { return !string.IsNullOrWhiteSpace((options as LocalLaunchOptions)?.DebugServer); }
public DebugUnixChild(DebuggedProcess process, LaunchOptions launchOptions) { _process = process; _threadStates = new Dictionary<int, ThreadProgress>(); _launchOptions = launchOptions; }
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 Debugger(LaunchOptions launchOptions, Logger logger) { _launchOptions = launchOptions; _debuggeePids = new Dictionary<string, int>(); Logger = logger; _miResults = new MIResults(logger); }
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); }