//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public void Start() { LoggingUtils.PrintFunction(); // // Export an execution script ('jdb.ini') for standard start-up properties. // string [] execCommands = m_jdbSetup.CreateJdbExecutionScript(); using (StreamWriter writer = new StreamWriter(Path.Combine(m_jdbSetup.CacheDirectory, "jdb.ini"), false, Encoding.ASCII)) { foreach (string command in execCommands) { writer.WriteLine(command); } } // // Prepare a new JDB instance. Connections must be made on the command line, so delay this until an attach request. // StringBuilder argumentBuilder = new StringBuilder(); #if DEBUG && false argumentBuilder.Append(string.Format("-dbgtrace ")); #endif argumentBuilder.Append(string.Format("-connect com.sun.jdi.SocketAttach:hostname={0},port={1} ", m_jdbSetup.Host, m_jdbSetup.Port)); m_jdbClientInstance = new AsyncRedirectProcess(Path.Combine(JavaSettings.JdkRoot, @"bin\jdb.exe"), argumentBuilder.ToString()); }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public static AsyncRedirectProcess AdbCommandAsync(AndroidDevice target, string command, string arguments) { LoggingUtils.Print(string.Format("[AndroidDevice] AdbCommandAsync: Target={0} Cmd={1} Args={2}", target.ID, command, arguments)); AsyncRedirectProcess adbCommand = new AsyncRedirectProcess(AndroidSettings.SdkRoot + @"\platform-tools\adb.exe", string.Format("-s {0} {1} {2}", target.ID, command, arguments)); return(adbCommand); }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public GdbServer(GdbSetup gdbSetup) { LoggingUtils.PrintFunction (); m_gdbSetup = gdbSetup; m_gdbServerInstance = null; m_gdbServerAttached = null; }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public GdbServer(GdbSetup gdbSetup) { LoggingUtils.PrintFunction(); m_gdbSetup = gdbSetup; m_gdbServerInstance = null; m_gdbServerAttached = null; }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// protected virtual void Dispose (bool disposing) { if (disposing) { if (m_gdbServerInstance != null) { m_gdbServerInstance.Dispose (); m_gdbServerInstance = null; } if (m_gdbServerAttached != null) { m_gdbServerAttached.Dispose (); m_gdbServerAttached = null; } } }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// protected virtual void Dispose(bool disposing) { if (disposing) { if (m_gdbServerInstance != null) { m_gdbServerInstance.Dispose(); m_gdbServerInstance = null; } if (m_gdbServerAttached != null) { m_gdbServerAttached.Dispose(); m_gdbServerAttached = null; } } }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// protected virtual void Dispose(bool disposing) { if (disposing) { if (m_jdbClientInstance != null) { m_jdbClientInstance.Dispose(); m_jdbClientInstance = null; } if (m_sessionStarted != null) { m_sessionStarted.Dispose(); m_sessionStarted = null; } } }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public void ProcessExited(object sendingProcess, EventArgs args) { try { m_timeSinceLastOperation.Restart(); LoggingUtils.Print(string.Format("[JdbClient] ProcessExited")); m_jdbClientInstance = null; // // If we're waiting on a synchronous command, signal a finish to process termination. // foreach (KeyValuePair <string, ManualResetEvent> syncKeyPair in m_syncCommandLocks) { syncKeyPair.Value.Set(); } } catch (Exception e) { LoggingUtils.HandleException(e); } }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// protected virtual void Dispose (bool disposing) { if (disposing) { if (m_jdbClientInstance != null) { m_jdbClientInstance.Dispose (); m_jdbClientInstance = null; } if (m_sessionStarted != null) { m_sessionStarted.Dispose (); m_sessionStarted = null; } } }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public void Start () { LoggingUtils.PrintFunction (); // // Export an execution script ('gdb.setup') for standard start-up properties. // string [] execCommands = m_gdbSetup.CreateGdbExecutionScript (); using (StreamWriter writer = new StreamWriter (Path.Combine (m_gdbSetup.CacheDirectory, "gdb.setup"))) { foreach (string command in execCommands) { writer.WriteLine (command); } } // // Spawn a new GDB instance which executes gdb.setup and begins debugging file 'app_process'. // string clientArguments = m_gdbSetup.GdbToolArguments + string.Format (@" -fullname -x {0}", PathUtils.SantiseWindowsPath (Path.Combine (m_gdbSetup.CacheDirectory, "gdb.setup"))); m_gdbClientInstance = new AsyncRedirectProcess (m_gdbSetup.GdbToolPath, clientArguments); m_gdbClientInstance.Start (this); m_timeSinceLastOperation.Start (); uint timeout = 15000; bool responseSignaled = false; while ((!responseSignaled) && (m_timeSinceLastOperation.ElapsedMilliseconds < timeout)) { responseSignaled = m_sessionStarted.WaitOne (0); if (!responseSignaled) { Thread.Sleep (100); } } if (!responseSignaled) { throw new TimeoutException ("Timed out waiting for GDB client to execute"); } // // Create asynchronous input and output job queue threads. // if (m_asyncRecordWorkerThread == null) { m_asyncRecordWorkerThread = new Thread (AsyncOutputWorkerThreadBody); m_asyncRecordWorkerThread.Start (); } // // Evaluate this client's GDB/MI support and capabilities. // try { SendCommand ("-list-features", delegate (MiResultRecord resultRecord) { MiResultRecord.RequireOk (resultRecord, "-list-features"); if (resultRecord.HasField ("features")) { foreach (MiResultValue feature in resultRecord ["features"] [0].Values) { m_gdbSupportedClientMiFeatures.Add (feature.GetString ()); } } }); } catch (Exception e) { LoggingUtils.HandleException (e); throw; } // // Evaluate available signals and their current 'should stop' status. // try { string command = string.Format ("-interpreter-exec console \"info signals\""); SendCommand (command, delegate (MiResultRecord resultRecord) { MiResultRecord.RequireOk (resultRecord, command); string pattern = @"(?<sig>[^ ]+)[ ]+(?<stop>[^\\t]+)\\t(?<print>[^\\t]+)\\t(?<pass>[^\\t]+)\\t\\t(?<desc>[^\\]+)"; Regex regExMatcher = new Regex (pattern, RegexOptions.Compiled | RegexOptions.IgnoreCase); for (int i = 2; i < resultRecord.Records.Count; ++i) // Skip the first rows (2) of headers { MiStreamRecord record = resultRecord.Records [i]; Match regExLineMatch = regExMatcher.Match (record.Stream); if (regExLineMatch.Success) { string sig = regExLineMatch.Result ("${sig}"); bool stop = regExLineMatch.Result ("${stop}").Equals ("Yes"); bool print = regExLineMatch.Result ("${print}").Equals ("Yes"); bool passToProgram = regExLineMatch.Result ("${pass}").Equals ("Yes"); string desc = regExLineMatch.Result ("${desc}"); Signal signal = new Signal (this, sig, desc, stop, passToProgram); lock (m_gdbSupportedClientSignals) { m_gdbSupportedClientSignals.Add (sig, signal); } } } }); } catch (Exception e) { LoggingUtils.HandleException (e); throw; } }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public static AsyncRedirectProcess AdbCommandAsync (AndroidDevice target, string command, string arguments) { LoggingUtils.Print (string.Format ("[AndroidDevice] AdbCommandAsync: Target={0} Cmd={1} Args={2}", target.ID, command, arguments)); AsyncRedirectProcess adbCommand = new AsyncRedirectProcess (AndroidSettings.SdkRoot + @"\platform-tools\adb.exe", string.Format ("-s {0} {1} {2}", target.ID, command, arguments)); return adbCommand; }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public int OnProgramDestroy (IDebugEngine2 pEngine, IDebugProcess2 pProcess, IDebugProgram2 pProgram, IDebugThread2 pThread, IDebugEvent2 pEvent, ref Guid riidEvent, uint dwAttrib) { LoggingUtils.PrintFunction (); try { if (m_adbLogcatProcess != null) { m_adbLogcatProcess.Kill (); m_adbLogcatProcess.Dispose (); m_adbLogcatProcess = null; } return VSConstants.S_OK; } catch (Exception e) { LoggingUtils.HandleException (e); return VSConstants.E_FAIL; } }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public void ProcessExited (object sendingProcess, EventArgs args) { try { m_timeSinceLastOperation.Restart (); LoggingUtils.Print (string.Format ("[JdbClient] ProcessExited")); m_jdbClientInstance = null; // // If we're waiting on a synchronous command, signal a finish to process termination. // foreach (KeyValuePair<string, ManualResetEvent> syncKeyPair in m_syncCommandLocks) { syncKeyPair.Value.Set (); } } catch (Exception e) { LoggingUtils.HandleException (e); } }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// protected virtual void Dispose (bool disposing) { if (disposing) { if (m_gdbClientInstance != null) { m_gdbClientInstance.Dispose (); m_gdbClientInstance = null; } if (m_asyncRecordWorkerThreadSignal != null) { m_asyncRecordWorkerThreadSignal.Dispose (); m_asyncRecordWorkerThreadSignal = null; } if (m_sessionStarted != null) { m_sessionStarted.Dispose (); m_sessionStarted = null; } } }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public void Start() { LoggingUtils.PrintFunction(); // // Check the target 'gdbserver' binary exits on target device/emulator. // string gdbServerPath = string.Empty; List <string> potentialGdbServerPaths = new List <string> (); foreach (string libraryPath in m_gdbSetup.Process.NativeLibraryAbiPaths) { potentialGdbServerPaths.Add(string.Format("{0}/gdbserver", libraryPath)); } potentialGdbServerPaths.Add(string.Format("{0}/gdbserver", m_gdbSetup.Process.NativeLibraryPath)); foreach (string path in potentialGdbServerPaths) { try { string ls = m_gdbSetup.Process.HostDevice.Shell("ls", path); if (ls.ToLowerInvariant().Contains("no such file")) { throw new DirectoryNotFoundException(path); } gdbServerPath = path; break; } catch (Exception) { // Ignore. } } // // If we can't find a bundled 'gdbserver' binary, attempt to find one in the NDK. // if (string.IsNullOrWhiteSpace(gdbServerPath)) { foreach (string path in m_gdbSetup.Process.NativeLibraryAbiPaths) { try { string shortAbi = path.Substring(path.LastIndexOf('/') + 1); string local = Path.Combine(AndroidSettings.NdkRoot, string.Format(@"prebuilt\android-{0}\gdbserver\gdbserver", shortAbi)); string remote = string.Format("/data/local/tmp/gdbserver-{0}", shortAbi); m_gdbSetup.Process.HostDevice.Push(local, remote); } catch (Exception e) { LoggingUtils.HandleException(e); } } } if (string.IsNullOrWhiteSpace(gdbServerPath)) { throw new InvalidOperationException(string.Format("Failed to locate required 'gdbserver' binary on device ({0}).", m_gdbSetup.Process.HostDevice.ID)); } KillActiveGdbServerSessions(); // // Construct a adaptive command line based on GdbSetup requirements. // StringBuilder commandLineArgumentsBuilder = new StringBuilder(); commandLineArgumentsBuilder.AppendFormat("run-as {0} {1} ", m_gdbSetup.Process.Name, gdbServerPath); if (!string.IsNullOrWhiteSpace(m_gdbSetup.Socket)) { commandLineArgumentsBuilder.AppendFormat("+{0} ", m_gdbSetup.Socket); } commandLineArgumentsBuilder.Append("--attach "); if (string.IsNullOrWhiteSpace(m_gdbSetup.Socket)) // Don't need a host if we have a bound socket? { commandLineArgumentsBuilder.AppendFormat("{0}:{1} ", m_gdbSetup.Host, m_gdbSetup.Port); } commandLineArgumentsBuilder.Append(m_gdbSetup.Process.Pid); // // Launch 'gdbserver' and wait for output to determine success. // Stopwatch waitForConnectionTimer = new Stopwatch(); waitForConnectionTimer.Start(); m_gdbServerAttached = new ManualResetEvent(false); m_gdbServerInstance = AndroidAdb.AdbCommandAsync(m_gdbSetup.Process.HostDevice, "shell", commandLineArgumentsBuilder.ToString()); m_gdbServerInstance.Start(this); LoggingUtils.Print(string.Format("[GdbServer] Waiting to attach...")); uint timeout = 5000; bool responseSignaled = false; while ((!responseSignaled) && (waitForConnectionTimer.ElapsedMilliseconds < timeout)) { responseSignaled = m_gdbServerAttached.WaitOne(0); if (!responseSignaled) { Thread.Sleep(100); } } if (!responseSignaled) { throw new TimeoutException("Timed out waiting for GdbServer to execute."); } }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public int OnDebuggerLogcatEvent (IDebugEngine2 pEngine, IDebugProcess2 pProcess, IDebugProgram2 pProgram, IDebugThread2 pThread, IDebugEvent2 pEvent, ref Guid riidEvent, uint dwAttrib) { LoggingUtils.PrintFunction (); try { DebugEngineEvent.DebuggerLogcatEvent debuggerLogcatEvent = pEvent as DebugEngineEvent.DebuggerLogcatEvent; using (SyncRedirectProcess command = AndroidAdb.AdbCommand (debuggerLogcatEvent.HostDevice, "logcat", "-c")) { command.StartAndWaitForExit (); } m_adbLogcatProcess = AndroidAdb.AdbCommandAsync (debuggerLogcatEvent.HostDevice, "logcat", ""); m_adbLogcatListener = new DeviceLogcatListener (); if (m_adbLogcatProcess == null) { throw new InvalidOperationException ("Failed to launch logcat application."); } if (m_adbLogcatListener == null) { throw new InvalidOperationException ("Failed to launch logcat listener."); } m_adbLogcatProcess.Start (m_adbLogcatListener); return VSConstants.S_OK; } catch (Exception e) { LoggingUtils.HandleException (e); return VSConstants.E_FAIL; } }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public void Start() { LoggingUtils.PrintFunction (); // // Check the target 'gdbserver' binary exits on target device/emulator. // string gdbServerPath = string.Empty; List<string> potentialGdbServerPaths = new List<string> (); foreach (string libraryPath in m_gdbSetup.Process.NativeLibraryAbiPaths) { potentialGdbServerPaths.Add (string.Format ("{0}/gdbserver", libraryPath)); } potentialGdbServerPaths.Add (string.Format ("{0}/gdbserver", m_gdbSetup.Process.NativeLibraryPath)); foreach (string path in potentialGdbServerPaths) { using (SyncRedirectProcess checkGdbServer = AndroidAdb.AdbCommand (m_gdbSetup.Process.HostDevice, "shell", "ls " + path)) { int exitCode = checkGdbServer.StartAndWaitForExit (1000); if ((exitCode == 0) && !checkGdbServer.StandardOutput.ToLower ().Contains ("no such file")) { gdbServerPath = path; break; } } } if (string.IsNullOrWhiteSpace (gdbServerPath)) { // TODO: Push the required gdbserver binary, so we can attach to any app. throw new InvalidOperationException (string.Format ("Failed to locate required 'gdbserver' binary on device ({0}).", m_gdbSetup.Process.HostDevice.ID)); } KillActiveGdbServerSessions (); // // Construct a adaptive command line based on GdbSetup requirements. // StringBuilder commandLineArgumentsBuilder = new StringBuilder (); commandLineArgumentsBuilder.AppendFormat ("run-as {0} {1} ", m_gdbSetup.Process.Name, gdbServerPath); if (!string.IsNullOrWhiteSpace (m_gdbSetup.Socket)) { commandLineArgumentsBuilder.AppendFormat ("+{0} ", m_gdbSetup.Socket); } commandLineArgumentsBuilder.Append ("--attach "); if (string.IsNullOrWhiteSpace (m_gdbSetup.Socket)) // Don't need a host if we have a bound socket? { commandLineArgumentsBuilder.AppendFormat ("{0}:{1} ", m_gdbSetup.Host, m_gdbSetup.Port); } commandLineArgumentsBuilder.Append (m_gdbSetup.Process.Pid); // // Launch 'gdbserver' and wait for output to determine success. // Stopwatch waitForConnectionTimer = new Stopwatch (); waitForConnectionTimer.Start (); m_gdbServerAttached = new ManualResetEvent (false); m_gdbServerInstance = AndroidAdb.AdbCommandAsync (m_gdbSetup.Process.HostDevice, "shell", commandLineArgumentsBuilder.ToString ()); m_gdbServerInstance.Start (this); LoggingUtils.Print (string.Format ("[GdbServer] Waiting to attach...")); uint timeout = 5000; bool responseSignaled = false; while ((!responseSignaled) && (waitForConnectionTimer.ElapsedMilliseconds < timeout)) { responseSignaled = m_gdbServerAttached.WaitOne (0); if (!responseSignaled) { Thread.Sleep (100); } } if (!responseSignaled) { throw new TimeoutException ("Timed out waiting for GdbServer to execute."); } }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public void Start() { LoggingUtils.PrintFunction(); // // Check the target 'gdbserver' binary exits on target device/emulator. // string gdbServerPath = string.Empty; List <string> potentialGdbServerPaths = new List <string> (); foreach (string libraryPath in m_gdbSetup.Process.NativeLibraryAbiPaths) { potentialGdbServerPaths.Add(string.Format("{0}/gdbserver", libraryPath)); } potentialGdbServerPaths.Add(string.Format("{0}/gdbserver", m_gdbSetup.Process.NativeLibraryPath)); foreach (string path in potentialGdbServerPaths) { using (SyncRedirectProcess checkGdbServer = AndroidAdb.AdbCommand(m_gdbSetup.Process.HostDevice, "shell", "ls " + path)) { int exitCode = checkGdbServer.StartAndWaitForExit(1000); if ((exitCode == 0) && !checkGdbServer.StandardOutput.ToLower().Contains("no such file")) { gdbServerPath = path; break; } } } if (string.IsNullOrWhiteSpace(gdbServerPath)) { // TODO: Push the required gdbserver binary, so we can attach to any app. throw new InvalidOperationException(string.Format("Failed to locate required 'gdbserver' binary on device ({0}).", m_gdbSetup.Process.HostDevice.ID)); } KillActiveGdbServerSessions(); // // Construct a adaptive command line based on GdbSetup requirements. // StringBuilder commandLineArgumentsBuilder = new StringBuilder(); commandLineArgumentsBuilder.AppendFormat("run-as {0} {1} ", m_gdbSetup.Process.Name, gdbServerPath); if (!string.IsNullOrWhiteSpace(m_gdbSetup.Socket)) { commandLineArgumentsBuilder.AppendFormat("+{0} ", m_gdbSetup.Socket); } commandLineArgumentsBuilder.Append("--attach "); if (string.IsNullOrWhiteSpace(m_gdbSetup.Socket)) // Don't need a host if we have a bound socket? { commandLineArgumentsBuilder.AppendFormat("{0}:{1} ", m_gdbSetup.Host, m_gdbSetup.Port); } commandLineArgumentsBuilder.Append(m_gdbSetup.Process.Pid); // // Launch 'gdbserver' and wait for output to determine success. // Stopwatch waitForConnectionTimer = new Stopwatch(); waitForConnectionTimer.Start(); m_gdbServerAttached = new ManualResetEvent(false); m_gdbServerInstance = AndroidAdb.AdbCommandAsync(m_gdbSetup.Process.HostDevice, "shell", commandLineArgumentsBuilder.ToString()); m_gdbServerInstance.Start(this); LoggingUtils.Print(string.Format("[GdbServer] Waiting to attach...")); uint timeout = 5000; bool responseSignaled = false; while ((!responseSignaled) && (waitForConnectionTimer.ElapsedMilliseconds < timeout)) { responseSignaled = m_gdbServerAttached.WaitOne(0); if (!responseSignaled) { Thread.Sleep(100); } } if (!responseSignaled) { throw new TimeoutException("Timed out waiting for GdbServer to execute."); } }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public void Start () { LoggingUtils.PrintFunction (); // // Export an execution script ('jdb.ini') for standard start-up properties. // string [] execCommands = m_jdbSetup.CreateJdbExecutionScript (); using (StreamWriter writer = new StreamWriter (Path.Combine (m_jdbSetup.CacheDirectory, "jdb.ini"), false, Encoding.ASCII)) { foreach (string command in execCommands) { writer.WriteLine (command); } } // // Prepare a new JDB instance. Connections must be made on the command line, so delay this until an attach request. // StringBuilder argumentBuilder = new StringBuilder (); #if DEBUG && false argumentBuilder.Append (string.Format ("-dbgtrace ")); #endif argumentBuilder.Append (string.Format ("-connect com.sun.jdi.SocketAttach:hostname={0},port={1} ", m_jdbSetup.Host, m_jdbSetup.Port)); m_jdbClientInstance = new AsyncRedirectProcess (Path.Combine (JavaSettings.JdkRoot, @"bin\jdb.exe"), argumentBuilder.ToString ()); }