Skip to content

Commit

Permalink
Merge pull request WildernessLabs#67 from WildernessLabs/debugging-re…
Browse files Browse the repository at this point in the history
…base

Debugging (rebase)
  • Loading branch information
alexischr authored Jun 3, 2021
2 parents 64746af + fe946e1 commit ba72b5e
Show file tree
Hide file tree
Showing 6 changed files with 152 additions and 89 deletions.
102 changes: 60 additions & 42 deletions Meadow.CLI.Core/DeviceManagement/MeadowDeviceManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.IO.Ports;
using System.Linq;
using System.Management;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using Meadow.CLI.Internals.MeadowComms.RecvClasses;
Expand Down Expand Up @@ -279,28 +280,48 @@ public static void ForwardMonoDataToVisualStudio(byte[] debuggerData)
debuggingServer.SendToVisualStudio(debuggerData);
}

// Creates a DebuggingServer that can listen on the given port on all network interfaces.
public static Task<DebuggingServer> CreateDebuggingServer(MeadowSerialDevice meadow, int vsDebugPort = 0)
{
if (vsDebugPort == 0)
{
Console.WriteLine($"Without '--VSDebugPort' being specified, will assume Visual Studio 2019 using default port {DefaultVS2019DebugPort}");
vsDebugPort = DefaultVS2019DebugPort;
}
return CreateDebuggingServer(meadow, new IPEndPoint(IPAddress.Any, vsDebugPort));
}

// Enter StartDebugging mode.
public static async Task StartDebugging(MeadowSerialDevice meadow, int vsDebugPort)
public static async Task<DebuggingServer> CreateDebuggingServer(MeadowSerialDevice meadow, IPEndPoint localEndpoint)
{
// Create the DebuggingServer first so we aren't racing for it in ForwardMonoDataToVisualStudio after Meadow restarts
debuggingServer = new DebuggingServer(localEndpoint);

// Tell meadow to start it's debugging server, after restarting.
_meadowRequestType = HcomMeadowRequestType.HCOM_MDOW_REQUEST_MONO_START_DBG_SESSION;
await new SendTargetData(meadow).SendSimpleCommand(_meadowRequestType);

// The previous command caused Meadow to restart. Therefore, we must reestablish
// Meadow communication.
meadow.AttemptToReconnectToMeadow();

// Create an instance of the TCP socket send/receiver class and
// start it receiving.
if (vsDebugPort == 0)
var attempts = 0;
retry:
try
{
Console.WriteLine($"Without '--VSDebugPort' being specified, will assume Visual Studio 2019 using default port {DefaultVS2019DebugPort}");
vsDebugPort = DefaultVS2019DebugPort;
attempts++;
meadow.AttemptToReconnectToMeadow();
} catch (Exception ex) when (ex is IOException || ex.InnerException is IOException)
{
if (attempts < 5)
{
await Task.Yield();
goto retry;
} else
{
throw;
}
}

// Start the local Meadow.CLI debugging server
debuggingServer = new DebuggingServer(vsDebugPort);
debuggingServer.StartListening(meadow);
return debuggingServer;
}

public static void EnterEchoMode(MeadowSerialDevice meadow)
Expand Down Expand Up @@ -332,22 +353,22 @@ public static async Task Esp32Restart(MeadowSerialDevice meadow)

//public static async Task
// GetInitialFileBytes(MeadowSerialDevice meadow, string fileName)
// {
// await ProcessCommand(meadow, HcomMeadowRequestType.HCOM_MDOW_REQUEST_GET_INITIAL_FILE_BYTES);

// {
// await ProcessCommand(meadow, HcomMeadowRequestType.HCOM_MDOW_REQUEST_GET_INITIAL_FILE_BYTES);

// _meadowRequestType = HcomMeadowRequestType.HCOM_MDOW_REQUEST_GET_INITIAL_FILE_BYTES;
// await new SendTargetData(meadow).SendSimpleCommand(_meadowRequestType);
// return await WaitForResponseMessage(meadow, p => p.MessageType == MeadowMessageType.Data, millisecondDelay: 1000);

/*
// return await WaitForResponseMessage(meadow, p => p.MessageType == MeadowMessageType.Data, millisecondDelay: 1000);

/*
await ProcessCommand(meadow, HcomMeadowRequestType.HCOM_MDOW_REQUEST_GET_INITIAL_FILE_BYTES);
await new SendTargetData(meadow).SendSimpleCommand(_meadowRequestType);
return await WaitForResponseMessage(meadow, p => p.MessageType == MeadowMessageType.DeviceInfo, millisecondDelay: timeoutMs);
*/
return await WaitForResponseMessage(meadow, p => p.MessageType == MeadowMessageType.DeviceInfo, millisecondDelay: timeoutMs);
*/
// }

