Skip to content

Commit

Permalink
Add MicrosoftSymbolsStore support. Add `StepOverPropertiesAndOperat…
Browse files Browse the repository at this point in the history
…ors` option
  • Loading branch information
JaneySprings committed Apr 24, 2024
1 parent 2077215 commit 3d852e3
Show file tree
Hide file tree
Showing 13 changed files with 199 additions and 54 deletions.
10 changes: 10 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,16 @@
"default": true,
"description": "%configuration.description.debuggerOptions.projectAssembliesOnly%"
},
"dotnetMeteor.debuggerOptions.stepOverPropertiesAndOperators": {
"type": "boolean",
"default": false,
"description": "%configuration.description.debuggerOptions.stepOverPropertiesAndOperators%"
},
"dotnetMeteor.debuggerOptions.searchMicrosoftSymbolServer": {
"type": "boolean",
"default": false,
"description": "%configuration.description.debuggerOptions.searchMicrosoftSymbolServer%"
},
"dotnetMeteor.debuggerOptions.integerDisplayFormat": {
"type": "string",
"default": "Decimal",
Expand Down
3 changes: 3 additions & 0 deletions package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,8 @@
"configuration.description.debuggerOptions.ellipsizeStrings": "Truncates strings in the debugging window.",
"configuration.description.debuggerOptions.ellipsizedLength": "The maximum length of a truncated string.",
"configuration.description.debuggerOptions.projectAssembliesOnly": "Specifies whether the debugger should consider only project assemblies as user code.",
"configuration.description.debuggerOptions.stepOverPropertiesAndOperators": "Specifies whether the debugger should step over properties and operators.",
"configuration.description.debuggerOptions.searchMicrosoftSymbolServer": "Search for symbols on the Microsoft Symbol Server.",
"configuration.description.debuggerOptions.searchNuGetSymbolServer": "Search for symbols on the NuGet Symbol Server.",
"configuration.description.debuggerOptions.integerDisplayFormat": "Specifies the format of displayed integers in the debugging window."
}
32 changes: 17 additions & 15 deletions src/DotNet.Meteor.Debug/Agents/DebugLaunchAgent.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System;
using System.IO;
using System.Net;
using DotNet.Meteor.Debug.Extensions;
using DotNet.Meteor.Debug.Sdb;
Expand All @@ -11,22 +10,25 @@
namespace DotNet.Meteor.Debug;

public class DebugLaunchAgent : BaseLaunchAgent {
public DebugLaunchAgent(LaunchConfiguration configuration) : base(configuration) {}
private readonly SoftDebuggerStartArgs startArguments;
private readonly SoftDebuggerStartInfo startInformation;

public override void Connect(SoftDebuggerSession session) {
SoftDebuggerStartArgs arguments = null;
public DebugLaunchAgent(LaunchConfiguration configuration) : base(configuration) {
if (configuration.Device.IsAndroid || (configuration.Device.IsIPhone && !configuration.Device.IsEmulator))
startArguments = new ClientConnectionProvider(IPAddress.Loopback, configuration.DebugPort, configuration.Project.Name);
else if (configuration.Device.IsIPhone || configuration.Device.IsMacCatalyst)
startArguments = new ServerConnectionProvider(IPAddress.Loopback, configuration.DebugPort, configuration.Project.Name);

if (Configuration.Device.IsAndroid || (Configuration.Device.IsIPhone && !Configuration.Device.IsEmulator))
arguments = new ClientConnectionProvider(IPAddress.Loopback, Configuration.DebugPort, Configuration.Project.Name);
else if (Configuration.Device.IsIPhone || Configuration.Device.IsMacCatalyst)
arguments = new ServerConnectionProvider(IPAddress.Loopback, Configuration.DebugPort, Configuration.Project.Name);
ArgumentNullException.ThrowIfNull(startArguments, "Debugger connection arguments not implemented.");

var debuggerStartInfo = new SoftDebuggerStartInfo(arguments);
if (Configuration.DebuggerSessionOptions.ProjectAssembliesOnly)
debuggerStartInfo.SetUserAssemblyNames(Configuration.GetApplicationAssembliesDirectory());
startInformation = new SoftDebuggerStartInfo(startArguments);
startInformation.SetAssemblySymbols(configuration.GetApplicationAssembliesDirectory(), configuration.DebuggerSessionOptions);
if (configuration.DebuggerSessionOptions.ProjectAssembliesOnly)
startInformation.SetUserAssemblyNames(configuration.GetApplicationAssembliesDirectory(), configuration.DebuggerSessionOptions);
}

ArgumentNullException.ThrowIfNull(arguments, "Debugger connection arguments not implemented.");
session.Run(debuggerStartInfo, Configuration.DebuggerSessionOptions);
public override void Connect(SoftDebuggerSession session) {
session.Run(startInformation, Configuration.DebuggerSessionOptions);
}
public override void Launch(IProcessLogger logger) {
if (Configuration.Device.IsAndroid)
Expand All @@ -43,7 +45,7 @@ public class DebugLaunchAgent : BaseLaunchAgent {
// TODO: Implement Apple launching for Windows
// if (RuntimeSystem.IsWindows) {
// IDeviceTool.Installer(Configuration.Device.Serial, Configuration.OutputAssembly, this);

// var debugProcess = IDeviceTool.Debug(Configuration.Device.Serial, Configuration.GetApplicationId(), Configuration.DebugPort, this);
// disposables.Add(() => debugProcess.Kill());
// return;
Expand All @@ -55,7 +57,7 @@ public class DebugLaunchAgent : BaseLaunchAgent {
} else {
var debugPortForwarding = MonoLaunch.TcpTunnel(Configuration.Device.Serial, Configuration.DebugPort, logger);
MonoLaunch.InstallDev(Configuration.Device.Serial, Configuration.OutputAssembly, logger);

var debugProcess = MonoLaunch.DebugDev(Configuration.Device.Serial, Configuration.OutputAssembly, Configuration.DebugPort, logger);
Disposables.Add(() => debugProcess.Terminate());
Disposables.Add(() => debugPortForwarding.Terminate());
Expand Down
18 changes: 9 additions & 9 deletions src/DotNet.Meteor.Debug/Agents/GCDumpLaunchAgent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ public class GCDumpLaunchAgent : BaseLaunchAgent {
private string gcdumpPath;
private int applicationPID;

public GCDumpLaunchAgent(LaunchConfiguration configuration) : base(configuration) {}
public GCDumpLaunchAgent(LaunchConfiguration configuration) : base(configuration) { }

public override void Connect(SoftDebuggerSession session) {}
public override void Connect(SoftDebuggerSession session) { }
public override void Launch(IProcessLogger logger) {
gcdumpPath = Path.Combine(Configuration.TempDirectoryPath, $"{Configuration.GetApplicationName()}.gcdump");
diagnosticPort = Path.Combine(RuntimeSystem.HomeDirectory, $"{Configuration.Device.Platform}-port.lock");
Expand All @@ -41,7 +41,7 @@ public class GCDumpLaunchAgent : BaseLaunchAgent {
if (string.IsNullOrEmpty(diagnosticPort) || string.IsNullOrEmpty(gcdumpPath))
return;

var gcdumpProcess = applicationPID == 0
var gcdumpProcess = applicationPID == 0
? GCDump.Collect($"{diagnosticPort},connect", gcdumpPath, logger)
: GCDump.Collect(applicationPID, gcdumpPath, logger);

Expand All @@ -54,17 +54,17 @@ public class GCDumpLaunchAgent : BaseLaunchAgent {
}

private void LaunchAppleMobile(IProcessLogger logger) {
if (Configuration.Device.IsEmulator) {
if (Configuration.Device.IsEmulator) {
var routerProcess = DSRouter.ServerToClient($"{diagnosticPort}", $"127.0.0.1:{Configuration.ProfilerPort}", false, logger);
var simProcess = MonoLaunch.ProfileSim(Configuration.Device.Serial, Configuration.OutputAssembly, $"127.0.0.1:{Configuration.ProfilerPort},nosuspend,listen", logger);

Disposables.Add(() => routerProcess.Terminate());
Disposables.Add(() => simProcess.Terminate());
} else {
var routerProcess = DSRouter.ServerToClient(diagnosticPort, $"127.0.0.1:{Configuration.ProfilerPort}", forwardApple: true, logger);
MonoLaunch.InstallDev(Configuration.Device.Serial, Configuration.OutputAssembly, logger);
var devProcess = MonoLaunch.ProfileDev(Configuration.Device.Serial, Configuration.OutputAssembly, $"127.0.0.1:{Configuration.ProfilerPort},nosuspend,listen", logger);

Disposables.Add(() => routerProcess.Terminate());
Disposables.Add(() => devProcess.Terminate());
}
Expand All @@ -85,10 +85,10 @@ public class GCDumpLaunchAgent : BaseLaunchAgent {
if (Configuration.Device.IsEmulator)
Configuration.Device.Serial = AndroidEmulator.Run(Configuration.Device.Name).Serial;

DeviceBridge.Reverse(Configuration.Device.Serial, Configuration.ProfilerPort, Configuration.ProfilerPort+1);
DeviceBridge.Reverse(Configuration.Device.Serial, Configuration.ProfilerPort, Configuration.ProfilerPort + 1);
DeviceBridge.Shell(Configuration.Device.Serial, "setprop", "debug.mono.profile", $"127.0.0.1:{Configuration.ProfilerPort},nosuspend,connect");
var routerProcess = DSRouter.ServerToServer(Configuration.ProfilerPort+1, logger);

var routerProcess = DSRouter.ServerToServer(Configuration.ProfilerPort + 1, logger);
applicationPID = routerProcess.Id;

if (Configuration.UninstallApp)
Expand Down
17 changes: 13 additions & 4 deletions src/DotNet.Meteor.Debug/DebugOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
namespace DotNet.Meteor.Debug;

public class DebugOptions {
[JsonPropertyName("evaluation_timeout")]
[JsonPropertyName("evaluation_timeout")]
public int EvaluationTimeout { get; set; } = ServerExtensions.DefaultDebuggerOptions.EvaluationOptions.EvaluationTimeout;

[JsonPropertyName("member_evaluation_timeout")]
[JsonPropertyName("member_evaluation_timeout")]
public int MemberEvaluationTimeout { get; set; } = ServerExtensions.DefaultDebuggerOptions.EvaluationOptions.MemberEvaluationTimeout;

[JsonPropertyName("allow_target_invoke")]
[JsonPropertyName("allow_target_invoke")]
public bool AllowTargetInvoke { get; set; } = ServerExtensions.DefaultDebuggerOptions.EvaluationOptions.AllowTargetInvoke;

[JsonPropertyName("allow_method_evaluation")]
Expand Down Expand Up @@ -47,6 +47,15 @@ public class DebugOptions {
[JsonPropertyName("project_assemblies_only")]
public bool ProjectAssembliesOnly { get; set; } = ServerExtensions.DefaultDebuggerOptions.ProjectAssembliesOnly;

[JsonPropertyName("step_over_properties_and_operators")]
public bool StepOverPropertiesAndOperators { get; set; } = ServerExtensions.DefaultDebuggerOptions.StepOverPropertiesAndOperators;

[JsonPropertyName("search_microsoft_symbol_server")]
public bool SearchMicrosoftSymbolServer { get; set; } = ServerExtensions.DefaultDebuggerOptions.SearchMicrosoftSymbolServer;

[JsonPropertyName("search_nuget_symbol_server")]
public bool SearchNuGetSymbolServer { get; set; } = ServerExtensions.DefaultDebuggerOptions.SearchNuGetSymbolServer;

internal static IntegerDisplayFormat GetIntegerDisplayFormat(string value) {
if (value == Mono.Debugging.Client.IntegerDisplayFormat.Decimal.ToString())
return Mono.Debugging.Client.IntegerDisplayFormat.Decimal;
Expand All @@ -58,4 +67,4 @@ public class DebugOptions {
}

[JsonSerializable(typeof(DebugOptions))]
internal partial class DebuggerOptionsContext : JsonSerializerContext {}
internal partial class DebuggerOptionsContext : JsonSerializerContext { }
5 changes: 3 additions & 2 deletions src/DotNet.Meteor.Debug/DebugSession.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,10 @@ public class DebugSession : Session {
protected override LaunchResponse HandleLaunchRequest(LaunchArguments arguments) {
return ServerExtensions.DoSafe(() => {
var configuration = new LaunchConfiguration(arguments.ConfigurationProperties);
launchAgent = configuration.GetLauchAgent();
SymbolServerExtensions.SetTempDirectory(configuration.TempDirectoryPath);
SymbolServerExtensions.SetEventLogger(OnDebugDataReceived);
launchAgent = configuration.GetLauchAgent();
typeResolver = new ExternalTypeResolver(configuration.TempDirectoryPath, configuration.DebuggerSessionOptions);
launchAgent.Disposables.Add(() => typeResolver.Dispose());
session.TypeResolverHandler = typeResolver.Handle;
Expand Down
42 changes: 40 additions & 2 deletions src/DotNet.Meteor.Debug/Extensions/MonoExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public static class MonoExtensions {
dv = dv.Substring(1, dv.Length - 2).Replace(Environment.NewLine, " ");
return dv;
}

public static StackFrame GetFrameSafe(this Backtrace bt, int n) {
try {
return bt.GetFrame(n);
Expand Down Expand Up @@ -55,9 +56,11 @@ public static class MonoExtensions {
options.UseExternalTypeResolver = useExternalTypeResolver;
return frame.GetExpressionValue(expression, options);
}
public static void SetUserAssemblyNames(this SoftDebuggerStartInfo startInfo, string assembliesDirectory) {

public static void SetUserAssemblyNames(this SoftDebuggerStartInfo startInfo, string assembliesDirectory, DebuggerSessionOptions options) {
var includeSymbolServers = options.SearchMicrosoftSymbolServer || options.SearchNuGetSymbolServer;
var assembliesPaths = Directory.EnumerateFiles(assembliesDirectory, "*.dll");
var files = assembliesPaths.Where(it => File.Exists(Path.ChangeExtension(it, ".pdb")));
var files = assembliesPaths.Where(it => SymbolServerExtensions.HasDebugSymbols(it, includeSymbolServers));
if (!files.Any())
return;

Expand Down Expand Up @@ -86,6 +89,41 @@ public static class MonoExtensions {
startInfo.UserAssemblyNames = names;
startInfo.AssemblyPathMap = pathMap;
}
public static void SetAssemblySymbols(this SoftDebuggerStartInfo startInfo, string assembliesDirectory, DebuggerSessionOptions options) {
var useMicrosoftServer = options.SearchMicrosoftSymbolServer;
var useNuGetServer = options.SearchNuGetSymbolServer;
startInfo.SetAssemblySymbols(assembliesDirectory, useMicrosoftServer, useNuGetServer);
}
public static void SetAssemblySymbols(this SoftDebuggerStartInfo startInfo, string assembliesDirectory, bool useMicrosoftServer, bool useNuGetServer) {
var assemblyPaths = Directory.EnumerateFiles(assembliesDirectory, "*.dll");
var targetAssemblyPaths = assemblyPaths.Where(it => !File.Exists(Path.ChangeExtension(it, ".pdb")));
if (!targetAssemblyPaths.Any() || (!useMicrosoftServer && !useNuGetServer))
return;

var symbolPathMap = new Dictionary<string, string>();
foreach (var assemblyPath in targetAssemblyPaths) {
string symbolsFilePath = null;
string assemblyName = null;

if (string.IsNullOrEmpty(symbolsFilePath) && useMicrosoftServer)
symbolsFilePath = SymbolServerExtensions.DownloadSourceSymbols(assemblyPath, SymbolServerExtensions.MicrosoftSymbolServerAddress);
if (string.IsNullOrEmpty(symbolsFilePath) && useNuGetServer)
symbolsFilePath = SymbolServerExtensions.DownloadSourceSymbols(assemblyPath, SymbolServerExtensions.NuGetSymbolServerAddress);

if (string.IsNullOrEmpty(symbolsFilePath)) {
DebuggerLoggingService.CustomLogger.LogMessage($"No symbols found for '{assemblyPath}'");
continue;
}

using var asm = Mono.Cecil.AssemblyDefinition.ReadAssembly(assemblyPath);
assemblyName = asm.Name.FullName;

if (!string.IsNullOrEmpty(assemblyName) && !string.IsNullOrEmpty(symbolsFilePath))
symbolPathMap.Add(assemblyName, symbolsFilePath);
}

startInfo.SymbolPathMap = symbolPathMap;
}

public static void WriteSdbCommand(this ISoftDebuggerConnectionProvider connectionProvider, Stream stream, string command) {
byte[] commandBytes = new byte[command.Length + 1];
Expand Down
5 changes: 5 additions & 0 deletions src/DotNet.Meteor.Debug/Extensions/ServerExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
namespace DotNet.Meteor.Debug.Extensions;

public static class ServerExtensions {
private static bool isAndroidAssembliesExtracted;
public static DebuggerSessionOptions DefaultDebuggerOptions { get; } = new DebuggerSessionOptions {
EvaluationOptions = new EvaluationOptions {
EvaluationTimeout = 1000,
Expand Down Expand Up @@ -56,6 +57,9 @@ public static class ServerExtensions {
}
public static string ExtractAndroidAssemblies(string assemblyPath) {
var targetDirectory = Path.GetDirectoryName(assemblyPath)!;
if (isAndroidAssembliesExtracted)
return targetDirectory;

try {
using var archive = new ZipArchive(File.OpenRead(assemblyPath));
var assembliesEntry = archive.Entries.Where(entry => entry.FullName.StartsWith("assemblies", StringComparison.OrdinalIgnoreCase));
Expand All @@ -72,6 +76,7 @@ public static class ServerExtensions {
using var stream = entry.Open();
stream.CopyTo(fileStream);
}
isAndroidAssembliesExtracted = true;
return targetDirectory;
} catch (Exception ex) {
DebuggerLoggingService.CustomLogger.LogError(ex.Message, ex);
Expand Down
Loading

0 comments on commit 3d852e3

Please sign in to comment.