Commit 0faf0150 authored by aandrey@chromium.org's avatar aandrey@chromium.org

Show stack traces in console for unhandled promise rejection messages.

BUG=393913
R=pfeldman@chromium.org, yurys@chromium.org

Review URL: https://codereview.chromium.org/657063002

git-svn-id: svn://svn.chromium.org/blink/trunk@183810 bbb929c8-8fbe-4397-9dbb-9b2b20218538
parent 5c9d037f
...@@ -72,6 +72,12 @@ InspectorTest.fixConsoleViewportDimensions = function(width, height) ...@@ -72,6 +72,12 @@ InspectorTest.fixConsoleViewportDimensions = function(width, height)
viewport.invalidate(); viewport.invalidate();
} }
InspectorTest.consoleMessagesCount = function()
{
var consoleView = WebInspector.ConsolePanel._view();
return consoleView._consoleMessages.length;
}
InspectorTest.dumpConsoleMessages = function(printOriginatingCommand, dumpClassNames, formatter) InspectorTest.dumpConsoleMessages = function(printOriginatingCommand, dumpClassNames, formatter)
{ {
InspectorTest.addResults(InspectorTest.dumpConsoleMessagesIntoArray(printOriginatingCommand, dumpClassNames, formatter)); InspectorTest.addResults(InspectorTest.dumpConsoleMessagesIntoArray(printOriginatingCommand, dumpClassNames, formatter));
......
CONSOLE ERROR: Unhandled promise rejection CONSOLE ERROR: Unhandled promise rejection
CONSOLE ERROR: Unhandled promise rejection CONSOLE ERROR: Unhandled promise rejection
CONSOLE ERROR: Unhandled promise rejection CONSOLE ERROR: line 9: Unhandled promise rejection
CONSOLE ERROR: Unhandled promise rejection CONSOLE ERROR: line 27: Unhandled promise rejection
Tests that uncaught promise rejections are logged into console. Tests that uncaught promise rejections are logged into console.
Unhandled promise rejection Promise console-uncaught-promise.html:1 Unhandled promise rejection Promise
__proto__: Promise __proto__: Promise
[[PromiseStatus]]: "rejected" [[PromiseStatus]]: "rejected"
[[PromiseValue]]: Error: onload.err1 [[PromiseValue]]: Error: onload.err1
Unhandled promise rejection Promise console-uncaught-promise.html:1 Unhandled promise rejection Promise
__proto__: Promise __proto__: Promise
[[PromiseStatus]]: "rejected" [[PromiseStatus]]: "rejected"
[[PromiseValue]]: Error: onload.err2 [[PromiseValue]]: Error: onload.err2
Unhandled promise rejection Promise {[[PromiseStatus]]: "rejected", [[PromiseValue]]: Error: inspector.err1}
__proto__: Promise console-uncaught-promise.html:9 Unhandled promise rejection Promise {[[PromiseStatus]]: "rejected", [[PromiseValue]]: Error: inspector.err1}
[[PromiseStatus]]: "rejected" __proto__: Promise
[[PromiseValue]]: Error: inspector.err1 [[PromiseStatus]]: "rejected"
Unhandled promise rejection Promise {[[PromiseStatus]]: "rejected", [[PromiseValue]]: Error: inspector.err2} [[PromiseValue]]: Error: inspector.err1
__proto__: Promise console-uncaught-promise.html:9 runPromises
[[PromiseStatus]]: "rejected" console-uncaught-promise.html:40 timeout
[[PromiseValue]]: Error: inspector.err2
console-uncaught-promise.html:27 Unhandled promise rejection Promise {[[PromiseStatus]]: "rejected", [[PromiseValue]]: Error: inspector.err2}
__proto__: Promise
[[PromiseStatus]]: "rejected"
[[PromiseValue]]: Error: inspector.err2
console-uncaught-promise.html:27 runPromises
console-uncaught-promise.html:40 timeout
...@@ -33,29 +33,33 @@ function onload() ...@@ -33,29 +33,33 @@ function onload()
runTest(); runTest();
} }
function runPromisesFromInspector()
{
// setTimeout to cut off VM call frames from the stack trace.
setTimeout(function timeout() {
runPromises("inspector")
}, 0);
}
function test() function test()
{ {
InspectorTest.addConsoleViewSniffer(addMessage, true); InspectorTest.addConsoleViewSniffer(checkConsoleMessages, true);
WebInspector.console.showPromise().done();
WebInspector.console.showPromise().then(function() { checkConsoleMessages();
InspectorTest.evaluateInPage("runPromises('inspector')");
});
var count = 0; function checkConsoleMessages()
function addMessage(uiMessage)
{ {
if (uiMessage.toString().indexOf("inspector.err") !== -1) var count = InspectorTest.consoleMessagesCount();
++count;
if (count === 2) if (count === 2)
InspectorTest.evaluateInPage("runPromisesFromInspector()");
else if (count === 4)
InspectorTest.expandConsoleMessages(dump); InspectorTest.expandConsoleMessages(dump);
} }
function dump() function dump()
{ {
// Sort console messages from async Promises to avoid flakiness. InspectorTest.dumpConsoleMessages(false, false, InspectorTest.textContentWithLineBreaks);
var results = InspectorTest.dumpConsoleMessagesIntoArray(false, false, InspectorTest.textContentWithLineBreaks);
results.sort();
InspectorTest.addResults(results);
InspectorTest.completeTest(); InspectorTest.completeTest();
} }
} }
......
CONSOLE ERROR: Unhandled promise rejection CONSOLE ERROR: line 35: Unhandled promise rejection
CONSOLE ERROR: Unhandled promise rejection CONSOLE ERROR: line 35: Unhandled promise rejection
Tests that pause on promise rejection works. Tests that pause on promise rejection works.
=== Pausing only on uncaught exceptions === === Pausing only on uncaught exceptions ===
......
CONSOLE ERROR: Unhandled promise rejection CONSOLE ERROR: line 16: Unhandled promise rejection
CONSOLE ERROR: Unhandled promise rejection CONSOLE ERROR: line 34: Unhandled promise rejection
Tests uncaught promise rejections fired during pause. Tests uncaught promise rejections fired during pause.
Set timer for test function. Set timer for test function.
debugger-uncaught-promise-on-pause.html:10 Console was cleared debugger-uncaught-promise-on-pause.html:10 Console was cleared
Unhandled promise rejection Promise {[[PromiseStatus]]: "rejected", [[PromiseValue]]: Error: inspector.err1}
__proto__: Promise debugger-uncaught-promise-on-pause.html:16 Unhandled promise rejection Promise {[[PromiseStatus]]: "rejected", [[PromiseValue]]: Error: inspector.err1}
[[PromiseStatus]]: "rejected" __proto__: Promise
[[PromiseValue]]: Error: inspector.err1 [[PromiseStatus]]: "rejected"
Unhandled promise rejection Promise {[[PromiseStatus]]: "rejected", [[PromiseValue]]: Error: inspector.err2} [[PromiseValue]]: Error: inspector.err1
__proto__: Promise debugger-uncaught-promise-on-pause.html:16 runPromises
[[PromiseStatus]]: "rejected" debugger-uncaught-promise-on-pause.html:41 timeout
[[PromiseValue]]: Error: inspector.err2
debugger-uncaught-promise-on-pause.html:34 Unhandled promise rejection Promise {[[PromiseStatus]]: "rejected", [[PromiseValue]]: Error: inspector.err2}
__proto__: Promise
[[PromiseStatus]]: "rejected"
[[PromiseValue]]: Error: inspector.err2
debugger-uncaught-promise-on-pause.html:34 runPromises
debugger-uncaught-promise-on-pause.html:41 timeout
...@@ -34,6 +34,14 @@ function runPromises(source) ...@@ -34,6 +34,14 @@ function runPromises(source)
reject(new Error(source + ".err2")); reject(new Error(source + ".err2"));
} }
function runPromisesFromInspector()
{
// setTimeout to cut off VM call frames from the stack trace.
setTimeout(function timeout() {
runPromises("inspector")
}, 0);
}
function test() function test()
{ {
InspectorTest.setQuiet(true); InspectorTest.setQuiet(true);
...@@ -47,7 +55,7 @@ function test() ...@@ -47,7 +55,7 @@ function test()
function didPause(callFrames, reason, breakpointIds, asyncStackTrace) function didPause(callFrames, reason, breakpointIds, asyncStackTrace)
{ {
InspectorTest.evaluateInPage("runPromises('inspector')", resumeExecution); InspectorTest.evaluateInPage("runPromisesFromInspector()", resumeExecution);
} }
function resumeExecution() function resumeExecution()
......
...@@ -158,7 +158,23 @@ static void messageHandlerInMainThread(v8::Handle<v8::Message> message, v8::Hand ...@@ -158,7 +158,23 @@ static void messageHandlerInMainThread(v8::Handle<v8::Message> message, v8::Hand
} }
} }
typedef WillBeHeapDeque<ScriptValue> PromiseRejectMessageQueue; namespace {
class PromiseRejectMessage {
public:
PromiseRejectMessage(const ScriptValue& promise, PassRefPtrWillBeRawPtr<ScriptCallStack> callStack)
: m_promise(promise)
, m_callStack(callStack)
{
}
const ScriptValue m_promise;
const RefPtrWillBeMember<ScriptCallStack> m_callStack;
};
} // namespace
typedef WillBeHeapDeque<PromiseRejectMessage> PromiseRejectMessageQueue;
static PromiseRejectMessageQueue& promiseRejectMessageQueue() static PromiseRejectMessageQueue& promiseRejectMessageQueue()
{ {
...@@ -172,14 +188,14 @@ void V8Initializer::reportRejectedPromises() ...@@ -172,14 +188,14 @@ void V8Initializer::reportRejectedPromises()
PromiseRejectMessageQueue& queue = promiseRejectMessageQueue(); PromiseRejectMessageQueue& queue = promiseRejectMessageQueue();
while (!queue.isEmpty()) { while (!queue.isEmpty()) {
ScriptValue promise = queue.takeFirst(); PromiseRejectMessage message = queue.takeFirst();
ScriptState* scriptState = promise.scriptState(); ScriptState* scriptState = message.m_promise.scriptState();
if (!scriptState->contextIsValid()) if (!scriptState->contextIsValid())
continue; continue;
ScriptState::Scope scope(scriptState); ScriptState::Scope scope(scriptState);
ASSERT(!promise.isEmpty()); ASSERT(!message.m_promise.isEmpty());
v8::Handle<v8::Value> value = promise.v8Value(); v8::Handle<v8::Value> value = message.m_promise.v8Value();
ASSERT(!value.IsEmpty() && value->IsPromise()); ASSERT(!value.IsEmpty() && value->IsPromise());
if (v8::Handle<v8::Promise>::Cast(value)->HasHandler()) if (v8::Handle<v8::Promise>::Cast(value)->HasHandler())
continue; continue;
...@@ -191,11 +207,12 @@ void V8Initializer::reportRejectedPromises() ...@@ -191,11 +207,12 @@ void V8Initializer::reportRejectedPromises()
const String errorMessage = "Unhandled promise rejection"; const String errorMessage = "Unhandled promise rejection";
Vector<ScriptValue> args; Vector<ScriptValue> args;
args.append(ScriptValue(scriptState, v8String(scriptState->isolate(), errorMessage))); args.append(ScriptValue(scriptState, v8String(scriptState->isolate(), errorMessage)));
args.append(promise); args.append(message.m_promise);
RefPtrWillBeRawPtr<ScriptArguments> arguments = ScriptArguments::create(scriptState, args); RefPtrWillBeRawPtr<ScriptArguments> arguments = ScriptArguments::create(scriptState, args);
RefPtrWillBeRawPtr<ConsoleMessage> consoleMessage = ConsoleMessage::create(JSMessageSource, ErrorMessageLevel, errorMessage, "", 0); RefPtrWillBeRawPtr<ConsoleMessage> consoleMessage = ConsoleMessage::create(JSMessageSource, ErrorMessageLevel, errorMessage);
consoleMessage->setScriptArguments(arguments); consoleMessage->setScriptArguments(arguments);
consoleMessage->setCallStack(message.m_callStack);
executionContext->addConsoleMessage(consoleMessage.release()); executionContext->addConsoleMessage(consoleMessage.release());
} }
} }
...@@ -226,8 +243,13 @@ static void promiseRejectHandlerInMainThread(v8::PromiseRejectMessage message) ...@@ -226,8 +243,13 @@ static void promiseRejectHandlerInMainThread(v8::PromiseRejectMessage message)
if (!toFrameIfNotDetached(context)) if (!toFrameIfNotDetached(context))
return; return;
RefPtrWillBeRawPtr<ScriptCallStack> callStack = nullptr;
v8::Handle<v8::StackTrace> stackTrace = message.GetStackTrace();
if (!stackTrace.IsEmpty() && stackTrace->GetFrameCount() > 0)
callStack = createScriptCallStack(stackTrace, ScriptCallStack::maxCallStackSizeToCapture, isolate);
ScriptState* scriptState = ScriptState::from(context); ScriptState* scriptState = ScriptState::from(context);
promiseRejectMessageQueue().append(ScriptValue(scriptState, promise)); promiseRejectMessageQueue().append(PromiseRejectMessage(ScriptValue(scriptState, promise), callStack));
} }
static void failedAccessCheckCallbackInMainThread(v8::Local<v8::Object> host, v8::AccessType type, v8::Local<v8::Value> data) static void failedAccessCheckCallbackInMainThread(v8::Local<v8::Object> host, v8::AccessType type, v8::Local<v8::Value> data)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment