未验证 提交 4c992b1b 编写于 作者: T Thays Grazia 提交者: GitHub

[wasm][debugger] Implement support to symbolOptions from dap. (#79284)

* Draft to implement support to symbolOptions from dap.

* removing debugger.launch

* Adding tests and fix compilation error

* Adding test case.

* Fix test cases, and implement support to PDBChecksum used on nuget.org to get symbols.

* merge

* Fixing tests.

* Tests are timing out.

* Apply suggestions from code review
Co-authored-by: NLarry Ewing <lewing@microsoft.com>
Co-authored-by: NAnkit Jain <radical@gmail.com>

* adressing @radical comments.

* Addressing @radical comment.

* Addressing @radical comments.

* Apply suggestions from code review
Co-authored-by: NAnkit Jain <radical@gmail.com>

* Addressing @radical comments.

* Addressing @radical comments
Changing when the symbols from symbol server is loaded because it takes a long time to load, as there are a lot of assemblies loaded not found on symbol servers.

* use MicrosoftCodeAnalysisCSharpVersion for scripting package.

* Adding more tests as asked by @radical
Removing timeout change as @radical has split it into 2 files
Fixing test behavior, when justMyCode is disabled but the symbols are not loaded from symbol server.

* [wasm] some cleanup

- Don't call `UpdateSymbolStore` from `DebugStore..ctor` because that
gets called multiple times in `LoadStore`, but only once instance gets
used.
- Use an isolated symbol cache path per test

* remove debug spew

* Addressing radical comment.
Co-authored-by: NLarry Ewing <lewing@microsoft.com>
Co-authored-by: NAnkit Jain <radical@gmail.com>
上级 d6c2b438
......@@ -248,5 +248,9 @@
<runtimewinx64MicrosoftNETCoreRuntimeJITToolsVersion>1.0.0-alpha.1.23066.1</runtimewinx64MicrosoftNETCoreRuntimeJITToolsVersion>
<runtimeosx110arm64MicrosoftNETCoreRuntimeJITToolsVersion>1.0.0-alpha.1.23066.1</runtimeosx110arm64MicrosoftNETCoreRuntimeJITToolsVersion>
<runtimeosx1012x64MicrosoftNETCoreRuntimeJITToolsVersion>1.0.0-alpha.1.23066.1</runtimeosx1012x64MicrosoftNETCoreRuntimeJITToolsVersion>
<!-- BrowserDebugProxy libs -->
<MicrosoftExtensionsLoggingVersion>3.1.7</MicrosoftExtensionsLoggingVersion>
<MicrosoftSymbolStoreVersion>1.0.406601</MicrosoftSymbolStoreVersion>
</PropertyGroup>
</Project>
......@@ -975,10 +975,11 @@ mono_de_ss_update (SingleStepReq *req, MonoJitInfo *ji, SeqPoint *sp, void *tls,
if (minfo)
loc = mono_debug_method_lookup_location (minfo, sp->il_offset);
if (!loc) {
PRINT_DEBUG_MSG (1, "[%p] No line number info for il offset %x, continuing single stepping.\n", (gpointer) (gsize) mono_native_thread_id_get (), sp->il_offset);
if (!loc) { //we should not continue single stepping because the client side can have symbols loaded dynamically
PRINT_DEBUG_MSG (1, "[%p] No line number info for il offset %x, don't know if it's in the same line single stepping.\n", (gpointer) (gsize) mono_native_thread_id_get (), sp->il_offset);
req->last_method = method;
hit = FALSE;
req->last_line = -1;
return hit;
} else if (loc && method == req->last_method && loc->row == req->last_line) {
int nframes;
rt_callbacks.ss_calculate_framecount (tls, ctx, FALSE, NULL, &nframes);
......
......@@ -20,6 +20,8 @@
<_browserDebugHostFiles Include="$(ArtifactsDir)bin\BrowserDebugHost\$(TargetArchitecture)\$(Configuration)\Newtonsoft.Json.dll" />
<_browserDebugHostFiles Include="$(ArtifactsDir)bin\BrowserDebugHost\$(TargetArchitecture)\$(Configuration)\Microsoft.CodeAnalysis.CSharp.Scripting.dll" />
<_browserDebugHostFiles Include="$(ArtifactsDir)bin\BrowserDebugHost\$(TargetArchitecture)\$(Configuration)\Microsoft.CodeAnalysis.Scripting.dll" />
<_browserDebugHostFiles Include="$(ArtifactsDir)bin\BrowserDebugHost\$(TargetArchitecture)\$(Configuration)\Microsoft.SymbolStore.dll" />
<_browserDebugHostFiles Include="$(ArtifactsDir)bin\BrowserDebugHost\$(TargetArchitecture)\$(Configuration)\Microsoft.FileFormats.dll" />
<PackageFile Include="@(_browserDebugHostFiles)" TargetPath="tools\$(NetCoreAppCurrent)\" />
</ItemGroup>
......
......@@ -203,8 +203,7 @@ async Task ConnectProxy(HttpContext context)
try
{
var loggerFactory = context.RequestServices.GetService<ILoggerFactory>();
context.Request.Query.TryGetValue("urlSymbolServer", out StringValues urlSymbolServerList);
var proxy = new DebuggerProxy(loggerFactory, urlSymbolServerList.ToList(), runtimeId, options: options);
var proxy = new DebuggerProxy(loggerFactory, runtimeId, options: options);
System.Net.WebSockets.WebSocket ideSocket = await context.WebSockets.AcceptWebSocketAsync();
......
......@@ -8,10 +8,11 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="3.7.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="3.1.7" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="$(MicrosoftCodeAnalysisCSharpVersion)" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="$(MicrosoftExtensionsLoggingVersion)" />
<PackageReference Include="Microsoft.SymbolStore" Version="$(MicrosoftSymbolStoreVersion)" />
<PackageReference Include="Newtonsoft.Json" Version="$(NewtonsoftJsonVersion)" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.7.0" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="$(MicrosoftCodeAnalysisCSharpVersion)" />
</ItemGroup>
<ItemGroup>
......
......@@ -18,10 +18,10 @@ public class DebuggerProxy : DebuggerProxyBase
{
internal MonoProxy MonoProxy { get; }
public DebuggerProxy(ILoggerFactory loggerFactory, IList<string> urlSymbolServerList, int runtimeId = 0, string loggerId = "", ProxyOptions options = null)
public DebuggerProxy(ILoggerFactory loggerFactory, int runtimeId = 0, string loggerId = "", ProxyOptions options = null)
{
string suffix = loggerId.Length > 0 ? $"-{loggerId}" : string.Empty;
MonoProxy = new MonoProxy(loggerFactory.CreateLogger($"DevToolsProxy{suffix}"), urlSymbolServerList, runtimeId, loggerId, options);
MonoProxy = new MonoProxy(loggerFactory.CreateLogger($"DevToolsProxy{suffix}"), runtimeId, loggerId, options);
}
public Task Run(Uri browserUri, WebSocket ideSocket, CancellationTokenSource cts)
......
......@@ -58,7 +58,7 @@ public override void Visit(SyntaxNode node)
if (visitCount == 0)
{
if (node is MemberAccessExpressionSyntax maes
&& node.Kind() == SyntaxKind.SimpleMemberAccessExpression
&& node.IsKind(SyntaxKind.SimpleMemberAccessExpression)
&& !(node.Parent is MemberAccessExpressionSyntax)
&& !(node.Parent is InvocationExpressionSyntax)
&& !(node.Parent is ElementAccessExpressionSyntax))
......@@ -401,7 +401,7 @@ private static async Task<IList<JObject>> ResolveElementAccess(ExpressionSyntaxR
// this fails with `"a)"`
// because the code becomes: return (a));
// and the returned expression from GetExpressionFromSyntaxTree is `a`!
if (expressionTree.Kind() == SyntaxKind.IdentifierName || expressionTree.Kind() == SyntaxKind.ThisExpression)
if (expressionTree.IsKind(SyntaxKind.IdentifierName) || expressionTree.IsKind(SyntaxKind.ThisExpression))
{
string varName = expressionTree.ToString();
JObject value = await resolver.Resolve(varName, token);
......@@ -416,7 +416,7 @@ private static async Task<IList<JObject>> ResolveElementAccess(ExpressionSyntaxR
syntaxTree = replacer.ReplaceVars(syntaxTree, memberAccessValues, identifierValues, null, null);
// eg. "this.dateTime", " dateTime.TimeOfDay"
if (expressionTree.Kind() == SyntaxKind.SimpleMemberAccessExpression && replacer.memberAccesses.Count == 1)
if (expressionTree.IsKind(SyntaxKind.SimpleMemberAccessExpression) && replacer.memberAccesses.Count == 1)
{
return memberAccessValues[0];
}
......
......@@ -17,7 +17,7 @@ namespace Microsoft.WebAssembly.Diagnostics;
internal sealed class FirefoxMonoProxy : MonoProxy
{
public FirefoxMonoProxy(ILogger logger, string loggerId = null, ProxyOptions options = null) : base(logger, null, loggerId: loggerId, options: options)
public FirefoxMonoProxy(ILogger logger, string loggerId = null, ProxyOptions options = null) : base(logger, loggerId: loggerId, options: options)
{
}
......
......@@ -18,7 +18,8 @@ namespace Microsoft.WebAssembly.Diagnostics
{
internal class MonoProxy : DevToolsProxy
{
private IList<string> urlSymbolServerList;
internal List<string> UrlSymbolServerList { get; private set; }
internal string CachePathSymbolServer { get; private set; }
private HashSet<SessionId> sessions = new HashSet<SessionId>();
private static readonly string[] s_executionContextIndependentCDPCommandNames = { "DotnetDebugger.setDebuggerProperty", "DotnetDebugger.runTests" };
protected Dictionary<SessionId, ExecutionContext> contexts = new Dictionary<SessionId, ExecutionContext>();
......@@ -32,9 +33,9 @@ internal class MonoProxy : DevToolsProxy
protected readonly ProxyOptions _options;
public MonoProxy(ILogger logger, IList<string> urlSymbolServerList, int runtimeId = 0, string loggerId = "", ProxyOptions options = null) : base(logger, loggerId)
public MonoProxy(ILogger logger, int runtimeId = 0, string loggerId = "", ProxyOptions options = null) : base(logger, loggerId)
{
this.urlSymbolServerList = urlSymbolServerList ?? new List<string>();
UrlSymbolServerList = new List<string>();
RuntimeId = runtimeId;
_options = options;
_defaultPauseOnExceptions = PauseOnExceptionsKind.Unset;
......@@ -76,10 +77,10 @@ internal void SendLog(SessionId sessionId, string message, CancellationToken tok
{
type,
args = new JArray(JObject.FromObject(new
{
type = "string",
value = message,
})),
{
type = "string",
value = message,
})),
executionContextId = context.Id
});
SendEvent(sessionId, "Runtime.consoleAPICalled", o, token);
......@@ -143,7 +144,7 @@ protected override async Task<bool> AcceptEvent(SessionId sessionId, JObject par
bool? is_default = aux_data["isDefault"]?.Value<bool>();
if (is_default == true)
{
await OnDefaultContext(sessionId, new ExecutionContext(new MonoSDBHelper (this, logger, sessionId), id, aux_data, _defaultPauseOnExceptions), token);
await OnDefaultContext(sessionId, new ExecutionContext(new MonoSDBHelper(this, logger, sessionId), id, aux_data, _defaultPauseOnExceptions), token);
}
}
return true;
......@@ -170,6 +171,8 @@ protected override async Task<bool> AcceptEvent(SessionId sessionId, JObject par
{
await RuntimeReady(sessionId, token);
await SendResume(sessionId, token);
if (!JustMyCode)
await ReloadSymbolsFromSymbolServer(sessionId, GetContext(sessionId), token);
return true;
}
case "mono_wasm_fire_debugger_agent_message":
......@@ -240,7 +243,7 @@ protected override async Task<bool> AcceptCommand(MessageId id, JObject parms, C
if (!contexts.TryGetValue(id, out ExecutionContext context) && !s_executionContextIndependentCDPCommandNames.Contains(method))
{
if (method == "Debugger.setPauseOnExceptions")
if (method == "Debugger.setPauseOnExceptions")
{
string state = args["state"].Value<string>();
var pauseOnException = GetPauseOnExceptionsStatusFromString(state);
......@@ -505,11 +508,11 @@ protected override async Task<bool> AcceptCommand(MessageId id, JObject parms, C
switch (property.Key)
{
case "JustMyCodeStepping":
SetJustMyCode(id, (bool) property.Value, token);
break;
await SetJustMyCode(id, (bool)property.Value, context, token);
break;
default:
logger.LogDebug($"DotnetDebugger.setDebuggerProperty failed for {property.Key} with value {property.Value}");
break;
break;
}
}
return true;
......@@ -534,11 +537,21 @@ protected override async Task<bool> AcceptCommand(MessageId id, JObject parms, C
SendResponse(id, Result.Err("ApplyUpdate failed."), token);
return true;
}
case "DotnetDebugger.addSymbolServerUrl":
case "DotnetDebugger.setSymbolOptions":
{
string url = args["url"]?.Value<string>();
if (!string.IsNullOrEmpty(url) && !urlSymbolServerList.Contains(url))
urlSymbolServerList.Add(url);
SendResponse(id, Result.OkFromObject(new { }), token);
CachePathSymbolServer = args["symbolOptions"]?["cachePath"]?.Value<string>();
var urls = args["symbolOptions"]?["searchPaths"]?.Value<JArray>();
if (urls == null)
return true;
UrlSymbolServerList.Clear();
UrlSymbolServerList.AddRange(urls.Values<string>());
if (!JustMyCode)
{
if (!await IsRuntimeAlreadyReadyAlready(id, token))
return true;
return await ReloadSymbolsFromSymbolServer(id, context, token);
}
return true;
}
case "DotnetDebugger.getMethodLocation":
......@@ -578,6 +591,14 @@ protected override async Task<bool> AcceptCommand(MessageId id, JObject parms, C
return method.StartsWith("DotnetDebugger.", StringComparison.OrdinalIgnoreCase);
}
private async Task<bool> ReloadSymbolsFromSymbolServer(SessionId id, ExecutionContext context, CancellationToken token)
{
DebugStore store = await LoadStore(id, true, token);
store.UpdateSymbolStore(UrlSymbolServerList, CachePathSymbolServer);
await store.ReloadAllPDBsFromSymbolServersAndSendSources(this, id, context, token);
return true;
}
private async Task<bool> ApplyUpdates(MessageId id, JObject args, CancellationToken token)
{
var context = GetContext(id);
......@@ -590,8 +611,14 @@ private async Task<bool> ApplyUpdates(MessageId id, JObject args, CancellationTo
return applyUpdates;
}
private void SetJustMyCode(MessageId id, bool isEnabled, CancellationToken token)
private async Task SetJustMyCode(MessageId id, bool isEnabled, ExecutionContext context, CancellationToken token)
{
if (JustMyCode != isEnabled && isEnabled == false)
{
JustMyCode = isEnabled;
if (await IsRuntimeAlreadyReadyAlready(id, token))
await ReloadSymbolsFromSymbolServer(id, context, token);
}
JustMyCode = isEnabled;
SendResponse(id, Result.OkFromObject(new { justMyCodeEnabled = JustMyCode }), token);
}
......@@ -886,7 +913,7 @@ private async Task<bool> SendBreakpointsOfMethodUpdated(SessionId sessionId, Exe
return true;
}
protected virtual async Task<bool> ShouldSkipMethod(SessionId sessionId, ExecutionContext context, EventKind event_kind, int j, MethodInfoWithDebugInformation method, CancellationToken token)
protected virtual async Task<bool> ShouldSkipMethod(SessionId sessionId, ExecutionContext context, EventKind event_kind, int frameNumber, MethodInfoWithDebugInformation method, CancellationToken token)
{
var shouldReturn = await SkipMethod(
isSkippable: context.IsSkippingHiddenMethod,
......@@ -904,7 +931,10 @@ protected virtual async Task<bool> ShouldSkipMethod(SessionId sessionId, Executi
if (shouldReturn)
return true;
if (j == 0 && method?.Info.DebuggerAttrInfo.DoAttributesAffectCallStack(JustMyCode) == true)
if (frameNumber != 0)
return false;
if (method?.Info?.DebuggerAttrInfo?.DoAttributesAffectCallStack(JustMyCode) == true)
{
if (method.Info.DebuggerAttrInfo.ShouldStepOut(event_kind))
{
......@@ -929,6 +959,16 @@ protected virtual async Task<bool> ShouldSkipMethod(SessionId sessionId, Executi
context.IsResumedAfterBp = true;
}
}
else
{
if (!JustMyCode && method?.Info?.DebuggerAttrInfo?.HasNonUserCode == true && !method.Info.hasDebugInformation)
{
if (event_kind == EventKind.Step)
context.IsSkippingHiddenMethod = true;
if (await SkipMethod(isSkippable: true, shouldBeSkipped: true, StepKind.Out))
return true;
}
}
return false;
async Task<bool> SkipMethod(bool isSkippable, bool shouldBeSkipped, StepKind stepKind)
{
......@@ -1132,49 +1172,6 @@ internal async Task<bool> OnReceiveDebuggerAgentEvent(SessionId sessionId, JObje
return false;
}
internal async Task<MethodInfo> LoadSymbolsOnDemand(AssemblyInfo asm, int method_token, SessionId sessionId, CancellationToken token)
{
ExecutionContext context = GetContext(sessionId);
if (urlSymbolServerList.Count == 0)
return null;
if (asm.TriedToLoadSymbolsOnDemand || !asm.CodeViewInformationAvailable)
return null;
asm.TriedToLoadSymbolsOnDemand = true;
var pdbName = Path.GetFileName(asm.PdbName);
foreach (string urlSymbolServer in urlSymbolServerList)
{
string downloadURL = $"{urlSymbolServer}/{pdbName}/{asm.PdbGuid.ToString("N").ToUpperInvariant() + asm.PdbAge}/{pdbName}";
try
{
using HttpResponseMessage response = await HttpClient.GetAsync(downloadURL, token);
if (!response.IsSuccessStatusCode)
{
Log("info", $"Unable to download symbols on demand url:{downloadURL} assembly: {asm.Name}");
continue;
}
using Stream streamToReadFrom = await response.Content.ReadAsStreamAsync(token);
asm.UpdatePdbInformation(streamToReadFrom);
foreach (SourceFile source in asm.Sources)
{
var scriptSource = JObject.FromObject(source.ToScriptSource(context.Id, context.AuxData));
await SendEvent(sessionId, "Debugger.scriptParsed", scriptSource, token);
}
return asm.GetMethodByToken(method_token);
}
catch (Exception e)
{
Log("info", $"Unable to load symbols on demand exception: {e} url:{downloadURL} assembly: {asm.Name}");
}
break;
}
Log("info", $"Unable to load symbols on demand assembly: {asm.Name}");
return null;
}
protected void OnDefaultContextUpdate(SessionId sessionId, ExecutionContext context)
{
if (UpdateContext(sessionId, context, out ExecutionContext previousContext))
......@@ -1575,7 +1572,7 @@ protected async Task<DebugStore> RuntimeReady(SessionId sessionId, CancellationT
{
var comparer = new SourceLocation.LocationComparer();
// if column is specified the frontend wants the exact matches
// and will clear the bp if it isn't close enoug
// and will clear the bp if it isn't close enough
var bpLocations = store.FindBreakpointLocations(req, ifNoneFoundThenFindNext);
IEnumerable<IGrouping<SourceId, SourceLocation>> locations = bpLocations.Distinct(comparer)
.OrderBy(l => l.Column)
......
......@@ -890,19 +890,6 @@ public async Task<MethodInfoWithDebugInformation> GetMethodInfo(int methodId, Ca
var method = asm.GetMethodByToken(methodToken);
if (method == null && !asm.HasSymbols)
{
try
{
method = await proxy.LoadSymbolsOnDemand(asm, methodToken, sessionId, token);
}
catch (Exception e)
{
logger.LogDebug($"Unable to find method token: {methodToken} assembly name: {asm.Name} exception: {e}");
return null;
}
}
string methodName = await GetMethodName(methodId, token);
//get information from runtime
method ??= await CreateMethodInfoFromRuntimeInformation(asm, methodId, methodName, methodToken, token);
......
......@@ -83,7 +83,7 @@ public ChromeProvider(string id, ILogger logger) : base(id, logger)
_logger.LogInformation($"{messagePrefix} launching proxy for {con_str}");
_debuggerProxy = new DebuggerProxy(loggerFactory, null, loggerId: Id);
_debuggerProxy = new DebuggerProxy(loggerFactory, loggerId: Id);
TestHarnessProxy.RegisterNewProxy(Id, _debuggerProxy);
var browserUri = new Uri(con_str);
WebSocket? ideSocket = await context.WebSockets.AcceptWebSocketAsync().ConfigureAwait(false);
......
......@@ -54,6 +54,7 @@ public static WasmHost RunningOn
private const int DefaultTestTimeoutMs = 1 * 60 * 1000;
protected TimeSpan TestTimeout = TimeSpan.FromMilliseconds(DefaultTestTimeoutMs);
protected ITestOutputHelper _testOutput;
protected readonly TestEnvironment _env;
static string s_debuggerTestAppPath;
static int s_idCounter = -1;
......@@ -117,8 +118,16 @@ public static string TestLogPath
}
}
public static string TempPath => Path.Combine(Path.GetTempPath(), "dbg-tests-tmp");
static DebuggerTestBase()
{
if (Directory.Exists(TempPath))
Directory.Delete(TempPath, recursive: true);
}
public DebuggerTestBase(ITestOutputHelper testOutput, string driver = "debugger-driver.html")
{
_env = new TestEnvironment(testOutput);
_testOutput = testOutput;
Id = Interlocked.Increment(ref s_idCounter);
// the debugger is working in locale of the debugged application. For example Datetime.ToString()
......@@ -151,7 +160,11 @@ public virtual async Task InitializeAsync()
await insp.OpenSessionAsync(fn, TestTimeout);
}
public virtual async Task DisposeAsync() => await insp.ShutdownAsync().ConfigureAwait(false);
public virtual async Task DisposeAsync()
{
await insp.ShutdownAsync().ConfigureAwait(false);
_env.Dispose();
}
public Task Ready() => startTask;
......@@ -1508,6 +1521,13 @@ internal async Task SetJustMyCode(bool enabled)
Assert.Equal(res.Value["justMyCodeEnabled"], enabled);
}
internal async Task SetSymbolOptions(JObject param)
{
var res = await cli.SendCommand("DotnetDebugger.setSymbolOptions", param, token);
Assert.True(res.IsOk);
}
internal async Task CheckEvaluateFail(string id, params (string expression, string message)[] args)
{
foreach (var arg in args)
......
......@@ -11,4 +11,6 @@ internal static class EnvironmentVariables
{
public static readonly string? DebuggerTestPath = Environment.GetEnvironmentVariable("DEBUGGER_TEST_PATH");
public static readonly string? TestLogPath = Environment.GetEnvironmentVariable("TEST_LOG_PATH");
public static readonly bool SkipCleanup = Environment.GetEnvironmentVariable("SKIP_CLEANUP") == "1" ||
Environment.GetEnvironmentVariable("SKIP_CLEANUP") == "true";
}
......@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Newtonsoft.Json.Linq;
......@@ -877,7 +878,7 @@ public async Task BreakpointOnHiddenLineShouldStopAtEarliestNextAvailableLineAsy
"dotnet://debugger-test.dll/debugger-async-test.cs", line_pause, column_pause,
$"DebuggerTests.AsyncTests.ContinueWithTests.{method_name}");
}
[ConditionalTheory(nameof(RunningOnChrome))]
[InlineData(112, 16, 114, 16, "HiddenLinesInAnAsyncBlock")]
[InlineData(130, 16, 133, 16, "HiddenLinesJustBeforeANestedAsyncBlock")]
......@@ -1037,5 +1038,182 @@ public async Task DebuggerHiddenIgnoreStepUserBreakpoint(string steppingFunction
step_into2["callFrames"][0]["location"]["lineNumber"].Value<int>()
);
}
[ConditionalTheory(nameof(RunningOnChrome))]
[InlineData(true)]
[InlineData(false)]
public async Task SteppingIntoLibrarySymbolsLoadedFromSymbolServer(bool justMyCode)
{
string cachePath = _env.CreateTempDirectory("symbols-cache");
_testOutput.WriteLine($"** Using cache path: {cachePath}");
var searchPaths = new JArray
{
"https://symbols.nuget.org/download/symbols"
};
var waitForScript = WaitForScriptParsedEventsAsync(new string [] { "JArray.cs" });
var symbolOptions = JObject.FromObject(new { symbolOptions = JObject.FromObject(new { cachePath, searchPaths })});
await SetJustMyCode(justMyCode);
await SetSymbolOptions(symbolOptions);
await EvaluateAndCheck(
"window.setTimeout(function() { invoke_static_method ('[debugger-test] TestLoadSymbols:Run'); }, 1);",
"dotnet://debugger-test.dll/debugger-test.cs", 1572, 8,
"TestLoadSymbols.Run"
);
if (!justMyCode)
await waitForScript;
await StepAndCheck(StepKind.Into, justMyCode ? "dotnet://debugger-test.dll/debugger-test.cs" : "dotnet://Newtonsoft.Json.dll/JArray.cs", justMyCode ? 1575 : 350, justMyCode ? 8 : 12, justMyCode ? "TestLoadSymbols.Run" : "Newtonsoft.Json.Linq.JArray.Add",
locals_fn: async (locals) =>
{
if (!justMyCode)
await CheckObject(locals, "this", "Newtonsoft.Json.Linq.JArray", description: "[]");
else
await CheckObject(locals, "array", "Newtonsoft.Json.Linq.JArray", description: "[\n \"Manual text\"\n]");
}, times: 2
);
}
[ConditionalFact(nameof(RunningOnChrome))]
public async Task SteppingIntoLibraryWithoutSymbolsAndStepAgainAfterLoadSymbols()
{
string cachePath = _env.CreateTempDirectory("symbols-cache");
_testOutput.WriteLine($"** Using cache path: {cachePath}");
var searchPaths = new JArray
{
"https://symbols.nuget.org/download/symbols"
};
var waitForScript = WaitForScriptParsedEventsAsync(new string [] { "JArray.cs" });
var symbolOptions = JObject.FromObject(new { symbolOptions = JObject.FromObject(new { cachePath, searchPaths })});
await SetJustMyCode(false);
await EvaluateAndCheck(
"window.setTimeout(function() { invoke_static_method ('[debugger-test] TestLoadSymbols:Run'); invoke_static_method ('[debugger-test] TestLoadSymbols:Run'); }, 1);",
"dotnet://debugger-test.dll/debugger-test.cs", 1572, 8,
"TestLoadSymbols.Run"
);
await StepAndCheck(StepKind.Into, "dotnet://debugger-test.dll/debugger-test.cs", 1575, 8, "TestLoadSymbols.Run",
locals_fn: async (locals) =>
{
await CheckObject(locals, "array", "Newtonsoft.Json.Linq.JArray", description: "[\n \"Manual text\"\n]");
}, times: 2
);
await SetSymbolOptions(symbolOptions);
await waitForScript;
await SendCommandAndCheck(null, "Debugger.resume",
"dotnet://debugger-test.dll/debugger-test.cs", 1572, 8,
"TestLoadSymbols.Run"
);
await StepAndCheck(StepKind.Into, "dotnet://Newtonsoft.Json.dll/JArray.cs", 350, 12, "Newtonsoft.Json.Linq.JArray.Add",
locals_fn: async (locals) =>
{
await CheckObject(locals, "this", "Newtonsoft.Json.Linq.JArray", description: "[]");
}, times: 2
);
}
[ConditionalFact(nameof(RunningOnChrome))]
public async Task SteppingIntoLibrarySymbolsLoadedFromSymbolServerAddOtherSymbolServerAndStepAgain()
{
string cachePath = _env.CreateTempDirectory("symbols-cache");
_testOutput.WriteLine($"** Using cache path: {cachePath}");
var searchPaths = new JArray
{
"https://symbols.nuget.org/download/symbols"
};
var waitForScript = WaitForScriptParsedEventsAsync(new string [] { "JArray.cs" });
var symbolOptions = JObject.FromObject(new { symbolOptions = JObject.FromObject(new { cachePath, searchPaths })});
await SetJustMyCode(false);
await SetSymbolOptions(symbolOptions);
await EvaluateAndCheck(
"window.setTimeout(function() { invoke_static_method ('[debugger-test] TestLoadSymbols:Run'); invoke_static_method ('[debugger-test] TestLoadSymbols:Run'); }, 1);",
"dotnet://debugger-test.dll/debugger-test.cs", 1572, 8,
"TestLoadSymbols.Run"
);
await waitForScript;
await StepAndCheck(StepKind.Into, "dotnet://Newtonsoft.Json.dll/JArray.cs", 350, 12, "Newtonsoft.Json.Linq.JArray.Add",
locals_fn: async (locals) =>
{
await CheckObject(locals, "this", "Newtonsoft.Json.Linq.JArray", description: "[]");
}, times: 2
);
searchPaths.Add("https://msdl.microsoft.com/download/symbols");
symbolOptions = JObject.FromObject(new { symbolOptions = JObject.FromObject(new { cachePath, searchPaths })});
await SetSymbolOptions(symbolOptions);
await SendCommandAndCheck(null, "Debugger.resume",
"dotnet://debugger-test.dll/debugger-test.cs", 1572, 8,
"TestLoadSymbols.Run"
);
await StepAndCheck(StepKind.Into, "dotnet://Newtonsoft.Json.dll/JArray.cs", 350, 12, "Newtonsoft.Json.Linq.JArray.Add",
locals_fn: async (locals) =>
{
await CheckObject(locals, "this", "Newtonsoft.Json.Linq.JArray", description: "[]");
}, times: 2
);
}
[ConditionalTheory(nameof(RunningOnChrome))]
[InlineData("https://symbols.nuget.org/download/symbols", "")]
// Symbols are already loaded, so setting urls = [] won't affect it
[InlineData]
[InlineData("", "https://microsoft.com/non-existant/symbols")]
public async Task SteppingIntoLibrarySymbolsLoadedFromSymbolServerRemoveSymbolServerAndStepAgain(params string[] secondServers)
{
string cachePath = _env.CreateTempDirectory("symbols-cache");
_testOutput.WriteLine($"Using cachePath: {cachePath}");
var searchPaths = new JArray
{
"https://symbols.nuget.org/download/symbols",
"https://msdl.microsoft.com/download/bad-non-existant",
"https://msdl.microsoft.com/download/symbols"
};
var waitForScript = WaitForScriptParsedEventsAsync(new string [] { "JArray.cs" });
var symbolOptions = JObject.FromObject(new { symbolOptions = JObject.FromObject(new { cachePath, searchPaths })});
await SetJustMyCode(false);
await SetSymbolOptions(symbolOptions);
await EvaluateAndCheck(
"window.setTimeout(function() { invoke_static_method ('[debugger-test] TestLoadSymbols:Run'); invoke_static_method ('[debugger-test] TestLoadSymbols:Run'); }, 1);",
"dotnet://debugger-test.dll/debugger-test.cs", 1572, 8,
"TestLoadSymbols.Run"
);
await waitForScript;
await StepAndCheck(StepKind.Into, "dotnet://Newtonsoft.Json.dll/JArray.cs", 350, 12, "Newtonsoft.Json.Linq.JArray.Add",
locals_fn: async (locals) =>
{
await CheckObject(locals, "this", "Newtonsoft.Json.Linq.JArray", description: "[]");
}, times: 2
);
searchPaths.Clear();
foreach (string secondServer in secondServers)
searchPaths.Add(secondServer);
symbolOptions = JObject.FromObject(new { symbolOptions = JObject.FromObject(new { cachePath, searchPaths })});
await SetSymbolOptions(symbolOptions);
await SendCommandAndCheck(null, "Debugger.resume",
"dotnet://debugger-test.dll/debugger-test.cs", 1572, 8,
"TestLoadSymbols.Run"
);
await StepAndCheck(StepKind.Into, "dotnet://Newtonsoft.Json.dll/JArray.cs", 350, 12, "Newtonsoft.Json.Linq.JArray.Add",
locals_fn: async (locals) =>
{
await CheckObject(locals, "this", "Newtonsoft.Json.Linq.JArray", description: "[]");
}, times: 2
);
}
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.IO;
using System.Linq;
using Xunit.Abstractions;
namespace DebuggerTests;
public class TestEnvironment : IDisposable
{
private bool _disposed;
private readonly string _tempPath;
private readonly ITestOutputHelper _testOutput;
public TestEnvironment(ITestOutputHelper testOutput)
{
_testOutput = testOutput;
_tempPath = Path.Combine(DebuggerTestBase.TempPath, Guid.NewGuid().ToString());
if (Directory.Exists(_tempPath))
Directory.Delete(_tempPath, recursive: true);
Directory.CreateDirectory(_tempPath);
}
public void Dispose()
{
if (_disposed || EnvironmentVariables.SkipCleanup)
return;
Directory.Delete(_tempPath, recursive: true);
_disposed = true;
}
public string CreateTempDirectory(string relativeDir, params string[] relativePathParts)
{
string newPath = Path.Combine(_tempPath, relativeDir, Path.Combine(relativePathParts));
Directory.CreateDirectory(newPath);
return newPath;
}
}
......@@ -1561,4 +1561,18 @@ public static void Run()
var n = new ToStringOverridenN();
System.Diagnostics.Debugger.Break();
}
}
\ No newline at end of file
}
public class TestLoadSymbols
{
public static void Run()
{
var array = new Newtonsoft.Json.Linq.JArray();
var text = new Newtonsoft.Json.Linq.JValue("Manual text");
var date = new Newtonsoft.Json.Linq.JValue(new DateTime(2000, 5, 23));
System.Diagnostics.Debugger.Break();
array.Add(text);
array.Add(date);
}
}
......@@ -8,7 +8,12 @@
<WasmGenerateAppBundle>true</WasmGenerateAppBundle>
<OutputType>library</OutputType>
<WasmEmitSymbolMap>true</WasmEmitSymbolMap>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup>
<ItemGroup>
<!-- keep this version to make sure it will pause in the expected line -->
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" />
</ItemGroup>
<ItemGroup>
<WasmExtraFilesToDeploy Include="debugger-driver.html" />
......@@ -66,6 +71,7 @@
<WasmAssembliesToBundle Include="$(OutDir)\debugger-test-with-non-user-code-class.dll" />
<WasmAssembliesToBundle Condition="!$([MSBuild]::IsOSPlatform('windows'))" Include="$(OutDir)\debugger-test-with-colon-in-source-name.dll" />
<WasmAssembliesToBundle Include="$(OutDir)\debugger-test-vb.dll" />
<WasmAssembliesToBundle Include="$(OutDir)\Newtonsoft.Json.dll" />
<WasmAssembliesToBundle Include="$(MicrosoftNetCoreAppRuntimePackRidDir)\lib\$(NetCoreappCurrent)\System.Runtime.InteropServices.JavaScript.dll" />
<!-- Assemblies only dynamically loaded -->
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册