Index: experimental/visual_studio_plugin/src/NaClVsx.Package/DebugSupport/NaClDebugger.cs |
diff --git a/experimental/visual_studio_plugin/src/NaClVsx.Package/DebugSupport/NaClDebugger.cs b/experimental/visual_studio_plugin/src/NaClVsx.Package/DebugSupport/NaClDebugger.cs |
deleted file mode 100644 |
index 08e44199c8b2573d05ed324cb94d056d50778106..0000000000000000000000000000000000000000 |
--- a/experimental/visual_studio_plugin/src/NaClVsx.Package/DebugSupport/NaClDebugger.cs |
+++ /dev/null |
@@ -1,429 +0,0 @@ |
-// Copyright (c) 2011 The Native Client Authors. All rights reserved. |
-// Use of this source code is governed by a BSD-style license that can be |
-// found in the LICENSE file. |
- |
-#region |
- |
-using System; |
-using System.Collections.Generic; |
-using System.Diagnostics; |
-using System.Linq; |
-using System.Runtime.CompilerServices; |
-using System.Threading; |
-using System.Xml.Linq; |
-using Google.MsAd7.BaseImpl.Interfaces; |
-using Google.NaClVsx.ProjectSupport; |
-using NaClVsx.DebugHelpers; |
- |
-#endregion |
- |
-namespace Google.NaClVsx.DebugSupport { |
- public sealed class NaClDebugger : INaClDebugger { |
- public NaClDebugger() { |
- BaseAddress = 0; |
- symbols_ = new NaClSymbolProvider(this); |
- } |
- |
- public event SimpleDebuggerTypes.EventHandler Stopped; |
- public event SimpleDebuggerTypes.EventHandler StepFinished; |
- public event SimpleDebuggerTypes.EventHandler Continuing; |
- public event SimpleDebuggerTypes.MessageHandler Output; |
- public event SimpleDebuggerTypes.ModuleLoadHandler ModuleLoaded; |
- public event SimpleDebuggerTypes.MessageHandler Opened; |
- |
- public string Arch { get; private set; } |
- |
- public string Path { get; private set; } |
- |
- #region INaClDebugger Members |
- |
- public ulong BaseAddress { get; private set; } |
- |
- [MethodImpl(MethodImplOptions.Synchronized)] |
- public void Open(string connectionString) { |
- sendStopMessages_ = false; |
- gdbProxy_.Open(connectionString); |
- |
- // Can't set these until after Open() returns |
- gdbProxy_.SetStopAsync(OnGdbStop); |
- gdbProxy_.SetOutputAsync(OnGdbOutput); |
- |
- var evt = new EventWaitHandle(false, EventResetMode.AutoReset); |
- gdbProxy_.GetArch( |
- (r, s, d) => { |
- if (GdbProxy.ResultCode.DHR_OK == r) { |
- ParseArchString(s); |
- } |
- evt.Set(); |
- }); |
- if (!evt.WaitOne(kGdbTimeout)) { |
- throw new TimeoutException("GDB connection timed out"); |
- } |
- |
- var regs = new RegsX86_64(); |
- gdbProxy_.GetRegisters(ref regs); |
- BaseAddress = regs.R15; |
- |
- var fullNexePath = NaClProjectConfig.GetLastNexe(); |
- // note -- LoadModuleWithPath uses |BaseAddress| which we set |
- // above by reading register |R15|. Works only in x86-64 sandbox. |
- LoadModuleWithPath(fullNexePath); |
- /** |
- * TODO(mmortensen): In the future we may want to |
- * query sel_ldr for the nexe name (and full path?) to |
- * make sure we are running the nexe we built...esp |
- * when we are launching through chrome and using a server |
- * to run our app. |
- gdb_proxy_.GetPath( |
- (r, s, d) => { |
- status = r; |
- if (status == GdbProxy.ResultCode.DHR_OK && s!="") { |
- LoadModuleWithPath(s); |
- } |
- else if (s == "") { |
- // Set the path based on project data, as obtained from |
- // NaClProjectconfig.NexeList. |
- LoadModuleWithPath(full_nexe_path); |
- } |
- evt.Set(); |
- }); |
- if (!evt.WaitOne(gdbTimeout_)) { |
- throw new TimeoutException("GDB connection timed out"); |
- } |
- **/ |
- InvokeOpened(SimpleDebuggerTypes.ResultCode.Ok, Path); |
- |
- sendStopMessages_ = true; |
- gdbWorkerThread_ = new System.Threading.Thread(GdbWorkerThreadProc); |
- gdbWorkerThread_.Name = "GDB Proxy Background Worker"; |
- gdbWorkerThread_.Start(); |
- |
- // Debuggee is stopped at nexe entry point. |
- // Calling |GetLastSig| results in debugger getting notification about |
- // debuggee stopped status. |
- var lastSig = 0; |
- gdbProxy_.GetLastSig(out lastSig); |
- } |
- |
- [MethodImpl(MethodImplOptions.Synchronized)] |
- public void Close() { |
- gdbTermEvent_.Set(); |
- gdbProxy_.Close(); |
- gdbWorkerThread_.Join(kGdbPingInterval * 8); |
- if (gdbWorkerThread_.IsAlive) { |
- gdbWorkerThread_.Abort(); |
- } |
- gdbWorkerThread_ = null; |
- } |
- |
- #endregion |
- |
- #region Implementation of ISimpleDebugger |
- |
- public string Architecture { |
- get { return Arch; } |
- } |
- |
- public ISimpleSymbolProvider Symbols { |
- get { return symbols_; } |
- } |
- |
- [MethodImpl(MethodImplOptions.Synchronized)] |
- public void Break() { |
- gdbProxy_.RequestBreak(); |
- } |
- |
- [MethodImpl(MethodImplOptions.Synchronized)] |
- public object GetRegisters(uint id) { |
- // FIXME -- |id| does NOT appear to be used by this function!! |
- // Note: |id| can be probably used to indicate register sets other than 'general' registers. |
- // For example, set of SSE registers. |
- var regs = new RegsX86_64(); |
- gdbProxy_.GetRegisters(ref regs); |
- if (regs.Rip == 0) { |
- Debug.WriteLine("ERROR: regs.RIPS is 0"); |
- } else { |
- Debug.WriteLine("regs.RIPS is " + String.Format("{0,4:X}", regs.Rip)); |
- } |
- Debug.WriteLine( |
- " GetRegisters.... Rip=" + |
- String.Format("{0,4:X}", regs.Rip) + |
- " Rsp=" + String.Format("{0,4:X}", regs.Rsp) + |
- " SegCS=" + String.Format("{0,4:X}", regs.SegCs) + |
- " SegDS=" + String.Format("{0,4:X}", regs.SegDs) + |
- " EFlags=" + String.Format("{0,4:X}", regs.EFlags)); |
- return regs; |
- } |
- |
- [MethodImpl(MethodImplOptions.Synchronized)] |
- public void Step(uint id) { |
- // SingleStep until |
- // - rip no longer points to the same source line. |
- // - some other thread throws a signal. |
- // - some signal other than STEP is thrown on this thread |
- // |
- // Some debugers might implement this a just a single step however, |
- // given the number of "NOP" required by the jump alignment, this |
- // could be painful, so instead we look for a different line. |
- var rip = ((RegsX86_64) GetRegisters(id)).Rip; |
- var pos = symbols_.PositionFromAddress(rip); |
- |
- // Check if we are starting on a breakpoint. If so we need to |
- // temporarily remove it or we will immediately trigger a break |
- // without moving. |
- var bp = gdbProxy_.HasBreakpoint(rip); |
- if (StepFinished != null) { |
- StepFinished( |
- this, |
- SimpleDebuggerTypes.EventType.Step, |
- SimpleDebuggerTypes.ResultCode.Ok); |
- } |
- |
- do { |
- // If we are on a breakpoint, temporarily remove it by |
- // stepping over it |
- if (bp) { |
- RemoveBreakpoint(rip); |
- sendStopMessages_ = false; |
- } |
- gdbProxy_.RequestStep(); |
- if (bp) { |
- AddBreakpoint(rip); |
- sendStopMessages_ = true; |
- |
- // We only need to check the first step, other BPs are valid |
- bp = false; |
- } |
- |
- // If the signal is not a break trap, or if the thead changed |
- // something else triggered the stop, so we are done. |
- int sig; |
- gdbProxy_.GetLastSig(out sig); |
- if (sig != kTrapSignal) |
- break; |
- |
- //TODO(noelallen) Add check for thread change... |
- //if (id != ) break; |
- |
- rip = ((RegsX86_64) GetRegisters(id)).Rip; |
- } while (pos == symbols_.PositionFromAddress(rip)); |
- } |
- |
- |
- [MethodImpl(MethodImplOptions.Synchronized)] |
- public void Continue() { |
- //TODO(noelallen) - use correct ID below |
- var regs = (RegsX86_64)GetRegisters(0); |
- var rip = regs.Rip; |
- |
- Debug.WriteLine("CONTINUE, rip=0x" + String.Format("{0,4:X}", rip)); |
- if (gdbProxy_.HasBreakpoint(rip)) { |
- Debug.WriteLine( |
- "NaClDebugger.cs, Continue()" + |
- "-HasBreakpoint = true, rip=" + |
- String.Format("{0,4:X}", rip)); |
- RemoveBreakpoint(rip); |
- // First step one instruction, to prevent a race condition |
- // where the IP gets back to the current line before we have |
- // a chance to re-enable the breakpoint |
- sendStopMessages_ = false; |
- gdbProxy_.RequestStep(); |
- sendStopMessages_ = true; |
- AddBreakpoint(rip); |
- } else { |
- Debug.WriteLine("NaClDebugger.cs, Continue()-HasBreakpoint = false"); |
- } |
- |
- var result = gdbProxy_.RequestContinue(); |
- OnGdbContinue(result); |
- } |
- |
- [MethodImpl(MethodImplOptions.Synchronized)] |
- public bool HasBreakpoint(ulong addr) { |
- return gdbProxy_.HasBreakpoint(addr); |
- } |
- |
- [MethodImpl(MethodImplOptions.Synchronized)] |
- public void AddBreakpoint(ulong addr) { |
- gdbProxy_.AddBreakpoint(addr); |
- } |
- |
- [MethodImpl(MethodImplOptions.Synchronized)] |
- public void RemoveBreakpoint(ulong addr) { |
- gdbProxy_.RemoveBreakpoint(addr); |
- } |
- |
- [MethodImpl(MethodImplOptions.Synchronized)] |
- public void AddTempBreakpoint(ulong addr) { |
- // Don't if a real one exists |
- if (HasBreakpoint(addr)) { |
- return; |
- } |
- gdbProxy_.AddBreakpoint(addr); |
- tempBreakpoints_.Add(addr); |
- } |
- |
- [MethodImpl(MethodImplOptions.Synchronized)] |
- public void RemoveTempBreakpoints() { |
- foreach (var addr in tempBreakpoints_) { |
- gdbProxy_.RemoveBreakpoint(addr); |
- } |
- tempBreakpoints_.Clear(); |
- } |
- |
- [MethodImpl(MethodImplOptions.Synchronized)] |
- public IEnumerable<uint> GetThreads() { |
- var evt = new EventWaitHandle(false, EventResetMode.AutoReset); |
- var tids = new List<uint>(); |
- gdbProxy_.GetThreads( |
- (r, s, d) => { |
- if (GdbProxy.ResultCode.DHR_OK == r) { |
- ParseThreadsString(s, tids); |
- } |
- evt.Set(); |
- }); |
- evt.WaitOne(); |
- return tids; |
- } |
- |
- [MethodImpl(MethodImplOptions.Synchronized)] |
- public void GetMemory(ulong sourceAddress, |
- Array destination, |
- uint countInBytes) { |
- var result = gdbProxy_.GetMemory(sourceAddress, destination, countInBytes); |
- if (result != GdbProxy.ResultCode.DHR_OK) { |
- throw new ApplicationException("Failed GetMemory query"); |
- } |
- } |
- |
- [MethodImpl(MethodImplOptions.Synchronized)] |
- public void SetMemory(ulong destAddress, Array src, uint count) { |
- var result = gdbProxy_.SetMemory(destAddress, src, count); |
- if (result != GdbProxy.ResultCode.DHR_OK) { |
- throw new ApplicationException("Failed SetMemory query"); |
- } |
- } |
- |
- public ulong GetU64(ulong address) { |
- var data = new byte[8]; |
- GetMemory(address, data, 8); |
- return BitConverter.ToUInt64(data, 0); |
- } |
- |
- public uint GetU32(ulong address) { |
- var data = new byte[4]; |
- GetMemory(address, data, 4); |
- return BitConverter.ToUInt32(data, 0); |
- } |
- |
- #endregion |
- |
- #region Private Implementation |
- |
- private readonly EventWaitHandle gdbTermEvent_ = |
- new EventWaitHandle(false, EventResetMode.ManualReset); |
- |
- private readonly GdbProxy gdbProxy_ = new GdbProxy(); |
- private readonly NaClSymbolProvider symbols_; |
- private readonly List<ulong> tempBreakpoints_ = new List<ulong>(); |
- |
- private const int kGdbPingInterval = 1000; // in ms |
- private const int kGdbTimeout = 10000; // in ms |
- private const int kTrapSignal = 5; |
- private System.Threading.Thread gdbWorkerThread_; |
- private bool sendStopMessages_ = true; |
- |
- #endregion |
- |
- #region Private Implementation |
- |
- private void GdbWorkerThreadProc() { |
- do { |
- lock (this) { |
- // gdb_proxy_::RequestContinue is not blocking anymore, so this worker thread |
- // has to poll RSP connection for reply (sent by debug server when debuggee |
- // stops for some reason). |
- // gdb_proxy_.WaitForReply will notify callback registered by gdb_proxy_.SetStopAsync. |
- if (gdbProxy_.IsRunning()) |
- gdbProxy_.WaitForReply(); |
- } |
- } while (gdbTermEvent_.WaitOne(kGdbPingInterval * 1) == false); |
- } |
- |
- private void InvokeOpened(SimpleDebuggerTypes.ResultCode status, string msg) { |
- var handler = Opened; |
- if (handler != null) { |
- handler(this, status, msg); |
- } |
- } |
- |
- private void LoadModuleWithPath(string fullPathToNexe) { |
- string status; |
- Path = fullPathToNexe; |
- Debug.WriteLine("LoadModuleWithPath {" + Path + "}"); |
- symbols_.LoadModule(Path, BaseAddress, out status); |
- if (ModuleLoaded != null) { |
- ModuleLoaded(this, Path, status); |
- } |
- } |
- |
- private void OnGdbContinue(GdbProxy.ResultCode result) { |
- if (Continuing != null) { |
- Continuing( |
- this, |
- SimpleDebuggerTypes.EventType.Continue, |
- (SimpleDebuggerTypes.ResultCode) result); |
- } |
- } |
- |
- private void OnGdbOutput(GdbProxy.ResultCode result, string msg, byte[] data) { |
- if (Output != null) { |
- Output(this, (SimpleDebuggerTypes.ResultCode) result, msg); |
- } |
- } |
- |
- private void OnGdbStop(GdbProxy.ResultCode result, string msg, byte[] data) { |
- RemoveTempBreakpoints(); |
- |
- if (sendStopMessages_ && Stopped != null) { |
- Debug.WriteLine("Sending stopped message"); |
- Stopped( |
- this, |
- SimpleDebuggerTypes.EventType.Break, |
- (SimpleDebuggerTypes.ResultCode) result); |
- } |
- } |
- |
- private void ParseArchString(string msg) { |
- Debug.WriteLine(msg); |
- try { |
- var targetString = XElement.Parse(msg); |
- var archElements = |
- targetString.Descendants("architecture"); |
- var el = archElements.FirstOrDefault(); |
- Arch = el.Value; |
- } |
- catch (Exception e) { |
- Debug.WriteLine(e.Message); |
- } |
- } |
- |
- private void ParseThreadsString(string msg, List<uint> tids) { |
- Debug.WriteLine(msg); |
- try { |
- var threadsString = XElement.Parse(msg); |
- foreach (var el in threadsString.Descendants("thread")) { |
- var tidStr = el.Attribute("id").Value; |
- var tid = Convert.ToUInt32(tidStr, 16); |
- |
- tids.Add(tid); |
- } |
- } |
- catch (Exception e) { |
- Debug.WriteLine(e.Message); |
- } |
- } |
- |
- #endregion |
- } |
-} |