//#define DEBUGCONNECTOR_DEBUG using System; using System.Collections.Generic; using System.Diagnostics; using System.Text; using System.IO; using System.Threading; namespace Cosmos.Debug.Common { /// Handles the dialog between the Debug Stub embedded in a debugged Cosmos Kernel and /// our Debug Engine hosted in Visual Studio. This abstract class is communication protocol /// independent. Sub-classes exist that manage the wire level details of the communications. /// public abstract partial class DebugConnector : IDisposable { // MtW: This class used to use async (BeginRead/EndRead) reading, which gave some weird bug. // The issue was that randomly it wouldn't return any data anymore, resulting in freezes of the kernel. public Action ConnectionLost; public Action CmdTrace; public Action CmdBreak; public Action CmdMethodContext; public Action CmdText; public Action CmdMessageBox; public Action CmdStarted; public Action OnDebugMsg; public Action CmdRegisters; public Action CmdFrame; public Action CmdStack; public Action CmdPong; public Action CmdChannel; public Action CmdStackCorruptionOccurred; public Action CmdNullReferenceOccurred; public Action CmdSimpleNumber; public Action CmdSimpleLongNumber; public Action CmdComplexNumber; public Action CmdComplexLongNumber; public Action Error; protected byte mCurrentMsgType; protected AutoResetEvent mCmdWait = new AutoResetEvent(false); private StreamWriter mDebugWriter = StreamWriter.Null; //new StreamWriter(@"e:\dcdebug.txt", false) { AutoFlush = true }; // This member used to be public. The SetConnectionHandler has been added. private Action Connected; protected void HandleError(Exception E) { if (Error != null) { Error(E); } else { throw new Exception("Unhandled exception occurred!", E); } } /// Descendants must invoke this method whenever they detect an incoming connection. public void DoConnected() { if (Connected != null) { Connected(); } } /// Defines the handler to be invoked when a connection occurs on this condector. This /// method is for use by the AD7Process instance. /// The handler to be notified when a connection occur. public void SetConnectionHandler(Action handler) { Connected = handler; } /// /// Method to do debug logging of the debug connector itself. /// [Conditional("DEBUGCONNECTOR_DEBUG")] protected void DebugLog(string message) { // even when this method doesn't do anything, DON'T REMOVE IT // it's used when debugging the DebugConnector, as it provides insights to what happens System.Diagnostics.Debug.WriteLine(message); } protected void DoDebugMsg(string aMsg, bool aOnlyIfConnected) { if (IsConnected || aOnlyIfConnected == false) { System.Diagnostics.Debug.WriteLine(aMsg); if (OnDebugMsg != null) { OnDebugMsg(aMsg); } } } protected bool mSigReceived = false; public bool SigReceived { get { return mSigReceived; } } // Is stream alive? Other side may not be responsive yet, use SigReceived instead for this instead public abstract bool IsConnected { get; } private int mDataSize; private List MethodContextDatas = new List(); private List MemoryDatas = new List(); public void DeleteBreakpoint(int aID) { SetBreakpoint(aID, 0); } protected ushort GetUInt16(byte[] aBytes, int aOffset) { return BitConverter.ToUInt16(aBytes, aOffset); } protected uint GetUInt32(byte[] aBytes, int aOffset) { return BitConverter.ToUInt32(aBytes, aOffset); } protected ulong GetUInt64(byte[] aBytes, int aOffset) { return BitConverter.ToUInt64(aBytes, aOffset); } protected float GetSingle(byte[] aBytes, int aOffset) { return BitConverter.ToSingle(aBytes, aOffset); } protected double GetDouble(byte[] aBytes, int aOffset) { return BitConverter.ToDouble(aBytes, aOffset); } protected void PacketMsg(byte[] aPacket) { mCurrentMsgType = aPacket[0]; //DoDebugMsg(String.Format("DC - PacketMsg: {0}", DebugConnectorStreamWithTimeouts.BytesToString(aPacket, 0, aPacket.Length))); //DoDebugMsg("DC - " + mCurrentMsgType); // Could change to an array, but really not much benefit switch (mCurrentMsgType) { case Ds2Vs.TracePoint: DebugLog("DC Recv: TracePoint"); Next(4, PacketTracePoint); break; case Ds2Vs.BreakPoint: DebugLog("DC Recv: BreakPoint"); Next(4, PacketBreakPoint); break; case Ds2Vs.Message: DebugLog("DC Recv: Message"); Next(2, PacketTextSize); break; case Ds2Vs.MessageBox: DebugLog("DC Recv: MessageBox"); Next(2, PacketMessageBoxTextSize); break; case Ds2Vs.Started: DebugLog("DC Recv: Started"); // Call WaitForMessage first, else it blocks because Ds2Vs.Started triggers // other commands which need responses. WaitForMessage(); // Guests never get the first byte sent. So we send a noop. // This dummy byte seems to clear out the serial channel. // Its never received, but if it ever is, its a noop anyways. SendCmd(Vs2Ds.Noop); // Send signature var xData = new byte[4]; Array.Copy(BitConverter.GetBytes(Cosmos.Debug.Common.Consts.SerialSignature), 0, xData, 0, 4); SendRawData(xData); CmdStarted(); break; case Ds2Vs.Noop: DebugLog("DC Recv: Noop"); // MtW: When implementing Serial support for debugging on real hardware, it appears // that when booting a machine, in the bios it emits zero's to the serial port. // Kudzu: Made a Noop command to handle this WaitForMessage(); break; case Ds2Vs.CmdCompleted: DebugLog("DC Recv: CmdCompleted"); Next(1, PacketCmdCompleted); break; case Ds2Vs.MethodContext: DebugLog("DC Recv: MethodContext"); Next(mDataSize, PacketMethodContext); break; case Ds2Vs.MemoryData: DebugLog("DC Recv: MemoryData"); Next(mDataSize, PacketMemoryData); break; case Ds2Vs.Registers: DebugLog("DC Recv: Registers"); Next(40, PacketRegisters); break; case Ds2Vs.Frame: DebugLog("DC Recv: Frame"); Next(-1, PacketFrame); break; case Ds2Vs.Stack: DebugLog("DC Recv: Stack"); Next(-1, PacketStack); break; case Ds2Vs.Pong: DebugLog("DC Recv: Pong"); Next(0, PacketPong); break; case Ds2Vs.StackCorruptionOccurred: DebugLog("DC Recv: StackCorruptionOccurred"); Next(4, PacketStackCorruptionOccurred); break; case Ds2Vs.NullReferenceOccurred: DebugLog("DC Recv: NullReferenceOccurred"); Next(4, PacketNullReferenceOccurred); break; case Ds2Vs.SimpleNumber: DebugLog("DC Recv: SimpleNumber"); Next(4, PacketSimpleNumber); break; case Ds2Vs.SimpleLongNumber: DebugLog("DC Recv: SimpleLongNumber"); Next(8, PacketSimpleLongNumber); break; case Ds2Vs.ComplexNumber: DebugLog("DC Recv: ComplexNumber"); Next(4, PacketComplexNumber); break; case Ds2Vs.ComplexLongNumber: DebugLog("DC Recv: ComplexLongNumber"); Next(8, PacketComplexLongNumber); break; default: if (mCurrentMsgType > 128) { // other channels than debugstub DebugLog("DC Recv: Console"); // copy to local variable, so the anonymous method will get the correct value! var xChannel = mCurrentMsgType; Next(1, data => PacketOtherChannelCommand(xChannel, data)); break; } // Exceptions crash VS so use MsgBox instead DebugLog("Unknown debug command: " + mCurrentMsgType); // Despite it being unkonwn, we try again. Normally this will // just cause more unknowns, but can be useful for debugging. WaitForMessage(); break; } } public virtual void Dispose() { if (mDebugWriter != null) { mDebugWriter.Dispose(); mDebugWriter = null; } if (mBackgroundThread != null) { mBackgroundThread.Abort(); mBackgroundThread.Join(); mBackgroundThread = null; } GC.SuppressFinalize(this); } // Signature is sent after garbage emitted during init of serial port. // For more info see note in DebugStub where signature is transmitted. protected byte[] mSigCheck = new byte[4] { 0, 0, 0, 0 }; protected virtual void WaitForSignature(byte[] aPacket) { mSigCheck[0] = mSigCheck[1]; mSigCheck[1] = mSigCheck[2]; mSigCheck[2] = mSigCheck[3]; mSigCheck[3] = aPacket[0]; var xSig = GetUInt32(mSigCheck, 0); DebugLog("DC: Sig Byte " + aPacket[0].ToString("X2").ToUpper() + " : " + xSig.ToString("X8").ToUpper()); if (xSig == Consts.SerialSignature) { // Sig found, wait for messages mSigReceived = true; SendTextToConsole("SigReceived!"); DebugLog("SigReceived"); WaitForMessage(); } else { SendPacketToConsole(aPacket); // Sig not found, keep looking Next(1, WaitForSignature); } } protected void SendPacketToConsole(byte[] aPacket) { CmdChannel(129, 0, aPacket); } protected void SendTextToConsole(string aText) { SendPacketToConsole(Encoding.UTF8.GetBytes(aText)); } } }