Commit 4d07a89d authored by Alexey Kozyatinskiy's avatar Alexey Kozyatinskiy Committed by Commit Bot

[inspector] fixed dcheck in stack trace

Async stack trace can contain empty syncrhonous stack and external
stack.

R=dgozman@chromium.org

Bug: chromium:790567
Cq-Include-Trybots: master.tryserver.blink:linux_trusty_blink_rel
Change-Id: I2b04743f7c4f15a038eb1041cc7fc117d438b6b2
Reviewed-on: https://chromium-review.googlesource.com/822971
Commit-Queue: Aleksey Kozyatinskiy <kozyatinskiy@chromium.org>
Reviewed-by: 's avatarDmitry Gozman <dgozman@chromium.org>
Cr-Commit-Position: refs/heads/master@{#50117}
parent 8d835e6d
...@@ -42,6 +42,7 @@ void calculateAsyncChain(V8Debugger* debugger, int contextGroupId, ...@@ -42,6 +42,7 @@ void calculateAsyncChain(V8Debugger* debugger, int contextGroupId,
// not happen if we have proper instrumentation, but let's double-check to be // not happen if we have proper instrumentation, but let's double-check to be
// safe. // safe.
if (contextGroupId && *asyncParent && if (contextGroupId && *asyncParent &&
(*asyncParent)->externalParent().IsInvalid() &&
(*asyncParent)->contextGroupId() != contextGroupId) { (*asyncParent)->contextGroupId() != contextGroupId) {
asyncParent->reset(); asyncParent->reset();
*externalParent = V8StackTraceId(); *externalParent = V8StackTraceId();
...@@ -338,14 +339,15 @@ std::shared_ptr<AsyncStackTrace> AsyncStackTrace::capture( ...@@ -338,14 +339,15 @@ std::shared_ptr<AsyncStackTrace> AsyncStackTrace::capture(
// but doesn't synchronous we can merge them together. e.g. Promise // but doesn't synchronous we can merge them together. e.g. Promise
// ThenableJob. // ThenableJob.
if (asyncParent && frames.empty() && if (asyncParent && frames.empty() &&
asyncParent->m_description == description) { (asyncParent->m_description == description || description.isEmpty())) {
return asyncParent; return asyncParent;
} }
DCHECK(contextGroupId || asyncParent); DCHECK(contextGroupId || asyncParent || !externalParent.IsInvalid());
if (!contextGroupId && asyncParent) { if (!contextGroupId && asyncParent) {
contextGroupId = asyncParent->m_contextGroupId; contextGroupId = asyncParent->m_contextGroupId;
} }
return std::shared_ptr<AsyncStackTrace>( return std::shared_ptr<AsyncStackTrace>(
new AsyncStackTrace(contextGroupId, description, std::move(frames), new AsyncStackTrace(contextGroupId, description, std::move(frames),
asyncParent, externalParent)); asyncParent, externalParent));
...@@ -362,7 +364,7 @@ AsyncStackTrace::AsyncStackTrace( ...@@ -362,7 +364,7 @@ AsyncStackTrace::AsyncStackTrace(
m_frames(std::move(frames)), m_frames(std::move(frames)),
m_asyncParent(asyncParent), m_asyncParent(asyncParent),
m_externalParent(externalParent) { m_externalParent(externalParent) {
DCHECK(m_contextGroupId); DCHECK(m_contextGroupId || (!externalParent.IsInvalid() && m_frames.empty()));
} }
std::unique_ptr<protocol::Runtime::StackTrace> std::unique_ptr<protocol::Runtime::StackTrace>
......
...@@ -120,6 +120,7 @@ class AsyncStackTrace { ...@@ -120,6 +120,7 @@ class AsyncStackTrace {
const String16& description() const; const String16& description() const;
std::weak_ptr<AsyncStackTrace> parent() const; std::weak_ptr<AsyncStackTrace> parent() const;
bool isEmpty() const; bool isEmpty() const;
const V8StackTraceId& externalParent() const { return m_externalParent; }
const std::vector<std::shared_ptr<StackFrame>>& frames() const { const std::vector<std::shared_ptr<StackFrame>>& frames() const {
return m_frames; return m_frames;
......
Tests different combinations of async stacks in chains.
Regular
userFunction (test.js:1:36)
-- inner async --
runWithRegular (utils.js:2:12)
inner (test.js:2:28)
runWithNone (utils.js:27:2)
(anonymous) (test.js:3:10)
Regular - Regular
userFunction (test.js:1:36)
-- inner async --
runWithRegular (utils.js:2:12)
inner (test.js:2:28)
-- outer async --
runWithRegular (utils.js:2:12)
(anonymous) (test.js:3:10)
Regular - EmptyName
userFunction (test.js:1:36)
-- inner async --
runWithRegular (utils.js:2:12)
inner (test.js:2:28)
-- <empty> --
runWithEmptyName (utils.js:6:12)
(anonymous) (test.js:3:10)
Regular - EmptyStack
userFunction (test.js:1:36)
-- inner async --
runWithRegular (utils.js:2:12)
inner (test.js:2:28)
Regular - EmptyNameEmptyStack
userFunction (test.js:1:36)
-- inner async --
runWithRegular (utils.js:2:12)
inner (test.js:2:28)
Regular - External
userFunction (test.js:1:36)
-- inner async --
runWithRegular (utils.js:2:12)
inner (test.js:2:28)
runWithRegular (utils.js:21:4)
<external stack>
EmptyName
userFunction (test.js:1:36)
-- <empty> --
runWithEmptyName (utils.js:6:12)
inner (test.js:2:28)
runWithNone (utils.js:27:2)
(anonymous) (test.js:3:10)
EmptyName - Regular
userFunction (test.js:1:36)
-- <empty> --
runWithEmptyName (utils.js:6:12)
inner (test.js:2:28)
-- outer async --
runWithRegular (utils.js:2:12)
(anonymous) (test.js:3:10)
EmptyName - EmptyName
userFunction (test.js:1:36)
-- <empty> --
runWithEmptyName (utils.js:6:12)
inner (test.js:2:28)
-- <empty> --
runWithEmptyName (utils.js:6:12)
(anonymous) (test.js:3:10)
EmptyName - EmptyStack
userFunction (test.js:1:36)
-- <empty> --
runWithEmptyName (utils.js:6:12)
inner (test.js:2:28)
EmptyName - EmptyNameEmptyStack
userFunction (test.js:1:36)
-- <empty> --
runWithEmptyName (utils.js:6:12)
inner (test.js:2:28)
EmptyName - External
userFunction (test.js:1:36)
-- <empty> --
runWithEmptyName (utils.js:6:12)
inner (test.js:2:28)
runWithRegular (utils.js:21:4)
<external stack>
EmptyStack
userFunction (test.js:1:36)
EmptyStack - Regular
userFunction (test.js:1:36)
-- inner async --
-- outer async --
runWithRegular (utils.js:2:12)
(anonymous) (test.js:3:10)
EmptyStack - EmptyName
userFunction (test.js:1:36)
-- inner async --
-- <empty> --
runWithEmptyName (utils.js:6:12)
(anonymous) (test.js:3:10)
EmptyStack - EmptyStack
userFunction (test.js:1:36)
EmptyStack - EmptyNameEmptyStack
userFunction (test.js:1:36)
EmptyStack - External
userFunction (test.js:1:36)
-- inner async --
<external stack>
EmptyNameEmptyStack
userFunction (test.js:1:36)
EmptyNameEmptyStack - Regular
userFunction (test.js:1:36)
-- outer async --
runWithRegular (utils.js:2:12)
(anonymous) (test.js:3:10)
EmptyNameEmptyStack - EmptyName
userFunction (test.js:1:36)
-- <empty> --
runWithEmptyName (utils.js:6:12)
(anonymous) (test.js:3:10)
EmptyNameEmptyStack - EmptyStack
userFunction (test.js:1:36)
EmptyNameEmptyStack - EmptyNameEmptyStack
userFunction (test.js:1:36)
EmptyNameEmptyStack - External
userFunction (test.js:1:36)
-- <empty> --
<external stack>
External
userFunction (test.js:1:36)
runWithRegular (utils.js:21:4)
<external stack>
// Copyright 2017 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.
let {session, contextGroup, Protocol} = InspectorTest.start(
'Tests different combinations of async stacks in chains.');
contextGroup.addScript(`
function runWithRegular(f, name) {
inspector.scheduleWithAsyncStack(f, name, false);
}
function runWithEmptyName(f) {
inspector.scheduleWithAsyncStack(f, '', false);
}
function runWithEmptyStack(f, name) {
inspector.scheduleWithAsyncStack(f, name, true);
}
function runWithEmptyNameEmptyStack(f) {
inspector.scheduleWithAsyncStack(f, '', true);
}
function runWithExternal(f) {
const id = inspector.storeCurrentStackTrace('external');
runWithRegular(() => {
inspector.externalAsyncTaskStarted(id);
f();
inspector.externalAsyncTaskFinished(id);
}, 'not-used-async');
}
function runWithNone(f) {
f();
}
//# sourceURL=utils.js`);
session.setupScriptMap();
(async function test() {
Protocol.Debugger.enable();
Protocol.Debugger.setAsyncCallStackDepth({maxDepth: 128});
const first = ['Regular', 'EmptyName', 'EmptyStack', 'EmptyNameEmptyStack', 'External']
const second = ['None', 'Regular', 'EmptyName', 'EmptyStack', 'EmptyNameEmptyStack', 'External']
for (const stack1 of first) {
for (const stack2 of second) {
if (stack1 === 'External' && stack2 !== 'None') continue;
InspectorTest.log(stack2 === 'None' ? stack1 : `${stack1} - ${stack2}`);
Protocol.Runtime.evaluate({
expression: `
var userFunction = () => {debugger};
var inner = () => runWith${stack1}(userFunction, 'inner async');
runWith${stack2}(inner, 'outer async');
//# sourceURL=test.js`
});
await pauseAndDumpStack();
}
}
await Protocol.Debugger.disable();
InspectorTest.completeTest();
})();
async function pauseAndDumpStack() {
const {params:{callFrames, asyncStackTrace, asyncStackTraceId}}
= await Protocol.Debugger.oncePaused();
session.logCallFrames(callFrames);
if (asyncStackTrace)
session.logAsyncStackTrace(asyncStackTrace);
if (asyncStackTraceId)
InspectorTest.log(' <external stack>');
InspectorTest.log('');
return Protocol.Debugger.resume();
}
...@@ -175,7 +175,8 @@ class SendMessageToBackendTask : public TaskRunner::Task { ...@@ -175,7 +175,8 @@ class SendMessageToBackendTask : public TaskRunner::Task {
v8::internal::Vector<uint16_t> message_; v8::internal::Vector<uint16_t> message_;
}; };
void RunAsyncTask(TaskRunner* task_runner, const char* task_name, void RunAsyncTask(TaskRunner* task_runner,
const v8_inspector::StringView& task_name,
TaskRunner::Task* task) { TaskRunner::Task* task) {
class AsyncTask : public TaskRunner::Task { class AsyncTask : public TaskRunner::Task {
public: public:
...@@ -193,10 +194,7 @@ void RunAsyncTask(TaskRunner* task_runner, const char* task_name, ...@@ -193,10 +194,7 @@ void RunAsyncTask(TaskRunner* task_runner, const char* task_name,
DISALLOW_COPY_AND_ASSIGN(AsyncTask); DISALLOW_COPY_AND_ASSIGN(AsyncTask);
}; };
task_runner->data()->AsyncTaskScheduled( task_runner->data()->AsyncTaskScheduled(task_name, task, false);
v8_inspector::StringView(reinterpret_cast<const uint8_t*>(task_name),
strlen(task_name)),
task, false);
task_runner->Append(new AsyncTask(task)); task_runner->Append(new AsyncTask(task));
} }
...@@ -626,13 +624,16 @@ class SetTimeoutExtension : public IsolateData::SetupGlobalTask { ...@@ -626,13 +624,16 @@ class SetTimeoutExtension : public IsolateData::SetupGlobalTask {
v8::Local<v8::Context> context = isolate->GetCurrentContext(); v8::Local<v8::Context> context = isolate->GetCurrentContext();
IsolateData* data = IsolateData::FromContext(context); IsolateData* data = IsolateData::FromContext(context);
int context_group_id = data->GetContextGroupId(context); int context_group_id = data->GetContextGroupId(context);
const char* task_name = "setTimeout";
v8_inspector::StringView task_name_view(
reinterpret_cast<const uint8_t*>(task_name), strlen(task_name));
if (args[0]->IsFunction()) { if (args[0]->IsFunction()) {
RunAsyncTask(data->task_runner(), "setTimeout", RunAsyncTask(data->task_runner(), task_name_view,
new SetTimeoutTask(context_group_id, isolate, new SetTimeoutTask(context_group_id, isolate,
v8::Local<v8::Function>::Cast(args[0]))); v8::Local<v8::Function>::Cast(args[0])));
} else { } else {
RunAsyncTask( RunAsyncTask(
data->task_runner(), "setTimeout", data->task_runner(), task_name_view,
new ExecuteStringTask( new ExecuteStringTask(
context_group_id, ToVector(args[0].As<v8::String>()), context_group_id, ToVector(args[0].As<v8::String>()),
v8::String::Empty(isolate), v8::Integer::New(isolate, 0), v8::String::Empty(isolate), v8::Integer::New(isolate, 0),
...@@ -703,6 +704,9 @@ class InspectorExtension : public IsolateData::SetupGlobalTask { ...@@ -703,6 +704,9 @@ class InspectorExtension : public IsolateData::SetupGlobalTask {
ToV8String(isolate, "externalAsyncTaskFinished"), ToV8String(isolate, "externalAsyncTaskFinished"),
v8::FunctionTemplate::New( v8::FunctionTemplate::New(
isolate, &InspectorExtension::ExternalAsyncTaskFinished)); isolate, &InspectorExtension::ExternalAsyncTaskFinished));
inspector->Set(ToV8String(isolate, "scheduleWithAsyncStack"),
v8::FunctionTemplate::New(
isolate, &InspectorExtension::ScheduleWithAsyncStack));
global->Set(ToV8String(isolate, "inspector"), inspector); global->Set(ToV8String(isolate, "inspector"), inspector);
} }
...@@ -926,6 +930,33 @@ class InspectorExtension : public IsolateData::SetupGlobalTask { ...@@ -926,6 +930,33 @@ class InspectorExtension : public IsolateData::SetupGlobalTask {
args[0].As<v8::ArrayBuffer>()->GetContents().Data()); args[0].As<v8::ArrayBuffer>()->GetContents().Data());
data->ExternalAsyncTaskFinished(*id); data->ExternalAsyncTaskFinished(*id);
} }
static void ScheduleWithAsyncStack(
const v8::FunctionCallbackInfo<v8::Value>& args) {
if (args.Length() != 3 || !args[0]->IsFunction() || !args[1]->IsString() ||
!args[2]->IsBoolean()) {
fprintf(stderr,
"Internal error: scheduleWithAsyncStack(function, "
"'task-name', with_empty_stack).");
Exit();
}
v8::Isolate* isolate = args.GetIsolate();
v8::Local<v8::Context> context = isolate->GetCurrentContext();
IsolateData* data = IsolateData::FromContext(context);
int context_group_id = data->GetContextGroupId(context);
bool with_empty_stack = args[2].As<v8::Boolean>()->Value();
if (with_empty_stack) context->Exit();
v8::internal::Vector<uint16_t> task_name =
ToVector(args[1].As<v8::String>());
v8_inspector::StringView task_name_view(task_name.start(),
task_name.length());
RunAsyncTask(data->task_runner(), task_name_view,
new SetTimeoutTask(context_group_id, isolate,
v8::Local<v8::Function>::Cast(args[0])));
if (with_empty_stack) context->Enter();
}
}; };
} // namespace } // namespace
......
...@@ -301,13 +301,9 @@ InspectorTest.Session = class { ...@@ -301,13 +301,9 @@ InspectorTest.Session = class {
logAsyncStackTrace(asyncStackTrace) { logAsyncStackTrace(asyncStackTrace) {
while (asyncStackTrace) { while (asyncStackTrace) {
if (asyncStackTrace.promiseCreationFrame) { InspectorTest.log(`-- ${asyncStackTrace.description || '<empty>'} --`);
var frame = asyncStackTrace.promiseCreationFrame;
InspectorTest.log(`-- ${asyncStackTrace.description} (${frame.url}:${frame.lineNumber}:${frame.columnNumber})--`);
} else {
InspectorTest.log(`-- ${asyncStackTrace.description} --`);
}
this.logCallFrames(asyncStackTrace.callFrames); this.logCallFrames(asyncStackTrace.callFrames);
if (asyncStackTrace.parentId) InspectorTest.log(' <external stack>');
asyncStackTrace = asyncStackTrace.parent; asyncStackTrace = asyncStackTrace.parent;
} }
} }
......
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