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

Throw in async function after await is not caught by debugger #4630

Open
fatcerberus opened this issue Feb 1, 2018 · 12 comments
Open

Throw in async function after await is not caught by debugger #4630

fatcerberus opened this issue Feb 1, 2018 · 12 comments

Comments

@fatcerberus
Copy link
Contributor

fatcerberus commented Feb 1, 2018

While debugging is enabled, if one throws an error inside of an async function:

async function f()
{
    throw new Error("*munch*");
}
f();

Normally, such an error would be intercepted and trigger a breakpoint (JsDiagDebugEventRuntimeException):

D:\temp>ssj -r error.js
SSj X.X.X Sphere JavaScript debugger (x64)
the powerful symbolic JS debugger for Sphere
(c) 2015-2018 Fat Cerberus

starting 'D:/temp/error.js'... OK.
connecting to 127.0.0.1:1208... OK.
establishing communication... OK.
downloading game information... OK.
    engine: miniSphere X.X.X
    title:  error.js
    author: Author Unknown

uncaught: Error: *munch*
   at f (@/error.js:4:2)
   at Generator.prototype.next (native code)
   at Global code (@/error.js:6:1)
-> # 0: f(), at @/error.js:4
4       throw new Error("*munch*");

@/error.js:4 f()
(ssj) l
      1 async function f()
      2 {
      3         //await null;
->    4         throw new Error("*munch*");
      5 }
      6 f();

However, if one were to uncomment the await in the code above, no breakpoint will be triggered and the promise is just silently rejected:

D:\temp>ssj -r error.js
SSj X.X.X Sphere JavaScript debugger (x64)
the powerful symbolic JS debugger for Sphere
(c) 2015-2018 Fat Cerberus

starting 'D:/temp/error.js'... OK.
connecting to 127.0.0.1:1208... OK.
establishing communication... OK.
downloading game information... OK.
    engine: miniSphere X.X.X
    title:  error.js
    author: Author Unknown

SSj/Ki debug session disconnected normally.
the SSj debugger has been detached.

Update (6/26/2018):
There are multiple work items here & I wanted to break them out independently. Will leave this open to track getting this to work w/ async/await. The other issues are:

RE async/await, it looks like we'll need to do some work to update byte-code-generation & possibly jit code so that we understand syntactic constructs that result in a "handled" async functions.

@fatcerberus fatcerberus changed the title Error thrown in async function after an await is not caught by debugger Throw in async function after await is not caught by debugger Feb 1, 2018
@fatcerberus
Copy link
Contributor Author

I've found that if I enable first-chance exceptions, I will get called back for a throw after an await but in that case uncaught is set to false, i.e. the Chakra debugger erroneously considers the error to be handled. I'm trying to look into the cause of this but I haven't turned up any leads yet.

@fatcerberus
Copy link
Contributor Author

Nevermind, I was wrong - the error after the await doesn't generate a first-chance exception either.

@akroshg
Copy link
Contributor

akroshg commented Feb 8, 2018

@fatcerberus the throw new Error("*munch*"); will not be executed unless you call the .then(...
on ch.exe you see the first chance exception will be generated when you do

/**exception(all):stack();**/

async function f()
{
    await null;
    throw new Error("*munch*");
}
f().then( function (result) {
    console.log('here 1 ' + result);
},
function (error) {
    console.log('here 2 ' + error);
});

same is true if you debug in the Edge using F12.

@fatcerberus
Copy link
Contributor Author

@akroshg That doesn’t seem right: #4608 implements uncaught rejection notifications and if what you say is true that means we won’t get a rejected promise notification for this code. But my own experience has proven the opposite.

@akroshg
Copy link
Contributor

akroshg commented Feb 8, 2018

I am not sure about #4608, but what I meant was it generates the first chance exception. Is that not what you see?

@fatcerberus
Copy link
Contributor Author

You say the throw won’t be executed until someone adds a .then() to the promise, which doesn’t make sense since in that case #4608 wouldn’t work (it does).

@akroshg
Copy link
Contributor

akroshg commented Feb 8, 2018

hmm, I must be mistaken then.

@rhuanjl
Copy link
Collaborator

rhuanjl commented Feb 8, 2018

Comparing this with HostPromiseRejectionTracker (#4608) is a bit of a misnomer. The detection mechanisms are totally different.

With HostPromiseRejectionTracker the trigger is whenever a promise is rejected to check if it's got a handler or not - to be honest I have no idea what the trigger the debugger uses is.

The above case:

async function f()
{
  await null;
  throw new Error("*munch*");
}
f();

Roughly de-sugars to:

function f()
{
  var p = Promise.Resolve(null);
  var prom;
  p.then(()=>{
    try{
      throw new Error("*munch*");//
    }
    catch(e){
      prom = Promise.Reject(e);//-> rejected promise with no handler
      // -> triggers HostPromiseRejectionTracker
    }
  }
  return prom;
}
f();

(I note that there should be 1 more try catch block and at least one more throwaway promise to write that fully but it just gets unnecessarily messy)

The throw definitely fires - but the debugger does not flag for it - you can catch and log the error if wanted of course.

@fatcerberus
Copy link
Contributor Author

@rhuanjl Right, I know they’re different mechanisms. I was just referring specifically to akroshg’s assertion that the throw is not executed at all, which clearly isn’t the case or else rejection tracking would be pointless.

@kfarnung kfarnung added the Bug label Feb 13, 2018
@fatcerberus
Copy link
Contributor Author

fatcerberus commented Feb 15, 2018

This seems to be the culprit:
https://github.com/Microsoft/ChakraCore/blob/002e9be2aa8c8ea29ce37a605c7f6c6baa2890bd/lib/Runtime/Debug/ProbeContainer.cpp#L1026-L1029

In the case of the following code:

async function f() {
    await null;
    throw new Error();
}
f();

When deciding whether to pass this exception onto the debugger, ChakraCore considers the thrown exception to both:

  1. Have a catch handler, --AND--
  2. Be both thrown and caught in user code

While 1) is true, 2) most definitely isn't as there is no catch in the async function. The exception is caught directly by the runtime in order to reject a promise.

@rhuanjl
Copy link
Collaborator

rhuanjl commented Feb 15, 2018

I've taken a quick look at this, and I believe that the issue is not Async functions at all but rather the way Promise reactions are implemented

An alternative repro of this issue would be:

Promise.resolve().then(()=>{throw new Error("hunger increases");});

No break point is seen in the debugger for this code.

In JavascriptPromise.cpp the following function call occurs in 2 different methods that handle promise reactions:

Js::JavascriptExceptionOperators::AutoCatchHandlerExists autoCatchHandlerExists(scriptContext);

I believe from looking over the code that this turns off debugger break points until it's disabled again I assume there is a purpose for this - but I'll be honest I don't really understand some parts of this code at the moment, could the fix perhaps be to only set that if the promise IsHandled?

@mike-kaufman
Copy link
Contributor

mike-kaufman commented Jun 18, 2018

See PR #5328 for a fix here a fix for exceptions in promises w/out handlers hooked up.

chakrabot pushed a commit that referenced this issue Jun 27, 2018
…in promises wouldn't notify debugger

Merge pull request #5328 from mike-kaufman:build/mkaufman/support-uncaught-exception-handler-in-promises

If an exception was raised inside a promise and the promise didn't have any rejection handlers, we wouldn't notify the debugger that an "unhandled" exception occurred.  Fixed this up and added some simple tests for it.

This addresses the common cases for promises, but doesn't yet address async/await constructs.  Will leave #4630 open for that.
chakrabot pushed a commit that referenced this issue Jun 27, 2018
…t" exceptions in promises wouldn't notify debugger

Merge pull request #5328 from mike-kaufman:build/mkaufman/support-uncaught-exception-handler-in-promises

If an exception was raised inside a promise and the promise didn't have any rejection handlers, we wouldn't notify the debugger that an "unhandled" exception occurred.  Fixed this up and added some simple tests for it.

This addresses the common cases for promises, but doesn't yet address async/await constructs.  Will leave #4630 open for that.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

9 participants