public static async Task DeployApp(MeadowSerialDevice meadow, string applicationFilePath)
public static async Task DeployApp(MeadowSerialDevice meadow, string applicationFilePath, bool includeDebugSymbols = true)
{
if (!File.Exists(applicationFilePath))
{
Expand All @@ -371,8 +392,7 @@ public static async Task DeployApp(MeadowSerialDevice meadow, string application

var files = new List<string>();
var crcs = new List<uint>();

foreach (var file in paths)
void AddFile(string file, bool lookForPDB)
{
using (FileStream fs = File.Open(file, FileMode.Open))
{
Expand All @@ -388,27 +408,25 @@ public static async Task DeployApp(MeadowSerialDevice meadow, string application
files.Add(Path.GetFileName(file));
crcs.Add(crc);
}
if (lookForPDB)
{
var pdbFile = Path.ChangeExtension(file, "pdb");
if (File.Exists(pdbFile))
AddFile(pdbFile, false);
}
}

foreach (var file in paths)
{
AddFile(file, includeDebugSymbols);
}

var dependences = AssemblyManager.GetDependencies(fi.Name, fi.DirectoryName);

//crawl dependences
foreach (var file in dependences)
{
using (FileStream fs = File.Open(Path.Combine(fi.DirectoryName, file), FileMode.Open))
{
var len = (int)fs.Length;
var bytes = new byte[len];

fs.Read(bytes, 0, len);

//0x
var crc = CrcTools.Crc32part(bytes, len, 0);// 0x04C11DB7);

Console.WriteLine($"{file} crc is {crc}");
files.Add(Path.GetFileName(file));
crcs.Add(crc);
}
AddFile(Path.Combine(fi.DirectoryName, file), includeDebugSymbols);
}

// delete unused files
Expand Down Expand Up @@ -460,12 +478,12 @@ public static async Task ProcessCommand(MeadowSerialDevice meadow, HcomMeadowReq
}
}

/// <summary>
///
/// </summary>
/// <param name="meadow"></param>
/// <param name="filter"></param>
/// <param name="millisecondDelay"></param>
/// <summary>
///
/// </summary>
/// <param name="meadow"></param>
/// <param name="filter"></param>
/// <param name="millisecondDelay"></param>
/// <returns></returns>
public static async Task<(bool Success, string Message, MeadowMessageType MessageType)>
WaitForResponseMessage(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,7 @@ bool ParseAndProcessReceivedPacket(byte[] receivedMsg, int receivedMsgLen)

// Debug message from Meadow for Visual Studio
case (ushort)HcomHostRequestType.HCOM_HOST_REQUEST_DEBUGGING_MONO_DATA:
ConsoleOut($"Debugging message from Meadow for Visual Studio"); // TESTING
//ConsoleOut($"Debugging message from Meadow for Visual Studio"); // TESTING
MeadowDeviceManager.ForwardMonoDataToVisualStudio(processor.MessageData);
break;

Expand Down
121 changes: 76 additions & 45 deletions Meadow.CLI.Core/Internals/MeadowComms/RecvClasses/DebuggingServer.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net;
using System.Net.Sockets;
Expand All @@ -16,51 +17,68 @@ public class DebuggingServer
// VS 2019 - 4024
// VS 2017 - 4022
// VS 2015 - 4020
int vsPort;
public IPEndPoint LocalEndpoint { get; private set; }
ActiveClient activeClient;
int activeClientCount = 0;

List<byte[]> buffers = new List<byte[]>();

// Constructor
public DebuggingServer(int visualStudioPort)
public DebuggingServer(IPEndPoint localEndpoint)
{
vsPort = visualStudioPort;
LocalEndpoint = localEndpoint;
}

public async void StartListening(MeadowSerialDevice meadow)
{
try
{
IPHostEntry ipHostInfo = Dns.GetHostEntry("localhost");
IPAddress ipAddress = ipHostInfo.AddressList[0];
IPEndPoint localEndPoint = new IPEndPoint(ipAddress, vsPort);

TcpListener tcpListener = new TcpListener(localEndPoint);
TcpListener tcpListener = new TcpListener(LocalEndpoint);
tcpListener.Start();
LocalEndpoint = (IPEndPoint)tcpListener.LocalEndpoint;
Console.WriteLine("Listening for Visual Studio to connect");

while(true)
{
await Task.Run(async () =>
{
// Wait for client to connect
TcpClient tcpClient = await tcpListener.AcceptTcpClientAsync();
// Wait for client to connect
TcpClient tcpClient = await tcpListener.AcceptTcpClientAsync();
OnConnect(meadow, tcpClient);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}

// tcpClient valid after connection
Console.WriteLine("Visual Studio has connected");
if (activeClientCount > 0)
{
Debug.Assert(activeClientCount == 1);
Debug.Assert(activeClient != null);
activeClient.Close();
activeClient = null;
activeClientCount = 0;
}

activeClient = new ActiveClient(this, tcpClient);
activeClient.ReceiveVSDebug(meadow);
activeClientCount++;
});
public void Connect(MeadowSerialDevice meadow)
{
TcpClient tcpClient = new TcpClient();
tcpClient.Connect(LocalEndpoint);
OnConnect(meadow, tcpClient);
}

void OnConnect(MeadowSerialDevice meadow, TcpClient tcpClient)
{
try
{
Console.WriteLine("Visual Studio has connected");
if (activeClientCount > 0)
{
Debug.Assert(activeClientCount == 1);
Debug.Assert(activeClient != null);
CloseActiveClient();
}

activeClient = new ActiveClient(this, tcpClient);
lock (buffers)
{
foreach (var buffer in buffers)
activeClient.SendToVisualStudio(buffer);
buffers.Clear();
}
activeClient.ReceiveVSDebug(meadow);
activeClientCount++;
}
catch (Exception ex)
{
Expand All @@ -73,11 +91,21 @@ internal void CloseActiveClient()
activeClient.Close();
activeClient = null;
activeClientCount = 0;
lock (buffers)
buffers.Clear();
}

public void SendToVisualStudio(byte[] byteData)
{
activeClient.SendToVisualStudio(byteData);
if (activeClient is ActiveClient ac)
{
ac.SendToVisualStudio(byteData);
return;
}

// Buffer the data until VS connects
lock (buffers)
buffers.Add(byteData);
}

// Imbedded class
Expand Down Expand Up @@ -112,25 +140,31 @@ internal async void ReceiveVSDebug(MeadowSerialDevice meadow)
// Receive from Visual Studio and send to Meadow
await Task.Run(async () =>
{
var recvdBuffer = new byte[490];
var meadowBuffer = Array.Empty<byte>();
while (tcpClient.Connected && okayToRun)
{
var recvdBuffer = new byte[490];
var bytesRead = await networkStream.ReadAsync(recvdBuffer, 0, recvdBuffer.Length);
if (!okayToRun)
int bytesRead;

read:
bytesRead = await networkStream.ReadAsync(recvdBuffer, 0, recvdBuffer.Length);
if (bytesRead == 0 || !okayToRun)
break;

var destIndex = meadowBuffer.Length;
Array.Resize(ref meadowBuffer, destIndex + bytesRead);
Array.Copy(recvdBuffer, 0, meadowBuffer, destIndex, bytesRead);

if (bytesRead > 0)
{
// Console.WriteLine($"{DateTime.Now:HH:mm:ss.fff}-Received {bytesRead} bytes from VS will forward to HCOM");
// Ensure we read all the data in this message before passing it along
if (networkStream.DataAvailable)
goto read;

// Need a buffer the exact size of received data to work with CLI
var meadowBuffer = new byte[bytesRead];
Array.Copy(recvdBuffer, 0, meadowBuffer, 0, bytesRead);
// Console.WriteLine($"{DateTime.Now:HH:mm:ss.fff}-Received {bytesRead} bytes from VS will forward to HCOM");

// Forward to Meadow
MeadowDeviceManager.ForwardVisualStudioDataToMono(meadowBuffer, meadow, 0);
//Console.WriteLine($"{DateTime.Now:HH:mm:ss.fff}-Forwarded {bytesRead} from VS to Meadow");
}
// Forward to Meadow
MeadowDeviceManager.ForwardVisualStudioDataToMono(meadowBuffer, meadow, 0);
//Console.WriteLine($"{DateTime.Now:HH:mm:ss.fff}-Forwarded {bytesRead} from VS to Meadow");
meadowBuffer = Array.Empty<byte>();
}
});
}
Expand Down Expand Up @@ -161,10 +195,7 @@ public async void SendToVisualStudio(byte[] byteData)
return;
}

await Task.Run(async () =>
{
await networkStream.WriteAsync(byteData, 0, byteData.Length);
});
await networkStream.WriteAsync(byteData, 0, byteData.Length);
}
catch (Exception e)
{
Expand Down
2 changes: 1 addition & 1 deletion Meadow.CLI/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -464,7 +464,7 @@ static async Task ProcessHcom(Options options)
}
else if (options.StartDebugging)
{
MeadowDeviceManager.StartDebugging(device, options.VSDebugPort);
(await MeadowDeviceManager.CreateDebuggingServer(device, options.VSDebugPort)).StartListening(device);
Console.WriteLine($"Ready for Visual Studio debugging");
options.KeepAlive = true;
}
Expand Down
4 changes: 4 additions & 0 deletions Meadow.CLI/Properties/launchSettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@
"commandName": "Project",
"commandLineArgs": "--Logout"
},
"StartDebugging": {
"commandName": "Project",
"commandLineArgs": "--StartDebugging"
},
"FlashOS": {
"commandName": "Project",
"commandLineArgs": "--FlashOS"
Expand Down
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,16 @@ You can set the debug trace level to values 0, 1, 2, or 3. 2 is the most useful.
`MeadowCLI.exe --GetDeviceInfo` - Outputs Meadow OS version and other information
`MeadowCLI.exe --GetDeviceName` - Outputs Meadow device name contained in configuration file

### Debugging

`MeadowCLI.exe --VSDebugPort XXXX --StartDebugging` - Starts listening for debugging connection (substitute XXXX for a free port number)

Note: you can use SDB command line debugger from https://github.com/mono/sdb. Just build it according to its readme, run the above command and then:

`sdb "connect 127.0.0.1 XXXX"` (substitute XXXX for the same port number as above)



## Running applications

You'll typically need at least 5 files installed to the Meadow flash to run a Meadow app:
Expand Down

0 comments on commit ba72b5e

Please sign in to comment.