Closed alexjordan closed 5 years ago
@alexjordan do you expect to have two functionExit
events in the try/catch/finally
function?
I think it would be simpler to have always a matched functionEnter/Exit
while we can have multiple _return
events. And we can provide the function and arguments in the callback of functionExit
and _return
events for convenience in the analysis.
_return callbacks are missing for the ConstantReturnNode.
Which return values are missing in this example? I tried with the latest master, and I can see both return events for 42 and 43 without changes.
@Haiyang-Sun
do you expect to have two functionExit events
Sorry, the output was a bit confusing ('functionExit'
was logged twice from the same callback), I cleaned it up in 9a784d1.
Which return values are missing in this example?
I was able to fix the _return
callbacks in f45ca38, but the final return value is missing from functionExit
, thus you should see this error:
Testing exitException.js in minitests with analysis enter-exit
Fail @enter-exit
15c15
< functionExit: (src/ch.usi.inf.nodeprof.test/js/minitests/exitException.js:48:2:58:2) / returnVal (number): 43
---
> functionExit: (src/ch.usi.inf.nodeprof.test/js/minitests/exitException.js:48:2:58:2) / 3
Thanks, I have just pushed a fix for the root event.
I guess maybe we could make use of this ReturnException
to implement the missing feature of Jalangi to change the return value of some expression, e.g., by throwing a return exception in the callback part to force returning a given value from the dynamic analysis. @eleinadani , what do you think?
@alexjordan The tests pass now, however, I notice a compiler warning when testing mx test-npm
:
https://travis-ci.com/Haiyang-Sun/nodeprof.js/builds/133268691#L4741
(should not be materialized (must not let virtual object escape at node 33392|EndNode):
)
And the stack trace does seem to point to the right place for the problem.
It's true that the return slot might be missing when ReturnValueInFrame is disabled. For now, it is safe as it is only used in places where the ReturnValueInFrame has to enabled. Ideally, the ReturnException
could be handled in the Truffle instrumentation layer. I
The yield attribute is to distinguish the "fake" function exit (suspension) and a real function exit for a generator function or an asynchronous function. The execution of such functions can be suspended. To track the suspension of the execution, we track and mark the function exits with exception.yield=true. This makes sure that every (async)functionEnter has a corresponding (async)functionExit and we can tell a suspended function if the yield attribute is true.
I've tried testing this PR, and it seems to only work sometimes. Whether or not these callbacks work seems to depend on the body of the function itself.
For example, the callbacks do not work for the exact program I put in this issue (async.js
). However, they do work for the very slightly modified version that was used as a test case in this PR.
The difference between the program I provided and the program used as a test case in this PR is extremely minimal: a few console.log
's were added to the countTo6
function. From my testing it seems that, in general, if a function is more complex, the async
/await
callbacks are more likely to work. For example, if you added only one console.log
to the original program I suggested, the callbacks do not work correctly. Adding two console.log
's seems to make the callbacks work correctly. Adding something like a variable declaration to the countTo6
function will also make it work.
Here are some examples that I found make the async
/await
callbacks work correctly/not work correctly:
async function countTo6(x) {
// adding two `console.log`'s
console.log("hi 1!");
console.log("hi 2!");
if (x > 5) {
return x;
} else {
return await countTo6(x + 1);
}
}
async function main() {
return await countTo6(1);
}
main().then(v => console.log("done! answer = " + v));
async function countTo6(x) {
// adding only one `console.log`
console.log("hi 1!");
if (x > 5) {
return x;
} else {
return await countTo6(x + 1);
}
}
async function main() {
return await countTo6(1);
}
main().then(v => console.log("done! answer = " + v));
async function countTo6(x) {
// adding a variable declaration
let uselessVariable = 0;
if (x > 5) {
return x;
} else {
return await countTo6(x + 1);
}
}
async function main() {
return await countTo6(1);
}
main().then(v => console.log("done! answer = " + v));
async function countTo6(x) {
// the original function I suggested
if (x > 5) {
return x;
} else {
return await countTo6(x + 1);
}
}
async function main() {
return await countTo6(1);
}
main().then(v => console.log("done! answer = " + v));
I also found a simple test program that causes NodeProf to crash.
analysis.js
// DO NOT INSTRUMENT
(function (sandbox) {
function MyAnalysis() {
this.awaitPre = function (iid, valAwaited) {
console.log(valAwaited);
}
}
sandbox.analysis = new MyAnalysis();
})(J$);
program.js
async function func1(x) {
await func2(x);
await func2(x);
}
async function func2(x) {
return await func3(x);
}
async function func3(x) {
return x;
}
func1(5);
Promise { 5 }
Promise { <pending> }
Promise { 5 }
Promise { 5 }
Promise { 5 }
Promise { <pending> }
java.lang.AssertionError
at ch.usi.inf.nodeprof.jalangi.factory.AbstractFactory.setPreArguments(AbstractFactory.java:128)
at ch.usi.inf.nodeprof.jalangi.factory.AwaitFactory$1.executePre(AwaitFactory.java:48)
at ch.usi.inf.nodeprof.analysis.ProfilerExecutionEventNode.onInputValue(ProfilerExecutionEventNode.java:84)
at com.oracle.truffle.api.instrumentation.ProbeNode$EventProviderChainNode.innerOnInputValue(ProbeNode.java:1443)
at com.oracle.truffle.api.instrumentation.ProbeNode$EventChainNode.onInputValue(ProbeNode.java:957)
at com.oracle.truffle.api.instrumentation.ProbeNode.onInputValue(ProbeNode.java:317)
at com.oracle.truffle.api.instrumentation.ProbeNode$InputValueChainNode.innerOnReturnValue(ProbeNode.java:1518)
at com.oracle.truffle.api.instrumentation.ProbeNode$EventChainNode.onReturnValue(ProbeNode.java:1012)
at com.oracle.truffle.api.instrumentation.ProbeNode.onReturnValue(ProbeNode.java:171)
at com.oracle.truffle.js.nodes.access.JSTargetableNodeWrapper.executeWithTarget(JSTargetableNodeWrapper.java:574)
at com.oracle.truffle.js.nodes.control.AwaitNode.resumeAwait(AwaitNode.java:228)
at com.oracle.truffle.js.nodes.control.AwaitNode.resume(AwaitNode.java:220)
at com.oracle.truffle.js.nodes.control.GeneratorWrapperNode.execute(GeneratorWrapperNode.java:114)
at com.oracle.truffle.js.nodes.JavaScriptNodeWrapper.execute(JavaScriptNodeWrapper.java:48)
at com.oracle.truffle.js.nodes.control.ReturnNode$TerminalPositionReturnNode.execute(ReturnNode.java:163)
at com.oracle.truffle.js.nodes.JavaScriptNodeWrapper.execute(JavaScriptNodeWrapper.java:48)
at com.oracle.truffle.js.nodes.control.AsyncFunctionBodyNode$AsyncFunctionRootNode.execute(AsyncFunctionBodyNode.java:99)
at com.oracle.truffle.api.impl.DefaultCallTarget.callDirectOrIndirect(DefaultCallTarget.java:85)
at com.oracle.truffle.api.impl.DefaultDirectCallNode.call(DefaultDirectCallNode.java:59)
at com.oracle.truffle.js.nodes.function.InternalCallNode.directCall(InternalCallNode.java:70)
at com.oracle.truffle.js.nodes.function.InternalCallNodeGen.execute(InternalCallNodeGen.java:40)
at com.oracle.truffle.js.nodes.control.AwaitResumeNode.execute(AwaitResumeNode.java:68)
at com.oracle.truffle.js.nodes.control.AwaitNode$1AwaitFulfilledRootNode.execute(AwaitNode.java:271)
at com.oracle.truffle.api.impl.DefaultCallTarget.callDirectOrIndirect(DefaultCallTarget.java:85)
at com.oracle.truffle.api.impl.DefaultDirectCallNode.call(DefaultDirectCallNode.java:59)
at com.oracle.truffle.js.nodes.function.JSFunctionCallNode$UnboundJSFunctionCacheNode.executeCall(JSFunctionCallNode.java:1250)
at com.oracle.truffle.js.nodes.function.JSFunctionCallNode.executeCall(JSFunctionCallNode.java:233)
at com.oracle.truffle.js.nodes.promise.PromiseReactionJobNode$1PromiseReactionJob.execute(PromiseReactionJobNode.java:125)
at com.oracle.truffle.api.impl.DefaultCallTarget.call(DefaultCallTarget.java:102)
at com.oracle.truffle.js.runtime.builtins.JSFunction.call(JSFunction.java:369)
at com.oracle.truffle.js.runtime.JSAgent.processAllPromises(JSAgent.java:147)
at com.oracle.truffle.trufflenode.GraalJSAccess.isolateRunMicrotasks(GraalJSAccess.java:2482)
at com.oracle.truffle.trufflenode.NativeAccess.executeFunction0(Native Method)
at com.oracle.truffle.trufflenode.node.ExecuteNativeFunctionNode.executeFunction0(ExecuteNativeFunctionNode.java:278)
at com.oracle.truffle.trufflenode.node.ExecuteNativeFunctionNode.execute(ExecuteNativeFunctionNode.java:154)
at com.oracle.truffle.trufflenode.node.ExecuteNativeFunctionNode$NativeFunctionRootNode.execute(ExecuteNativeFunctionNode.java:342)
at com.oracle.truffle.api.impl.DefaultCallTarget.callDirectOrIndirect(DefaultCallTarget.java:85)
at com.oracle.truffle.api.impl.DefaultDirectCallNode.call(DefaultDirectCallNode.java:59)
at com.oracle.truffle.js.nodes.function.JSFunctionCallNode$UnboundJSFunctionCacheNode.executeCall(JSFunctionCallNode.java:1250)
at com.oracle.truffle.js.nodes.function.JSFunctionCallNode.executeAndSpecialize(JSFunctionCallNode.java:291)
at com.oracle.truffle.js.nodes.function.JSFunctionCallNode.executeCall(JSFunctionCallNode.java:237)
at com.oracle.truffle.js.nodes.function.JSFunctionCallNode$CallNode.execute(JSFunctionCallNode.java:505)
at com.oracle.truffle.js.nodes.JavaScriptNode.executeVoid(JavaScriptNode.java:212)
at com.oracle.truffle.js.nodes.control.AbstractBlockNode.executeVoid(AbstractBlockNode.java:91)
at com.oracle.truffle.js.nodes.control.AbstractBlockNode.executeVoid(AbstractBlockNode.java:74)
at com.oracle.truffle.js.nodes.control.VoidBlockNode.execute(VoidBlockNode.java:58)
at com.oracle.truffle.js.nodes.instrumentation.JSTaggedExecutionNode.execute(JSTaggedExecutionNode.java:113)
at com.oracle.truffle.js.nodes.JavaScriptNode.executeVoid(JavaScriptNode.java:212)
at com.oracle.truffle.js.nodes.control.AbstractRepeatingNode.executeBody(AbstractRepeatingNode.java:73)
at com.oracle.truffle.js.nodes.control.WhileNode$DoWhileRepeatingNode.executeRepeating(WhileNode.java:186)
at com.oracle.truffle.api.nodes.RepeatingNode.executeRepeatingWithValue(RepeatingNode.java:106)
at com.oracle.truffle.api.impl.DefaultLoopNode.execute(DefaultLoopNode.java:71)
at com.oracle.truffle.js.nodes.control.WhileNode.executeVoid(WhileNode.java:168)
at com.oracle.truffle.js.nodes.control.DirectBreakTargetNode.executeVoid(DirectBreakTargetNode.java:73)
at com.oracle.truffle.js.nodes.control.AbstractBlockNode.executeVoid(AbstractBlockNode.java:91)
at com.oracle.truffle.js.nodes.control.AbstractBlockNode.executeVoid(AbstractBlockNode.java:74)
at com.oracle.truffle.js.nodes.control.VoidBlockNode.execute(VoidBlockNode.java:58)
at com.oracle.truffle.js.nodes.function.FunctionBodyNode.execute(FunctionBodyNode.java:73)
at com.oracle.truffle.js.nodes.function.FunctionRootNode.executeInRealm(FunctionRootNode.java:147)
at com.oracle.truffle.js.runtime.JavaScriptRealmBoundaryRootNode.execute(JavaScriptRealmBoundaryRootNode.java:93)
at com.oracle.truffle.api.impl.DefaultCallTarget.call(DefaultCallTarget.java:102)
at com.oracle.truffle.js.runtime.builtins.JSFunction.call(JSFunction.java:369)
at com.oracle.truffle.js.runtime.JSRuntime.call(JSRuntime.java:2436)
at com.oracle.truffle.trufflenode.GraalJSAccess.functionCall(GraalJSAccess.java:1295)
at com.oracle.truffle.trufflenode.GraalJSAccess.functionCall0(GraalJSAccess.java:1304)
Caused by: Attached Guest Language Frames (5)
I found that making the following changes to program.js
cause NodeProf not to crash:
return await func3(x)
with await func3(x)
on line 7func2
on lines 2 and 3 with calls to func3
Hi, I have found the cause to the problem here and am still working on a solution to fix it. Will start a PR to Graal.js once done.
@mwaldrich You can already have a preview of my fix in this branch: https://github.com/Haiyang-Sun/nodeprof.js/commits/asyncawait-fix
It should be able to fix these problems you mentioned above.
@Haiyang-Sun I tried using the new branch, and it seems not to crash anymore. Thanks!
@mwaldrich does the PR from @Haiyang-Sun (https://github.com/Haiyang-Sun/nodeprof.js/commits/asyncawait-fix) fix all the problems you reported above, or just the AssertionError
crash in Nodeprof?
This contains a test for
return
from the discussion in #37 that showed that functionExit is missing return values (1) when issued from a try-catch block and (2)_return
callbacks are missing for theConstantReturnNode
.functionExit
whenReturnValueInFrame
is disabledfunctionExit
whenReturnValueInFrame
is enabledConstantReturnNode