diff --git a/mono-debug.csproj b/mono-debug.csproj
index 51393e2..7e22a94 100644
--- a/mono-debug.csproj
+++ b/mono-debug.csproj
@@ -66,6 +66,10 @@
+
+
+
+
diff --git a/package.json b/package.json
index 80acada..25dc8f5 100644
--- a/package.json
+++ b/package.json
@@ -144,12 +144,16 @@
],
"configurationAttributes": {
"launch": {
- "required": ["program"],
+ "required": [],
"properties": {
"program": {
"type": "string",
"description": "%mono.launch.program.description%"
},
+ "packageName":{
+ "type": "string",
+ "description": "%mono.launch.packageName.description%"
+ },
"args": {
"type": "array",
"description": "%mono.launch.args.description%",
diff --git a/package.nls.json b/package.nls.json
index 71436cd..901916f 100644
--- a/package.nls.json
+++ b/package.nls.json
@@ -17,6 +17,7 @@
"mono.attach.config.name": "Attach",
"mono.launch.program.description": "Absolute path to the program.",
+ "mono.launch.packageName.description": "Android package name.",
"mono.launch.args.description": "Command line arguments passed to the program.",
"mono.launch.cwd.description": "Absolute path to the working directory of the program being debugged.",
"mono.launch.runtimeExecutable.description": "Absolute path to the runtime executable to be used. Default is the runtime executable on the PATH.",
diff --git a/src/MonoDebugSession.cs b/src/MonoDebugSession.cs
index 72c94f9..ec7242e 100644
--- a/src/MonoDebugSession.cs
+++ b/src/MonoDebugSession.cs
@@ -1,4 +1,4 @@
-/*---------------------------------------------------------------------------------------------
+/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
@@ -9,7 +9,7 @@
using System.Linq;
using System.Net;
using Mono.Debugging.Client;
-
+using System.Diagnostics;
namespace VSCodeDebug
{
@@ -180,7 +180,19 @@ public override void Initialize(Response response, dynamic args)
SendEvent(new InitializedEvent());
}
- public override async void Launch(Response response, dynamic args)
+ public override void Launch(Response response, dynamic args)
+ {
+ if (args.packageName != null)
+ {
+ LaunchXamarinAndroid(response, args);
+ }
+ else
+ {
+ LaunchMono(response, args);
+ }
+ }
+
+ public async void LaunchMono(Response response, dynamic args)
{
_attachMode = false;
@@ -415,6 +427,76 @@ public override void Attach(Response response, dynamic args)
SendResponse(response);
}
+ public void LaunchXamarinAndroid(Response response, dynamic args)
+ {
+ _attachMode = true;
+
+ SetExceptionBreakpoints(args.__exceptionOptions);
+
+ var packageName = getString(args, "packageName", null);
+ if (packageName == null) {
+ SendErrorResponse(response, 3008, "Property 'packageName' is missing.");
+ return;
+ }
+
+ var forwardOutput = RunAdbForResult("forward tcp:0 tcp:10000");
+ var port = int.Parse(forwardOutput);
+ RunAdbForResult("shell setprop debug.mono.connect port=10000,timeout=2000000000");
+ RunAdbForResult($"shell am force-stop {packageName}");
+ RunAdbForResult($"shell monkey -p {packageName} -c android.intent.category.LAUNCHER 1");
+ System.Threading.Thread.Sleep(500);
+
+ lock (_lock) {
+
+ _debuggeeKilled = false;
+ var console = RunAdb("shell logcat mono-stdout:D *:S");
+ var args0 = new XamarinDebuggerArgs(port, console) {
+ MaxConnectionAttempts = MAX_CONNECTION_ATTEMPTS,
+ TimeBetweenConnectionAttempts = CONNECTION_ATTEMPT_INTERVAL
+ };
+
+ _session.Run(new Mono.Debugging.Soft.SoftDebuggerStartInfo(args0), _debuggerSessionOptions);
+
+ _debuggeeExecuting = true;
+ }
+
+ SendResponse(response);
+ }
+
+ private string RunAdbForResult(string args)
+ {
+ var adbProcessInfo = new ProcessStartInfo
+ {
+ FileName = "adb",
+ Arguments = args,
+ RedirectStandardOutput = true,
+ UseShellExecute = false
+ };
+ var adbProcess = Process.Start(adbProcessInfo);
+ var result = adbProcess.StandardOutput.ReadToEnd();
+ adbProcess.WaitForExit();
+ return result;
+ }
+
+ private StreamReader RunAdb(string args)
+ {
+ var adbProcessInfo = new ProcessStartInfo
+ {
+ FileName = "adb",
+ Arguments = args,
+ RedirectStandardOutput = true,
+ UseShellExecute = false
+ };
+ var adbProcess = Process.Start(adbProcessInfo);
+ adbProcess.ErrorDataReceived += (o, e) => {
+ Console.WriteLine(e.Data);
+ };
+ adbProcess.OutputDataReceived += (o, e) => {
+ Console.WriteLine(e.Data);
+ };
+ return adbProcess.StandardOutput;
+ }
+
public override void Disconnect(Response response, dynamic args)
{
if (_attachMode) {
diff --git a/src/XamarinConnectionProvider.cs b/src/XamarinConnectionProvider.cs
new file mode 100644
index 0000000..6e7d197
--- /dev/null
+++ b/src/XamarinConnectionProvider.cs
@@ -0,0 +1,44 @@
+using System;
+using System.IO;
+using System.Net;
+using System.Net.Sockets;
+using Mono.Debugger.Soft;
+using Mono.Debugging.Client;
+using Mono.Debugging.Soft;
+
+
+namespace VSCodeDebug
+{
+ public class XamarinConnectionProvider : ISoftDebuggerConnectionProvider
+ {
+ private readonly IPEndPoint _endPoint;
+ private readonly StreamReader _console;
+
+ public XamarinConnectionProvider(int port, StreamReader console = null)
+ {
+ _endPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), port);
+ _console = console;
+ }
+
+ public IAsyncResult BeginConnect(DebuggerStartInfo dsi, AsyncCallback callback)
+ {
+ return XamarinVirtualMachineManager.BeginConnect(_endPoint, _console, callback);
+ }
+
+ public void CancelConnect(IAsyncResult result)
+ {
+ XamarinVirtualMachineManager.CancelConnection(result);
+ }
+
+ public void EndConnect(IAsyncResult result, out VirtualMachine vm, out string appName)
+ {
+ vm = XamarinVirtualMachineManager.EndConnect(result);
+ appName = null;
+ }
+
+ public bool ShouldRetryConnection(Exception ex)
+ {
+ return false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/XamarinDebuggerArgs.cs b/src/XamarinDebuggerArgs.cs
new file mode 100644
index 0000000..db6cfb8
--- /dev/null
+++ b/src/XamarinDebuggerArgs.cs
@@ -0,0 +1,15 @@
+using Mono.Debugging.Soft;
+using System.IO;
+
+namespace VSCodeDebug
+{
+ public class XamarinDebuggerArgs : SoftDebuggerStartArgs
+ {
+ public override ISoftDebuggerConnectionProvider ConnectionProvider { get; }
+
+ public XamarinDebuggerArgs(int port, StreamReader deviceConsole = null)
+ {
+ ConnectionProvider = new XamarinConnectionProvider(port, deviceConsole);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/XamarinTcpConnection.cs b/src/XamarinTcpConnection.cs
new file mode 100644
index 0000000..288884c
--- /dev/null
+++ b/src/XamarinTcpConnection.cs
@@ -0,0 +1,46 @@
+using System.IO;
+using System.Net;
+using System.Net.Sockets;
+using Mono.Debugger.Soft;
+
+namespace VSCodeDebug
+{
+ class XamarinTcpConnection : Connection
+ {
+ Socket socket;
+
+ internal XamarinTcpConnection (Socket socket, TextWriter logWriter)
+ : base (logWriter)
+ {
+ this.socket = socket;
+ //socket.SetSocketOption (SocketOptionLevel.IP, SocketOptionName.NoDelay, 1);
+ }
+
+ internal EndPoint EndPoint {
+ get {
+ return socket.RemoteEndPoint;
+ }
+ }
+
+ protected override int TransportSend (byte[] buf, int buf_offset, int len)
+ {
+ return socket.Send (buf, buf_offset, len, SocketFlags.None);
+ }
+
+ protected override int TransportReceive (byte[] buf, int buf_offset, int len)
+ {
+ return socket.Receive (buf, buf_offset, len, SocketFlags.None);
+ }
+
+ protected override void TransportSetTimeouts (int send_timeout, int receive_timeout)
+ {
+ socket.SendTimeout = send_timeout;
+ socket.ReceiveTimeout = receive_timeout;
+ }
+
+ protected override void TransportClose ()
+ {
+ socket.Close ();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/XamarinVirtualMachineManager.cs b/src/XamarinVirtualMachineManager.cs
new file mode 100644
index 0000000..7010aec
--- /dev/null
+++ b/src/XamarinVirtualMachineManager.cs
@@ -0,0 +1,60 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Net;
+using System.Net.Sockets;
+using System.Runtime.Remoting.Messaging;
+using System.Text;
+using Mono.Debugger.Soft;
+
+namespace VSCodeDebug
+{
+
+ public static class XamarinVirtualMachineManager
+ {
+ private const string START_DEBUGGER_COMMAND = "start debugger: sdb";
+ private const string CONNECT_STDOUT_COMMAND = "connect stdout";
+
+ private delegate VirtualMachine ConnectWithConsoleOutputCallback (Socket dbg_sock, IPEndPoint dbg_ep, StreamReader console, TextWriter logWriter);
+
+ private static VirtualMachine ConnectWithConsoleOutput(Socket dbg_sock, IPEndPoint dbg_ep, StreamReader console, TextWriter logWriter = null) {
+ dbg_sock.Connect (dbg_ep);
+ SendCommand(dbg_sock, START_DEBUGGER_COMMAND);
+ Connection transport = new XamarinTcpConnection (dbg_sock, logWriter);
+ return VirtualMachineManager.Connect (transport, console, null);
+ }
+
+ private static void SendCommand(Socket socket, string command)
+ {
+ byte[] commandBin = System.Text.Encoding.ASCII.GetBytes(command);
+ byte[] commandLenght = new byte[] { (byte)commandBin.Length };
+ socket.Send(commandLenght, 0, commandLenght.Length, SocketFlags.None);
+ socket.Send(commandBin, 0, commandBin.Length, SocketFlags.None);
+ }
+
+ public static IAsyncResult BeginConnect(IPEndPoint debugEndPoint, StreamReader console, AsyncCallback callback, TextWriter logWriter = null) {
+ Socket debugSocket = null;
+ debugSocket = new Socket (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
+ ConnectWithConsoleOutputCallback c = new ConnectWithConsoleOutputCallback (ConnectWithConsoleOutput);
+ return c.BeginInvoke (debugSocket, debugEndPoint, console, logWriter, callback, debugSocket);
+ }
+
+ public static VirtualMachine EndConnect(IAsyncResult asyncResult) {
+ if (asyncResult == null)
+ throw new ArgumentNullException ("asyncResult");
+
+ if (!asyncResult.IsCompleted)
+ asyncResult.AsyncWaitHandle.WaitOne ();
+
+ AsyncResult result = (AsyncResult) asyncResult;
+ ConnectWithConsoleOutputCallback cb = (ConnectWithConsoleOutputCallback) result.AsyncDelegate;
+ return cb.EndInvoke(asyncResult);
+ }
+
+ public static void CancelConnection(IAsyncResult asyncResult)
+ {
+ ((IDisposable) asyncResult.AsyncState).Dispose();
+ }
+ }
+}