mirror of
https://github.com/danbulant/Cosmos
synced 2026-05-19 04:18:43 +00:00
366 lines
13 KiB
C#
366 lines
13 KiB
C#
//#define DEBUGCONNECTOR_DEBUG
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Text;
|
|
using System.IO;
|
|
using System.Threading;
|
|
|
|
namespace Cosmos.Debug.DebugConnectors
|
|
{
|
|
/// <summary>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.
|
|
/// </summary>
|
|
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<Exception> ConnectionLost;
|
|
public Action<UInt32> CmdTrace;
|
|
public Action<UInt32> CmdBreak;
|
|
public Action<byte[]> CmdMethodContext;
|
|
public Action<string> CmdText;
|
|
public Action<string> CmdMessageBox;
|
|
public Action CmdStarted;
|
|
public Action<string> OnDebugMsg;
|
|
public Action<byte[]> CmdRegisters;
|
|
public Action<byte[]> CmdFrame;
|
|
public Action<byte[]> CmdStack;
|
|
public Action<byte[]> CmdPong;
|
|
public Action<byte, byte, byte[]> CmdChannel;
|
|
public Action<byte[]> CmdCoreDump;
|
|
public Action<UInt32> CmdStackCorruptionOccurred;
|
|
public Action<UInt32> CmdStackOverflowOccurred;
|
|
public Action<UInt32> CmdInterruptOccurred;
|
|
public Action<UInt32> CmdNullReferenceOccurred;
|
|
public Action<uint> CmdSimpleNumber;
|
|
public Action<uint> CmdKernelPanic;
|
|
public Action<ulong> CmdSimpleLongNumber;
|
|
public Action<float> CmdComplexNumber;
|
|
public Action<double> CmdComplexLongNumber;
|
|
public Action<Exception> 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);
|
|
}
|
|
}
|
|
|
|
/// <summary>Descendants must invoke this method whenever they detect an incoming connection.</summary>
|
|
public void DoConnected()
|
|
{
|
|
if (Connected != null)
|
|
{
|
|
Connected();
|
|
}
|
|
}
|
|
|
|
/// <summary>Defines the handler to be invoked when a connection occurs on this condector. This
|
|
/// method is for use by the AD7Process instance.</summary>
|
|
/// <param name="handler">The handler to be notified when a connection occur.</param>
|
|
public void SetConnectionHandler(Action handler)
|
|
{
|
|
Connected = handler;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Method to do debug logging of the debug connector itself.
|
|
/// </summary>
|
|
[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<byte[]> MethodContextDatas = new List<byte[]>();
|
|
private List<byte[]> MemoryDatas = new List<byte[]>();
|
|
|
|
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(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.StackOverflowOccurred:
|
|
DebugLog("DC Recv: StackOverflowOccurred");
|
|
Next(4, PacketStackOverflowOccurred);
|
|
break;
|
|
|
|
case Ds2Vs.InterruptOccurred:
|
|
DebugLog("DC Recv: InterruptOccurred");
|
|
Next(4, PacketInterruptOccurred);
|
|
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.KernelPanic:
|
|
DebugLog("DC Recv: KernelPanic");
|
|
Next(4, PacketKernelPanic);
|
|
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;
|
|
|
|
case Ds2Vs.CoreDump:
|
|
DebugLog("DC Recv: CoreDump");
|
|
Next(-1, PacketCoreDump);
|
|
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)
|
|
{
|
|
mCancellationTokenSource.Cancel();
|
|
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));
|
|
}
|
|
|
|
|
|
}
|
|
}
|