Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[wasm][debugger] Detect initial status of pause on exceptions. #54040

Merged
merged 9 commits into from
Jun 25, 2021

Conversation

thaystg
Copy link
Member

@thaystg thaystg commented Jun 11, 2021

When the debugger is already "debugging" a page and the page is refreshed, the browser does not send the status of "Pause on Exceptions", with this implementation is possible to detect if the "pause on exceptions" is set to all or to uncaught.

@ghost
Copy link

ghost commented Jun 11, 2021

Tagging subscribers to this area: @thaystg
See info in area-owners.md if you want to be subscribed.

Issue Details

When the debugger is already "debugging" a page and the page is refreshed, the browser does not send the status of "Pause on Exceptions", with this implementation is possible to detect if the "pause on exceptions" is set to all or to uncaught.

Author: thaystg
Assignees: -
Labels:

area-Debugger-mono

Milestone: -

@thaystg thaystg added the arch-wasm WebAssembly architecture label Jun 11, 2021
@ghost
Copy link

ghost commented Jun 11, 2021

Tagging subscribers to 'arch-wasm': @lewing
See info in area-owners.md if you want to be subscribed.

Issue Details

When the debugger is already "debugging" a page and the page is refreshed, the browser does not send the status of "Pause on Exceptions", with this implementation is possible to detect if the "pause on exceptions" is set to all or to uncaught.

Author: thaystg
Assignees: -
Labels:

arch-wasm, area-Debugger-mono

Milestone: -

await SendMonoCommand(sessionId, new MonoCommands("globalThis.dotnetDebugger = true"), token);
Result res = await SendCommand(sessionId,
"Page.addScriptToEvaluateOnNewDocument",
JObject.FromObject(new { source = "globalThis.dotnetDebugger = true; delete navigator.constructor.prototype.webdriver" }),
JObject.FromObject(new { source = $"globalThis.dotnetDebugger = true; delete navigator.constructor.prototype.webdriver; {checkUncaughtExceptions} {checkCaughtExceptions}" }),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

doing it here is only going to work on new documents, would we be out of sync in the attach case still? does it work if you add it to the SendMonoCommand part above?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tested two cases: The page is already loaded and then I connect debugger, it passes here and I can get the information that is expected.
The page is reloaded with the debugger already connected, it also passes here and I can get the information.
I'll test adding it to the SendMonoCommand.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does not work if I add it to SendMonoCommand because it uses Runtime.Evaluate and Runtime.Evaluate does not call Debugger.Pause when an exception is threw.

@@ -120,8 +120,40 @@ protected override async Task<bool> AcceptEvent(SessionId sessionId, string meth
return true;
}

case "Runtime.exceptionThrown":
{
if (!GetContext(sessionId).IsRuntimeReady)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

might need to remove this check if you were trying the on attach version

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Attach is the case when the page is already loaded and then we connect the debugger? I tested it and it's working.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I only tested debugging on Chrome, I didn't test with VS or VSCode.

@thaystg thaystg requested a review from radical June 11, 2021 14:15
if (!GetContext(sessionId).IsRuntimeReady)
{
string exceptionError = args?["exceptionDetails"]?["exception"]?["value"]?.Value<string>();
if (exceptionError == "pause_on_uncaught")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add const fields for the two strings?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes

Comment on lines 1113 to 1116
if (context.PauseOnCaught && context.PauseOnUncaught)
await SendMonoCommand(sessionId, MonoCommands.SetPauseOnExceptions("all"), token);
if (context.PauseOnUncaught)
await SendMonoCommand(sessionId, MonoCommands.SetPauseOnExceptions("uncaught"), token);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Won't this send both commands when context.PauseOnCaught && context.PauseOnUncaught is true?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, you are right!

await SendMonoCommand(sessionId, new MonoCommands("globalThis.dotnetDebugger = true"), token);
Result res = await SendCommand(sessionId,
"Page.addScriptToEvaluateOnNewDocument",
JObject.FromObject(new { source = "globalThis.dotnetDebugger = true; delete navigator.constructor.prototype.webdriver" }),
JObject.FromObject(new { source = $"globalThis.dotnetDebugger = true; delete navigator.constructor.prototype.webdriver; {checkUncaughtExceptions} {checkCaughtExceptions}" }),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I understand the flow here.

  1. on attach, we throw two exceptions, so we can infer the pause-on-exceptions setting of the debugger;
  2. then I see two code paths:
    a. Runtime.exceptionThrown - where we do the inference, and set the value accordingly, in the proxy, and the app, and only when runtime is not ready
    b. Debugger.paused - where we resume, if it paused because of one of the above exceptions
  • so, what are the actual scenarios here?
  • And what does the flow of events look like, for them?

Also, it would be very useful to express that in tests too, but that might not be straight forward, so it can be in a follow up PR.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we attach to an already loaded page it will only pass on Runtime.exceptionThrown and this would print a message on console if I don't return true.
If we refresh a page with the debugger attached it will pass on Debugger.paused, and we run the resume, and it will also pass on Runtime.exceptionThrown that would print a message if I don't return true.
In both cases before send the runtime ready we have already got the configuration so we can set it to mono debugger.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh no, I tested again, when the page is already loaded and we attach the debugger we receive the "Debugger.setPauseOnExceptions" with the correct information.
When we refresh the page that we are debugging, then we will receive the Debugger.paused and the Runtime.exceptionThrown.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we attach to an already loaded page it will only pass on Runtime.exceptionThrown and this would print a message on console if I don't return true.

By "pass on foo", do you mean that foo's code path will be followed?

Question for scenario #1:

  1. attach to already loaded page
  2. we send a Page.addScriptToEvaluateOnNewDocument, with .. {checkUncaughtExceptions} {checkCaughtExceptions}
  3. and based on your last comment, we get Debugger.setPauseOnExceptions
  4. And then .. do we get Runtime.exceptionThrown? If yes, then for which case(caught/uncaught)?

If we refresh a page with the debugger attached it will pass on Debugger.paused, and we run the resume, and it will also pass on Runtime.exceptionThrown that would print a message if I don't return true.

  • Also, what happens for the {checkUncaughtExceptions} in the command that we send.

    • if the setting is uncaught, then debugger is paused, we get exceptionThrown, resume the debugger. Does the code then resume to the next line, and we execute checkCaughtExceptions`?
  • Is there any case where we might want/need to get the "current" setting from the app, and use that in the proxy?

Copy link
Member Author

@thaystg thaystg Jun 15, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed somethings in the PR.
For scenario #1:
We get Runtime.exceptionThrown in the case of the page already loaded for uncaught and we don't receive Debugger.Pause. Then we receive the Debugger.setPauseOnExceptions with the correct information.

For scenario #2:
If the setting is uncaught and we refresh the page we get:
Debugger.pause where we set the context status that will be send later to mono runtime. And the we receive a Runtime.exceptionThrown that will just return true to avoid printing the exception on the console.
If the setting is all and we refresh the page we get:
Debugger.pause where we set the context status PauseOnCaught to true then we resume, then we receive another Debugger.pause where we set the context status PauseOnUncaught to true then we resume. Then we receive Runtime.exceptionThrown for uncaught.
If while loading the page before the runtime is ready we change the status, we will receive a Debugger.setPauseOnExceptions with the right information, so I override the information that is in context for PauseOnCaught and PauseOnUncaught and when the runtime is ready I send the correct status.

We don't need the current setting from the app, the current setting should always be correct, because during the use of the debugger we receive the Debugger.setPauseOnExceptions with the correct information.

@lewing lewing self-requested a review June 15, 2021 22:23
@lewing
Copy link
Member

lewing commented Jun 15, 2021

One other issue to check is what happens if you disconnect the debug proxy

@lewing
Copy link
Member

lewing commented Jun 15, 2021

attempt to resolve #42326

@thaystg
Copy link
Member Author

thaystg commented Jun 15, 2021

One other issue to check is what happens if you disconnect the debug proxy

If I disconnect the debug proxy and reload the page I don't see any message and it doesn't stop in our "forced" throw.

I could not test the pause on "all" exceptions because if I enable the pause on caught exceptions and reload the page it will stop in a lot of exceptions other then the one that I inserted in AttachToTarget.
@thaystg
Copy link
Member Author

thaystg commented Jun 17, 2021

I could not test the pause on "all" exceptions because if I enable the pause on caught exceptions and reload the page it will stop in a lot of exceptions other then the one that I inserted in AttachToTarget.

@thaystg
Copy link
Member Author

thaystg commented Jun 17, 2021

I added a test to "all" option reloading the page. I don't like it too much but it really test the new functionality.

…nitial_config

* origin/main: (362 commits)
  [wasm][debugger] Reuse debugger-agent on wasm debugger (dotnet#52300)
  Put Crossgen2 in sync with dotnet#54235 (dotnet#54438)
  Move System.Object serialization to ObjectConverter (dotnet#54436)
  Move setting fHasVirtualStaticMethods out of sanity check section (dotnet#54574)
  [wasm] Compile .bc->.o in parallel, before passing to the linker (dotnet#54053)
  Change PathInternal.IsCaseSensitive to a constant (dotnet#54340)
  Make mono_polling_required a public symbol (dotnet#54592)
  Respect EventSource::IsSupported setting in more codepaths (dotnet#51977)
  Root ComActivator for hosting (dotnet#54524)
  Add ILLink annotations to S.D.Common related to DbConnectionStringBuilder (dotnet#54280)
  Fix finalizer issue with regions (dotnet#54550)
  Add support for multi-arch install locations (dotnet#53763)
  Update library testing docs page to reduce confusion (dotnet#54324)
  [FileStream] handle UNC and device paths (dotnet#54483)
  Update NetAnalyzers version (dotnet#54511)
  Added runtime dependency to fix the intermittent test failures (dotnet#54587)
  Disable failing System.Reflection.Tests.ModuleTests.GetMethods (dotnet#54564)
  [wasm] Move AOT builds from `runtime-staging` to `runtime` (dotnet#54577)
  Keep obj node for ArrayIndex. (dotnet#54584)
  Disable another failing MemoryCache test (dotnet#54578)
  ...
@ghost ghost locked as resolved and limited conversation to collaborators Jul 25, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
arch-wasm WebAssembly architecture area-Debugger-mono
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants