f296dad962
This patch tracks the stack of async functions differently from other Promise async stack tracking. With this patch, the stack trace of a callstack of async functions should look similarly to the call stack if all of the functions were synchronous. An example can be found in the updated test expectations: https://codereview.chromium.org/2362923002 . The new stack traces are implemented using existing mechanisms in the inspector. The inspector has two ways to save async stack traces: recurring and non-recurring stacks. An example of a non-recurring stack is setTimeout, and a recurring one is saved for setInterval. Recurring stacks are deleted only when a special "cancel" function is called, rather than being deleted after being used the first time. Previous Promise async stack tracking always used non-recurring stacks. For async functions, this patch saves a recurring stack. The top frame of the stack is duplicated, as the resuming function contains a similar frame; the devtools frontend is responsible for removing or marking this frame, which it can do based on seeing the [async function] line which follows it. The second frame will instead be provided by the resuming execution context. The recurring stack is saved when the async function is entered, and it is deleted from a finally block. The id of the stack is saved in the outer Promise being constructed by the async function. When an intermediate throwaway Promise will be triggered as a reaction, it will be identified as such based on its debugging metadata, and the corresponding async function's recurring stack will be used. BUG=v8:4483 Review-Url: https://codereview.chromium.org/2357423002 Cr-Commit-Position: refs/heads/master@{#39695}
76 lines
1.9 KiB
JavaScript
76 lines
1.9 KiB
JavaScript
// Copyright 2016 the V8 project authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
// Flags: --harmony-async-await --expose-debug-as debug --allow-natives-syntax
|
|
|
|
// The test observes the callbacks that async/await makes to the inspector
|
|
// to make accurate stack traces. The pattern is based on saving a stack once
|
|
// with enqueueRecurring and restoring it multiple times.
|
|
|
|
// Additionally, the limited number of events is an indirect indication that
|
|
// we are not doing extra Promise processing that could be associated with memory
|
|
// leaks (v8:5380). In particular, no stacks are saved and restored for extra
|
|
// Promise handling on throwaway Promises.
|
|
|
|
// TODO(littledan): Write a test that demonstrates that the memory leak in
|
|
// the exception case is fixed.
|
|
|
|
Debug = debug.Debug;
|
|
|
|
var base_id = -1;
|
|
var exception = null;
|
|
var expected = [
|
|
'enqueueRecurring #1',
|
|
'willHandle #1',
|
|
'then #1',
|
|
'didHandle #1',
|
|
'willHandle #1',
|
|
'then #2',
|
|
'cancel #1',
|
|
'didHandle #1',
|
|
];
|
|
|
|
function assertLog(msg) {
|
|
print(msg);
|
|
assertTrue(expected.length > 0);
|
|
assertEquals(expected.shift(), msg);
|
|
if (!expected.length) {
|
|
Debug.setListener(null);
|
|
}
|
|
}
|
|
|
|
function listener(event, exec_state, event_data, data) {
|
|
if (event != Debug.DebugEvent.AsyncTaskEvent) return;
|
|
try {
|
|
if (base_id < 0)
|
|
base_id = event_data.id();
|
|
var id = event_data.id() - base_id + 1;
|
|
assertTrue("async function" == event_data.name());
|
|
assertLog(event_data.type() + " #" + id);
|
|
} catch (e) {
|
|
print(e + e.stack)
|
|
exception = e;
|
|
}
|
|
}
|
|
|
|
Debug.setListener(listener);
|
|
|
|
var resolver;
|
|
var p = new Promise(function(resolve, reject) {
|
|
resolver = resolve;
|
|
});
|
|
|
|
async function main() {
|
|
await p;
|
|
assertLog("then #1");
|
|
await undefined;
|
|
assertLog("then #2");
|
|
}
|
|
main();
|
|
resolver();
|
|
|
|
%RunMicrotasks();
|
|
|
|
assertNull(exception);
|