[inspector] avoid cloning of async call chains
- separated V8StackTraceImpl and AsyncStackTrace,
- V8Debugger owns all AsyncStackTrace and cleanup half of them when limit is reached (first created - first cleaned),
- V8StackTraceImpl, AsyncStackTrace and async-task-related tables in V8Debugger have weak reference to other async stack traces.
- async tasks are cleared with related async stacks.
BUG=v8:6189
R=dgozman@chromium.org
Review-Url: https://codereview.chromium.org/2816043006
Cr-Original-Commit-Position: refs/heads/master@{#44670}
Committed: 1bca73bc83
Review-Url: https://codereview.chromium.org/2816043006
Cr-Commit-Position: refs/heads/master@{#44694}
This commit is contained in:
parent
7fcf658a7b
commit
38be4a17c1
@ -263,7 +263,7 @@ void V8Console::countCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
|
|||||||
String16 identifier;
|
String16 identifier;
|
||||||
if (title.isEmpty()) {
|
if (title.isEmpty()) {
|
||||||
std::unique_ptr<V8StackTraceImpl> stackTrace =
|
std::unique_ptr<V8StackTraceImpl> stackTrace =
|
||||||
V8StackTraceImpl::capture(m_inspector->debugger(), 0, 1);
|
V8StackTraceImpl::capture(m_inspector->debugger(), helper.groupId(), 1);
|
||||||
if (stackTrace && !stackTrace->isEmpty()) {
|
if (stackTrace && !stackTrace->isEmpty()) {
|
||||||
identifier = toString16(stackTrace->topSourceURL()) + ":" +
|
identifier = toString16(stackTrace->topSourceURL()) + ":" +
|
||||||
String16::fromInteger(stackTrace->topLineNumber());
|
String16::fromInteger(stackTrace->topLineNumber());
|
||||||
|
@ -34,7 +34,6 @@ using protocol::Debugger::BreakpointId;
|
|||||||
using protocol::Debugger::CallFrame;
|
using protocol::Debugger::CallFrame;
|
||||||
using protocol::Runtime::ExceptionDetails;
|
using protocol::Runtime::ExceptionDetails;
|
||||||
using protocol::Runtime::ScriptId;
|
using protocol::Runtime::ScriptId;
|
||||||
using protocol::Runtime::StackTrace;
|
|
||||||
using protocol::Runtime::RemoteObject;
|
using protocol::Runtime::RemoteObject;
|
||||||
|
|
||||||
namespace DebuggerAgentState {
|
namespace DebuggerAgentState {
|
||||||
@ -598,7 +597,8 @@ Response V8DebuggerAgentImpl::searchInContent(
|
|||||||
Response V8DebuggerAgentImpl::setScriptSource(
|
Response V8DebuggerAgentImpl::setScriptSource(
|
||||||
const String16& scriptId, const String16& newContent, Maybe<bool> dryRun,
|
const String16& scriptId, const String16& newContent, Maybe<bool> dryRun,
|
||||||
Maybe<protocol::Array<protocol::Debugger::CallFrame>>* newCallFrames,
|
Maybe<protocol::Array<protocol::Debugger::CallFrame>>* newCallFrames,
|
||||||
Maybe<bool>* stackChanged, Maybe<StackTrace>* asyncStackTrace,
|
Maybe<bool>* stackChanged,
|
||||||
|
Maybe<protocol::Runtime::StackTrace>* asyncStackTrace,
|
||||||
Maybe<protocol::Runtime::ExceptionDetails>* optOutCompileError) {
|
Maybe<protocol::Runtime::ExceptionDetails>* optOutCompileError) {
|
||||||
if (!enabled()) return Response::Error(kDebuggerNotEnabled);
|
if (!enabled()) return Response::Error(kDebuggerNotEnabled);
|
||||||
|
|
||||||
@ -631,7 +631,7 @@ Response V8DebuggerAgentImpl::setScriptSource(
|
|||||||
Response V8DebuggerAgentImpl::restartFrame(
|
Response V8DebuggerAgentImpl::restartFrame(
|
||||||
const String16& callFrameId,
|
const String16& callFrameId,
|
||||||
std::unique_ptr<Array<CallFrame>>* newCallFrames,
|
std::unique_ptr<Array<CallFrame>>* newCallFrames,
|
||||||
Maybe<StackTrace>* asyncStackTrace) {
|
Maybe<protocol::Runtime::StackTrace>* asyncStackTrace) {
|
||||||
if (!isPaused()) return Response::Error(kDebuggerNotPaused);
|
if (!isPaused()) return Response::Error(kDebuggerNotPaused);
|
||||||
InjectedScript::CallFrameScope scope(m_inspector, m_session->contextGroupId(),
|
InjectedScript::CallFrameScope scope(m_inspector, m_session->contextGroupId(),
|
||||||
callFrameId);
|
callFrameId);
|
||||||
@ -1028,9 +1028,14 @@ Response V8DebuggerAgentImpl::currentCallFrames(
|
|||||||
return Response::OK();
|
return Response::OK();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<StackTrace> V8DebuggerAgentImpl::currentAsyncStackTrace() {
|
std::unique_ptr<protocol::Runtime::StackTrace>
|
||||||
if (!isPaused()) return nullptr;
|
V8DebuggerAgentImpl::currentAsyncStackTrace() {
|
||||||
return V8StackTraceImpl::buildInspectorObjectForTail(m_debugger);
|
std::shared_ptr<AsyncStackTrace> asyncParent =
|
||||||
|
m_debugger->currentAsyncParent();
|
||||||
|
if (!asyncParent) return nullptr;
|
||||||
|
return asyncParent->buildInspectorObject(
|
||||||
|
m_debugger->currentAsyncCreation().get(),
|
||||||
|
m_debugger->maxAsyncCallChainDepth() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool V8DebuggerAgentImpl::isPaused() const { return m_debugger->isPaused(); }
|
bool V8DebuggerAgentImpl::isPaused() const { return m_debugger->isPaused(); }
|
||||||
|
@ -23,7 +23,6 @@ class V8DebuggerScript;
|
|||||||
class V8InspectorImpl;
|
class V8InspectorImpl;
|
||||||
class V8InspectorSessionImpl;
|
class V8InspectorSessionImpl;
|
||||||
class V8Regex;
|
class V8Regex;
|
||||||
class V8StackTraceImpl;
|
|
||||||
|
|
||||||
using protocol::Maybe;
|
using protocol::Maybe;
|
||||||
using protocol::Response;
|
using protocol::Response;
|
||||||
|
@ -21,10 +21,7 @@ namespace v8_inspector {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
// Based on DevTools frontend measurement, with asyncCallStackDepth = 4,
|
static const int kMaxAsyncTaskStacks = 1024 * 1024;
|
||||||
// average async call stack tail requires ~1 Kb. Let's reserve ~ 128 Mb
|
|
||||||
// for async stacks.
|
|
||||||
static const int kMaxAsyncTaskStacks = 128 * 1024;
|
|
||||||
|
|
||||||
inline v8::Local<v8::Boolean> v8Boolean(bool value, v8::Isolate* isolate) {
|
inline v8::Local<v8::Boolean> v8Boolean(bool value, v8::Isolate* isolate) {
|
||||||
return value ? v8::True(isolate) : v8::False(isolate);
|
return value ? v8::True(isolate) : v8::False(isolate);
|
||||||
@ -167,7 +164,6 @@ V8Debugger::V8Debugger(v8::Isolate* isolate, V8InspectorImpl* inspector)
|
|||||||
m_runningNestedMessageLoop(false),
|
m_runningNestedMessageLoop(false),
|
||||||
m_ignoreScriptParsedEventsCounter(0),
|
m_ignoreScriptParsedEventsCounter(0),
|
||||||
m_maxAsyncCallStacks(kMaxAsyncTaskStacks),
|
m_maxAsyncCallStacks(kMaxAsyncTaskStacks),
|
||||||
m_lastTaskId(0),
|
|
||||||
m_maxAsyncCallStackDepth(0),
|
m_maxAsyncCallStackDepth(0),
|
||||||
m_pauseOnExceptionsState(v8::debug::NoBreakOnException),
|
m_pauseOnExceptionsState(v8::debug::NoBreakOnException),
|
||||||
m_wasmTranslation(isolate) {}
|
m_wasmTranslation(isolate) {}
|
||||||
@ -662,10 +658,6 @@ void V8Debugger::PromiseEventOccurred(v8::debug::PromiseDebugActionType type,
|
|||||||
case v8::debug::kDebugEnqueuePromiseReject:
|
case v8::debug::kDebugEnqueuePromiseReject:
|
||||||
asyncTaskScheduledForStack("Promise.reject", task, true);
|
asyncTaskScheduledForStack("Promise.reject", task, true);
|
||||||
break;
|
break;
|
||||||
case v8::debug::kDebugPromiseCollected:
|
|
||||||
asyncTaskCanceledForStack(task);
|
|
||||||
asyncTaskCanceledForStepping(task);
|
|
||||||
break;
|
|
||||||
case v8::debug::kDebugWillHandle:
|
case v8::debug::kDebugWillHandle:
|
||||||
asyncTaskStartedForStack(task);
|
asyncTaskStartedForStack(task);
|
||||||
asyncTaskStartedForStepping(task);
|
asyncTaskStartedForStepping(task);
|
||||||
@ -674,17 +666,20 @@ void V8Debugger::PromiseEventOccurred(v8::debug::PromiseDebugActionType type,
|
|||||||
asyncTaskFinishedForStack(task);
|
asyncTaskFinishedForStack(task);
|
||||||
asyncTaskFinishedForStepping(task);
|
asyncTaskFinishedForStepping(task);
|
||||||
break;
|
break;
|
||||||
|
case v8::debug::kDebugPromiseCollected:
|
||||||
|
asyncTaskCanceledForStack(task);
|
||||||
|
asyncTaskCanceledForStepping(task);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
V8StackTraceImpl* V8Debugger::currentAsyncCallChain() {
|
std::shared_ptr<AsyncStackTrace> V8Debugger::currentAsyncParent() {
|
||||||
if (!m_currentStacks.size()) return nullptr;
|
return m_currentAsyncParent.empty() ? nullptr : m_currentAsyncParent.back();
|
||||||
return m_currentStacks.back().get();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
V8StackTraceImpl* V8Debugger::currentAsyncTaskCreationStack() {
|
std::shared_ptr<AsyncStackTrace> V8Debugger::currentAsyncCreation() {
|
||||||
if (!m_currentCreationStacks.size()) return nullptr;
|
return m_currentAsyncCreation.empty() ? nullptr
|
||||||
return m_currentCreationStacks.back().get();
|
: m_currentAsyncCreation.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
void V8Debugger::compileDebuggerScript() {
|
void V8Debugger::compileDebuggerScript() {
|
||||||
@ -825,8 +820,8 @@ v8::MaybeLocal<v8::Array> V8Debugger::internalProperties(
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<V8StackTraceImpl> V8Debugger::createStackTrace(
|
std::unique_ptr<V8StackTraceImpl> V8Debugger::createStackTrace(
|
||||||
v8::Local<v8::StackTrace> stackTrace) {
|
v8::Local<v8::StackTrace> v8StackTrace) {
|
||||||
return V8StackTraceImpl::create(this, currentContextGroupId(), stackTrace,
|
return V8StackTraceImpl::create(this, currentContextGroupId(), v8StackTrace,
|
||||||
V8StackTraceImpl::maxCallStackSizeToCapture);
|
V8StackTraceImpl::maxCallStackSizeToCapture);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -847,31 +842,18 @@ void V8Debugger::setAsyncCallStackDepth(V8DebuggerAgentImpl* agent, int depth) {
|
|||||||
if (!maxAsyncCallStackDepth) allAsyncTasksCanceled();
|
if (!maxAsyncCallStackDepth) allAsyncTasksCanceled();
|
||||||
}
|
}
|
||||||
|
|
||||||
void V8Debugger::registerAsyncTaskIfNeeded(void* task) {
|
|
||||||
if (m_taskToId.find(task) != m_taskToId.end()) return;
|
|
||||||
|
|
||||||
int id = ++m_lastTaskId;
|
|
||||||
m_taskToId[task] = id;
|
|
||||||
m_idToTask[id] = task;
|
|
||||||
if (static_cast<int>(m_idToTask.size()) > m_maxAsyncCallStacks) {
|
|
||||||
void* taskToRemove = m_idToTask.begin()->second;
|
|
||||||
asyncTaskCanceledForStack(taskToRemove);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void V8Debugger::asyncTaskCreatedForStack(void* task, void* parentTask) {
|
void V8Debugger::asyncTaskCreatedForStack(void* task, void* parentTask) {
|
||||||
if (!m_maxAsyncCallStackDepth) return;
|
if (!m_maxAsyncCallStackDepth) return;
|
||||||
if (parentTask) m_parentTask[task] = parentTask;
|
if (parentTask) m_parentTask[task] = parentTask;
|
||||||
v8::HandleScope scope(m_isolate);
|
v8::HandleScope scope(m_isolate);
|
||||||
// We don't need to pass context group id here because we get this callback
|
std::shared_ptr<AsyncStackTrace> asyncCreation =
|
||||||
// from V8 for promise events only.
|
AsyncStackTrace::capture(this, currentContextGroupId(), String16(), 1);
|
||||||
// Passing one as maxStackSize forces no async chain for the new stack and
|
// Passing one as maxStackSize forces no async chain for the new stack.
|
||||||
// allows us to not grow exponentially.
|
if (asyncCreation && !asyncCreation->isEmpty()) {
|
||||||
std::unique_ptr<V8StackTraceImpl> creationStack =
|
m_asyncTaskCreationStacks[task] = asyncCreation;
|
||||||
V8StackTraceImpl::capture(this, 0, 1, String16());
|
m_allAsyncStacks.push_back(asyncCreation);
|
||||||
if (creationStack && !creationStack->isEmpty()) {
|
++m_asyncStacksCount;
|
||||||
m_asyncTaskCreationStacks[task] = std::move(creationStack);
|
collectOldAsyncStacksIfNeeded();
|
||||||
registerAsyncTaskIfNeeded(task);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -900,13 +882,16 @@ void V8Debugger::asyncTaskScheduledForStack(const String16& taskName,
|
|||||||
void* task, bool recurring) {
|
void* task, bool recurring) {
|
||||||
if (!m_maxAsyncCallStackDepth) return;
|
if (!m_maxAsyncCallStackDepth) return;
|
||||||
v8::HandleScope scope(m_isolate);
|
v8::HandleScope scope(m_isolate);
|
||||||
std::unique_ptr<V8StackTraceImpl> chain = V8StackTraceImpl::capture(
|
std::shared_ptr<AsyncStackTrace> asyncStack =
|
||||||
this, currentContextGroupId(),
|
AsyncStackTrace::capture(this, currentContextGroupId(), taskName,
|
||||||
V8StackTraceImpl::maxCallStackSizeToCapture, taskName);
|
V8StackTraceImpl::maxCallStackSizeToCapture);
|
||||||
if (chain) {
|
if (asyncStack) {
|
||||||
m_asyncTaskStacks[task] = std::move(chain);
|
m_asyncTaskStacks[task] = asyncStack;
|
||||||
if (recurring) m_recurringTasks.insert(task);
|
if (recurring) m_recurringTasks.insert(task);
|
||||||
registerAsyncTaskIfNeeded(task);
|
|
||||||
|
m_allAsyncStacks.push_back(asyncStack);
|
||||||
|
++m_asyncStacksCount;
|
||||||
|
collectOldAsyncStacksIfNeeded();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -916,10 +901,6 @@ void V8Debugger::asyncTaskCanceledForStack(void* task) {
|
|||||||
m_recurringTasks.erase(task);
|
m_recurringTasks.erase(task);
|
||||||
m_parentTask.erase(task);
|
m_parentTask.erase(task);
|
||||||
m_asyncTaskCreationStacks.erase(task);
|
m_asyncTaskCreationStacks.erase(task);
|
||||||
auto it = m_taskToId.find(task);
|
|
||||||
if (it == m_taskToId.end()) return;
|
|
||||||
m_idToTask.erase(it->second);
|
|
||||||
m_taskToId.erase(it);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void V8Debugger::asyncTaskStartedForStack(void* task) {
|
void V8Debugger::asyncTaskStartedForStack(void* task) {
|
||||||
@ -935,28 +916,28 @@ void V8Debugger::asyncTaskStartedForStack(void* task) {
|
|||||||
// - asyncTaskCanceled <-- canceled before finished
|
// - asyncTaskCanceled <-- canceled before finished
|
||||||
// <-- async stack requested here -->
|
// <-- async stack requested here -->
|
||||||
// - asyncTaskFinished
|
// - asyncTaskFinished
|
||||||
std::unique_ptr<V8StackTraceImpl> stack;
|
std::shared_ptr<AsyncStackTrace> asyncParent;
|
||||||
if (stackIt != m_asyncTaskStacks.end() && stackIt->second)
|
if (stackIt != m_asyncTaskStacks.end()) asyncParent = stackIt->second.lock();
|
||||||
stack = stackIt->second->cloneImpl();
|
|
||||||
auto itCreation = m_asyncTaskCreationStacks.find(task);
|
auto itCreation = m_asyncTaskCreationStacks.find(task);
|
||||||
if (stack && itCreation != m_asyncTaskCreationStacks.end()) {
|
if (asyncParent && itCreation != m_asyncTaskCreationStacks.end()) {
|
||||||
m_currentCreationStacks.push_back(itCreation->second->cloneImpl());
|
m_currentAsyncCreation.push_back(itCreation->second.lock());
|
||||||
} else {
|
} else {
|
||||||
m_currentCreationStacks.push_back(nullptr);
|
m_currentAsyncCreation.push_back(nullptr);
|
||||||
}
|
}
|
||||||
m_currentStacks.push_back(std::move(stack));
|
m_currentAsyncParent.push_back(asyncParent);
|
||||||
}
|
}
|
||||||
|
|
||||||
void V8Debugger::asyncTaskFinishedForStack(void* task) {
|
void V8Debugger::asyncTaskFinishedForStack(void* task) {
|
||||||
if (!m_maxAsyncCallStackDepth) return;
|
if (!m_maxAsyncCallStackDepth) return;
|
||||||
// We could start instrumenting half way and the stack is empty.
|
// We could start instrumenting half way and the stack is empty.
|
||||||
if (!m_currentStacks.size()) return;
|
if (!m_currentTasks.size()) return;
|
||||||
DCHECK(m_currentTasks.back() == task);
|
DCHECK(m_currentTasks.back() == task);
|
||||||
m_currentTasks.pop_back();
|
m_currentTasks.pop_back();
|
||||||
|
|
||||||
DCHECK(m_currentStacks.size() == m_currentCreationStacks.size());
|
DCHECK(m_currentAsyncParent.size() == m_currentAsyncCreation.size());
|
||||||
m_currentStacks.pop_back();
|
m_currentAsyncParent.pop_back();
|
||||||
m_currentCreationStacks.pop_back();
|
m_currentAsyncCreation.pop_back();
|
||||||
|
|
||||||
if (m_recurringTasks.find(task) == m_recurringTasks.end()) {
|
if (m_recurringTasks.find(task) == m_recurringTasks.end()) {
|
||||||
asyncTaskCanceledForStack(task);
|
asyncTaskCanceledForStack(task);
|
||||||
}
|
}
|
||||||
@ -993,14 +974,14 @@ void V8Debugger::asyncTaskCanceledForStepping(void* task) {
|
|||||||
void V8Debugger::allAsyncTasksCanceled() {
|
void V8Debugger::allAsyncTasksCanceled() {
|
||||||
m_asyncTaskStacks.clear();
|
m_asyncTaskStacks.clear();
|
||||||
m_recurringTasks.clear();
|
m_recurringTasks.clear();
|
||||||
m_currentStacks.clear();
|
m_currentAsyncParent.clear();
|
||||||
m_currentCreationStacks.clear();
|
m_currentAsyncCreation.clear();
|
||||||
m_currentTasks.clear();
|
m_currentTasks.clear();
|
||||||
m_parentTask.clear();
|
m_parentTask.clear();
|
||||||
m_asyncTaskCreationStacks.clear();
|
m_asyncTaskCreationStacks.clear();
|
||||||
m_idToTask.clear();
|
|
||||||
m_taskToId.clear();
|
m_allAsyncStacks.clear();
|
||||||
m_lastTaskId = 0;
|
m_asyncStacksCount = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void V8Debugger::muteScriptParsedEvents() {
|
void V8Debugger::muteScriptParsedEvents() {
|
||||||
@ -1020,11 +1001,10 @@ std::unique_ptr<V8StackTraceImpl> V8Debugger::captureStackTrace(
|
|||||||
int contextGroupId = currentContextGroupId();
|
int contextGroupId = currentContextGroupId();
|
||||||
if (!contextGroupId) return nullptr;
|
if (!contextGroupId) return nullptr;
|
||||||
|
|
||||||
size_t stackSize =
|
int stackSize = 1;
|
||||||
fullStack ? V8StackTraceImpl::maxCallStackSizeToCapture : 1;
|
if (fullStack || m_inspector->enabledRuntimeAgentForGroup(contextGroupId)) {
|
||||||
if (m_inspector->enabledRuntimeAgentForGroup(contextGroupId))
|
|
||||||
stackSize = V8StackTraceImpl::maxCallStackSizeToCapture;
|
stackSize = V8StackTraceImpl::maxCallStackSizeToCapture;
|
||||||
|
}
|
||||||
return V8StackTraceImpl::capture(this, contextGroupId, stackSize);
|
return V8StackTraceImpl::capture(this, contextGroupId, stackSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1033,4 +1013,30 @@ int V8Debugger::currentContextGroupId() {
|
|||||||
return m_inspector->contextGroupId(m_isolate->GetCurrentContext());
|
return m_inspector->contextGroupId(m_isolate->GetCurrentContext());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void V8Debugger::collectOldAsyncStacksIfNeeded() {
|
||||||
|
if (m_asyncStacksCount <= m_maxAsyncCallStacks) return;
|
||||||
|
int halfOfLimitRoundedUp =
|
||||||
|
m_maxAsyncCallStacks / 2 + m_maxAsyncCallStacks % 2;
|
||||||
|
while (m_asyncStacksCount > halfOfLimitRoundedUp) {
|
||||||
|
m_allAsyncStacks.pop_front();
|
||||||
|
--m_asyncStacksCount;
|
||||||
|
}
|
||||||
|
removeOldAsyncTasks(m_asyncTaskStacks);
|
||||||
|
removeOldAsyncTasks(m_asyncTaskCreationStacks);
|
||||||
|
}
|
||||||
|
|
||||||
|
void V8Debugger::removeOldAsyncTasks(AsyncTaskToStackTrace& map) {
|
||||||
|
AsyncTaskToStackTrace cleanCopy;
|
||||||
|
for (auto it : map) {
|
||||||
|
if (!it.second.expired()) cleanCopy.insert(it);
|
||||||
|
}
|
||||||
|
map.swap(cleanCopy);
|
||||||
|
}
|
||||||
|
|
||||||
|
void V8Debugger::setMaxAsyncTaskStacksForTest(int limit) {
|
||||||
|
m_maxAsyncCallStacks = 0;
|
||||||
|
collectOldAsyncStacksIfNeeded();
|
||||||
|
m_maxAsyncCallStacks = limit;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace v8_inspector
|
} // namespace v8_inspector
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#ifndef V8_INSPECTOR_V8DEBUGGER_H_
|
#ifndef V8_INSPECTOR_V8DEBUGGER_H_
|
||||||
#define V8_INSPECTOR_V8DEBUGGER_H_
|
#define V8_INSPECTOR_V8DEBUGGER_H_
|
||||||
|
|
||||||
|
#include <list>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "src/base/macros.h"
|
#include "src/base/macros.h"
|
||||||
@ -20,7 +21,9 @@
|
|||||||
|
|
||||||
namespace v8_inspector {
|
namespace v8_inspector {
|
||||||
|
|
||||||
|
class AsyncStackTrace;
|
||||||
struct ScriptBreakpoint;
|
struct ScriptBreakpoint;
|
||||||
|
class V8Debugger;
|
||||||
class V8DebuggerAgentImpl;
|
class V8DebuggerAgentImpl;
|
||||||
class V8InspectorImpl;
|
class V8InspectorImpl;
|
||||||
class V8StackTraceImpl;
|
class V8StackTraceImpl;
|
||||||
@ -35,6 +38,7 @@ class V8Debugger : public v8::debug::DebugDelegate {
|
|||||||
~V8Debugger();
|
~V8Debugger();
|
||||||
|
|
||||||
bool enabled() const;
|
bool enabled() const;
|
||||||
|
v8::Isolate* isolate() const { return m_isolate; }
|
||||||
|
|
||||||
String16 setBreakpoint(const ScriptBreakpoint&, int* actualLineNumber,
|
String16 setBreakpoint(const ScriptBreakpoint&, int* actualLineNumber,
|
||||||
int* actualColumnNumber);
|
int* actualColumnNumber);
|
||||||
@ -76,9 +80,11 @@ class V8Debugger : public v8::debug::DebugDelegate {
|
|||||||
v8::Local<v8::Context> pausedContext() { return m_pausedContext; }
|
v8::Local<v8::Context> pausedContext() { return m_pausedContext; }
|
||||||
|
|
||||||
int maxAsyncCallChainDepth() { return m_maxAsyncCallStackDepth; }
|
int maxAsyncCallChainDepth() { return m_maxAsyncCallStackDepth; }
|
||||||
V8StackTraceImpl* currentAsyncCallChain();
|
|
||||||
V8StackTraceImpl* currentAsyncTaskCreationStack();
|
|
||||||
void setAsyncCallStackDepth(V8DebuggerAgentImpl*, int);
|
void setAsyncCallStackDepth(V8DebuggerAgentImpl*, int);
|
||||||
|
|
||||||
|
std::shared_ptr<AsyncStackTrace> currentAsyncParent();
|
||||||
|
std::shared_ptr<AsyncStackTrace> currentAsyncCreation();
|
||||||
|
|
||||||
std::unique_ptr<V8StackTraceImpl> createStackTrace(v8::Local<v8::StackTrace>);
|
std::unique_ptr<V8StackTraceImpl> createStackTrace(v8::Local<v8::StackTrace>);
|
||||||
std::unique_ptr<V8StackTraceImpl> captureStackTrace(bool fullStack);
|
std::unique_ptr<V8StackTraceImpl> captureStackTrace(bool fullStack);
|
||||||
|
|
||||||
@ -99,7 +105,7 @@ class V8Debugger : public v8::debug::DebugDelegate {
|
|||||||
|
|
||||||
WasmTranslation* wasmTranslation() { return &m_wasmTranslation; }
|
WasmTranslation* wasmTranslation() { return &m_wasmTranslation; }
|
||||||
|
|
||||||
void setMaxAsyncTaskStacksForTest(int limit) { m_maxAsyncCallStacks = limit; }
|
void setMaxAsyncTaskStacksForTest(int limit);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void compileDebuggerScript();
|
void compileDebuggerScript();
|
||||||
@ -144,8 +150,6 @@ class V8Debugger : public v8::debug::DebugDelegate {
|
|||||||
void asyncTaskFinishedForStepping(void* task);
|
void asyncTaskFinishedForStepping(void* task);
|
||||||
void asyncTaskCanceledForStepping(void* task);
|
void asyncTaskCanceledForStepping(void* task);
|
||||||
|
|
||||||
void registerAsyncTaskIfNeeded(void* task);
|
|
||||||
|
|
||||||
// v8::debug::DebugEventListener implementation.
|
// v8::debug::DebugEventListener implementation.
|
||||||
void PromiseEventOccurred(v8::debug::PromiseDebugActionType type, int id,
|
void PromiseEventOccurred(v8::debug::PromiseDebugActionType type, int id,
|
||||||
int parentId, bool createdByUser) override;
|
int parentId, bool createdByUser) override;
|
||||||
@ -178,18 +182,24 @@ class V8Debugger : public v8::debug::DebugDelegate {
|
|||||||
int m_targetContextGroupId = 0;
|
int m_targetContextGroupId = 0;
|
||||||
|
|
||||||
using AsyncTaskToStackTrace =
|
using AsyncTaskToStackTrace =
|
||||||
protocol::HashMap<void*, std::unique_ptr<V8StackTraceImpl>>;
|
protocol::HashMap<void*, std::weak_ptr<AsyncStackTrace>>;
|
||||||
AsyncTaskToStackTrace m_asyncTaskStacks;
|
AsyncTaskToStackTrace m_asyncTaskStacks;
|
||||||
AsyncTaskToStackTrace m_asyncTaskCreationStacks;
|
AsyncTaskToStackTrace m_asyncTaskCreationStacks;
|
||||||
int m_maxAsyncCallStacks;
|
int m_maxAsyncCallStacks;
|
||||||
std::map<int, void*> m_idToTask;
|
|
||||||
std::unordered_map<void*, int> m_taskToId;
|
|
||||||
int m_lastTaskId;
|
|
||||||
protocol::HashSet<void*> m_recurringTasks;
|
protocol::HashSet<void*> m_recurringTasks;
|
||||||
int m_maxAsyncCallStackDepth;
|
int m_maxAsyncCallStackDepth;
|
||||||
|
|
||||||
std::vector<void*> m_currentTasks;
|
std::vector<void*> m_currentTasks;
|
||||||
std::vector<std::unique_ptr<V8StackTraceImpl>> m_currentStacks;
|
std::vector<std::shared_ptr<AsyncStackTrace>> m_currentAsyncParent;
|
||||||
std::vector<std::unique_ptr<V8StackTraceImpl>> m_currentCreationStacks;
|
std::vector<std::shared_ptr<AsyncStackTrace>> m_currentAsyncCreation;
|
||||||
|
|
||||||
|
void collectOldAsyncStacksIfNeeded();
|
||||||
|
void removeOldAsyncTasks(AsyncTaskToStackTrace& map);
|
||||||
|
int m_asyncStacksCount = 0;
|
||||||
|
// V8Debugger owns all the async stacks, while most of the other references
|
||||||
|
// are weak, which allows to collect some stacks when there are too many.
|
||||||
|
std::list<std::shared_ptr<AsyncStackTrace>> m_allAsyncStacks;
|
||||||
|
|
||||||
protocol::HashMap<V8DebuggerAgentImpl*, int> m_maxAsyncCallStackDepthMap;
|
protocol::HashMap<V8DebuggerAgentImpl*, int> m_maxAsyncCallStackDepthMap;
|
||||||
protocol::HashMap<void*, void*> m_parentTask;
|
protocol::HashMap<void*, void*> m_parentTask;
|
||||||
protocol::HashMap<void*, void*> m_firstNextTask;
|
protocol::HashMap<void*, void*> m_firstNextTask;
|
||||||
|
@ -4,12 +4,10 @@
|
|||||||
|
|
||||||
#include "src/inspector/v8-stack-trace-impl.h"
|
#include "src/inspector/v8-stack-trace-impl.h"
|
||||||
|
|
||||||
#include "src/inspector/string-util.h"
|
#include <algorithm>
|
||||||
#include "src/inspector/v8-debugger-agent-impl.h"
|
|
||||||
#include "src/inspector/v8-debugger.h"
|
|
||||||
#include "src/inspector/v8-inspector-impl.h"
|
|
||||||
|
|
||||||
#include "include/v8-version.h"
|
#include "src/inspector/v8-debugger.h"
|
||||||
|
#include "src/inspector/wasm-translation.h"
|
||||||
|
|
||||||
namespace v8_inspector {
|
namespace v8_inspector {
|
||||||
|
|
||||||
@ -17,77 +15,115 @@ namespace {
|
|||||||
|
|
||||||
static const v8::StackTrace::StackTraceOptions stackTraceOptions =
|
static const v8::StackTrace::StackTraceOptions stackTraceOptions =
|
||||||
static_cast<v8::StackTrace::StackTraceOptions>(
|
static_cast<v8::StackTrace::StackTraceOptions>(
|
||||||
v8::StackTrace::kLineNumber | v8::StackTrace::kColumnOffset |
|
v8::StackTrace::kDetailed |
|
||||||
v8::StackTrace::kScriptId | v8::StackTrace::kScriptNameOrSourceURL |
|
|
||||||
v8::StackTrace::kFunctionName |
|
|
||||||
v8::StackTrace::kExposeFramesAcrossSecurityOrigins);
|
v8::StackTrace::kExposeFramesAcrossSecurityOrigins);
|
||||||
|
|
||||||
V8StackTraceImpl::Frame toFrame(v8::Local<v8::StackFrame> frame,
|
std::vector<V8StackTraceImpl::Frame> toFramesVector(
|
||||||
WasmTranslation* wasmTranslation) {
|
V8Debugger* debugger, v8::Local<v8::StackTrace> v8StackTrace,
|
||||||
String16 scriptId = String16::fromInteger(frame->GetScriptId());
|
int maxStackSize) {
|
||||||
String16 sourceName;
|
DCHECK(debugger->isolate()->InContext());
|
||||||
v8::Local<v8::String> sourceNameValue(frame->GetScriptNameOrSourceURL());
|
int frameCount = std::min(v8StackTrace->GetFrameCount(), maxStackSize);
|
||||||
if (!sourceNameValue.IsEmpty())
|
std::vector<V8StackTraceImpl::Frame> frames;
|
||||||
sourceName = toProtocolString(sourceNameValue);
|
for (int i = 0; i < frameCount; ++i) {
|
||||||
|
v8::Local<v8::StackFrame> v8Frame = v8StackTrace->GetFrame(i);
|
||||||
String16 functionName;
|
frames.emplace_back(v8Frame);
|
||||||
v8::Local<v8::String> functionNameValue(frame->GetFunctionName());
|
// TODO(clemensh): Figure out a way to do this translation only right before
|
||||||
if (!functionNameValue.IsEmpty())
|
// sending the stack trace over wire.
|
||||||
functionName = toProtocolString(functionNameValue);
|
if (v8Frame->IsWasm()) frames.back().translate(debugger->wasmTranslation());
|
||||||
|
|
||||||
int sourceLineNumber = frame->GetLineNumber() - 1;
|
|
||||||
int sourceColumn = frame->GetColumn() - 1;
|
|
||||||
// TODO(clemensh): Figure out a way to do this translation only right before
|
|
||||||
// sending the stack trace over wire.
|
|
||||||
if (frame->IsWasm()) {
|
|
||||||
wasmTranslation->TranslateWasmScriptLocationToProtocolLocation(
|
|
||||||
&scriptId, &sourceLineNumber, &sourceColumn);
|
|
||||||
}
|
}
|
||||||
return V8StackTraceImpl::Frame(functionName, scriptId, sourceName,
|
return frames;
|
||||||
sourceLineNumber + 1, sourceColumn + 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void toFramesVector(v8::Local<v8::StackTrace> stackTrace,
|
void calculateAsyncChain(V8Debugger* debugger, int contextGroupId,
|
||||||
std::vector<V8StackTraceImpl::Frame>& frames,
|
std::shared_ptr<AsyncStackTrace>* asyncParent,
|
||||||
size_t maxStackSize, v8::Isolate* isolate,
|
std::shared_ptr<AsyncStackTrace>* asyncCreation,
|
||||||
V8Debugger* debugger) {
|
int* maxAsyncDepth) {
|
||||||
DCHECK(isolate->InContext());
|
*asyncParent = debugger->currentAsyncParent();
|
||||||
int frameCount = stackTrace->GetFrameCount();
|
*asyncCreation = debugger->currentAsyncCreation();
|
||||||
if (frameCount > static_cast<int>(maxStackSize))
|
if (maxAsyncDepth) *maxAsyncDepth = debugger->maxAsyncCallChainDepth();
|
||||||
frameCount = static_cast<int>(maxStackSize);
|
|
||||||
WasmTranslation* wasmTranslation = debugger->wasmTranslation();
|
DCHECK(!*asyncParent || !*asyncCreation ||
|
||||||
for (int i = 0; i < frameCount; i++) {
|
(*asyncParent)->contextGroupId() ==
|
||||||
v8::Local<v8::StackFrame> stackFrame = stackTrace->GetFrame(i);
|
(*asyncCreation)->contextGroupId());
|
||||||
frames.push_back(toFrame(stackFrame, wasmTranslation));
|
// Do not accidentally append async call chain from another group. This should
|
||||||
|
// not happen if we have proper instrumentation, but let's double-check to be
|
||||||
|
// safe.
|
||||||
|
if (contextGroupId && *asyncParent && (*asyncParent)->contextGroupId() &&
|
||||||
|
(*asyncParent)->contextGroupId() != contextGroupId) {
|
||||||
|
asyncParent->reset();
|
||||||
|
asyncCreation->reset();
|
||||||
|
if (maxAsyncDepth) *maxAsyncDepth = 0;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Only the top stack in the chain may be empty and doesn't contain creation
|
||||||
|
// stack, so ensure that second stack is non-empty (it's the top of appended
|
||||||
|
// chain).
|
||||||
|
if (*asyncParent && !(*asyncCreation) && !(*asyncParent)->creation().lock() &&
|
||||||
|
(*asyncParent)->isEmpty()) {
|
||||||
|
*asyncParent = (*asyncParent)->parent().lock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<protocol::Runtime::StackTrace> buildInspectorObjectCommon(
|
||||||
|
const std::vector<V8StackTraceImpl::Frame>& frames,
|
||||||
|
const std::shared_ptr<AsyncStackTrace>& asyncParent,
|
||||||
|
const std::shared_ptr<AsyncStackTrace>& asyncCreation, int maxAsyncDepth) {
|
||||||
|
std::unique_ptr<protocol::Array<protocol::Runtime::CallFrame>>
|
||||||
|
inspectorFrames = protocol::Array<protocol::Runtime::CallFrame>::create();
|
||||||
|
for (size_t i = 0; i < frames.size(); i++) {
|
||||||
|
inspectorFrames->addItem(frames[i].buildInspectorObject());
|
||||||
|
}
|
||||||
|
std::unique_ptr<protocol::Runtime::StackTrace> stackTrace =
|
||||||
|
protocol::Runtime::StackTrace::create()
|
||||||
|
.setCallFrames(std::move(inspectorFrames))
|
||||||
|
.build();
|
||||||
|
if (asyncParent && maxAsyncDepth > 0) {
|
||||||
|
stackTrace->setParent(asyncParent->buildInspectorObject(asyncCreation.get(),
|
||||||
|
maxAsyncDepth - 1));
|
||||||
|
}
|
||||||
|
return stackTrace;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
V8StackTraceImpl::Frame::Frame(const String16& functionName,
|
V8StackTraceImpl::Frame::Frame(v8::Local<v8::StackFrame> v8Frame)
|
||||||
const String16& scriptId,
|
: m_functionName(toProtocolString(v8Frame->GetFunctionName())),
|
||||||
const String16& scriptName, int lineNumber,
|
m_scriptId(String16::fromInteger(v8Frame->GetScriptId())),
|
||||||
int column)
|
m_sourceURL(toProtocolString(v8Frame->GetScriptNameOrSourceURL())),
|
||||||
: m_functionName(functionName),
|
m_lineNumber(v8Frame->GetLineNumber() - 1),
|
||||||
m_scriptId(scriptId),
|
m_columnNumber(v8Frame->GetColumn() - 1) {
|
||||||
m_scriptName(scriptName),
|
DCHECK(m_lineNumber + 1 != v8::Message::kNoLineNumberInfo);
|
||||||
m_lineNumber(lineNumber),
|
DCHECK(m_columnNumber + 1 != v8::Message::kNoColumnInfo);
|
||||||
m_columnNumber(column) {
|
|
||||||
DCHECK(m_lineNumber != v8::Message::kNoLineNumberInfo);
|
|
||||||
DCHECK(m_columnNumber != v8::Message::kNoColumnInfo);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// buildInspectorObject() and SourceLocation's toTracedValue() should set the
|
void V8StackTraceImpl::Frame::translate(WasmTranslation* wasmTranslation) {
|
||||||
// same fields.
|
wasmTranslation->TranslateWasmScriptLocationToProtocolLocation(
|
||||||
// If either of them is modified, the other should be also modified.
|
&m_scriptId, &m_lineNumber, &m_columnNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
const String16& V8StackTraceImpl::Frame::functionName() const {
|
||||||
|
return m_functionName;
|
||||||
|
}
|
||||||
|
|
||||||
|
const String16& V8StackTraceImpl::Frame::scriptId() const { return m_scriptId; }
|
||||||
|
|
||||||
|
const String16& V8StackTraceImpl::Frame::sourceURL() const {
|
||||||
|
return m_sourceURL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int V8StackTraceImpl::Frame::lineNumber() const { return m_lineNumber; }
|
||||||
|
|
||||||
|
int V8StackTraceImpl::Frame::columnNumber() const { return m_columnNumber; }
|
||||||
|
|
||||||
std::unique_ptr<protocol::Runtime::CallFrame>
|
std::unique_ptr<protocol::Runtime::CallFrame>
|
||||||
V8StackTraceImpl::Frame::buildInspectorObject() const {
|
V8StackTraceImpl::Frame::buildInspectorObject() const {
|
||||||
return protocol::Runtime::CallFrame::create()
|
return protocol::Runtime::CallFrame::create()
|
||||||
.setFunctionName(m_functionName)
|
.setFunctionName(m_functionName)
|
||||||
.setScriptId(m_scriptId)
|
.setScriptId(m_scriptId)
|
||||||
.setUrl(m_scriptName)
|
.setUrl(m_sourceURL)
|
||||||
.setLineNumber(m_lineNumber - 1)
|
.setLineNumber(m_lineNumber)
|
||||||
.setColumnNumber(m_columnNumber - 1)
|
.setColumnNumber(m_columnNumber)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,196 +131,96 @@ V8StackTraceImpl::Frame::buildInspectorObject() const {
|
|||||||
void V8StackTraceImpl::setCaptureStackTraceForUncaughtExceptions(
|
void V8StackTraceImpl::setCaptureStackTraceForUncaughtExceptions(
|
||||||
v8::Isolate* isolate, bool capture) {
|
v8::Isolate* isolate, bool capture) {
|
||||||
isolate->SetCaptureStackTraceForUncaughtExceptions(
|
isolate->SetCaptureStackTraceForUncaughtExceptions(
|
||||||
capture, V8StackTraceImpl::maxCallStackSizeToCapture, stackTraceOptions);
|
capture, V8StackTraceImpl::maxCallStackSizeToCapture);
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
std::unique_ptr<V8StackTraceImpl> V8StackTraceImpl::create(
|
std::unique_ptr<V8StackTraceImpl> V8StackTraceImpl::create(
|
||||||
V8Debugger* debugger, int contextGroupId,
|
V8Debugger* debugger, int contextGroupId,
|
||||||
v8::Local<v8::StackTrace> stackTrace, size_t maxStackSize,
|
v8::Local<v8::StackTrace> v8StackTrace, int maxStackSize) {
|
||||||
const String16& description) {
|
|
||||||
DCHECK(debugger);
|
DCHECK(debugger);
|
||||||
v8::Isolate* isolate = debugger->inspector()->isolate();
|
|
||||||
|
v8::Isolate* isolate = debugger->isolate();
|
||||||
v8::HandleScope scope(isolate);
|
v8::HandleScope scope(isolate);
|
||||||
|
|
||||||
std::vector<V8StackTraceImpl::Frame> frames;
|
std::vector<V8StackTraceImpl::Frame> frames;
|
||||||
if (!stackTrace.IsEmpty()) {
|
if (!v8StackTrace.IsEmpty() && v8StackTrace->GetFrameCount()) {
|
||||||
toFramesVector(stackTrace, frames, maxStackSize, isolate, debugger);
|
frames = toFramesVector(debugger, v8StackTrace, maxStackSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
int maxAsyncCallChainDepth = 1;
|
int maxAsyncDepth = 0;
|
||||||
V8StackTraceImpl* asyncCallChain = nullptr;
|
std::shared_ptr<AsyncStackTrace> asyncParent;
|
||||||
V8StackTraceImpl* creationStack = nullptr;
|
std::shared_ptr<AsyncStackTrace> asyncCreation;
|
||||||
if (maxStackSize > 1) {
|
calculateAsyncChain(debugger, contextGroupId, &asyncParent, &asyncCreation,
|
||||||
asyncCallChain = debugger->currentAsyncCallChain();
|
&maxAsyncDepth);
|
||||||
creationStack = debugger->currentAsyncTaskCreationStack();
|
if (frames.empty() && !asyncCreation && !asyncParent) return nullptr;
|
||||||
maxAsyncCallChainDepth = debugger->maxAsyncCallChainDepth();
|
return std::unique_ptr<V8StackTraceImpl>(
|
||||||
}
|
new V8StackTraceImpl(frames, maxAsyncDepth, asyncParent, asyncCreation));
|
||||||
|
|
||||||
// Do not accidentally append async call chain from another group. This should
|
|
||||||
// not happen if we have proper instrumentation, but let's double-check to be
|
|
||||||
// safe.
|
|
||||||
if (contextGroupId && asyncCallChain && asyncCallChain->m_contextGroupId &&
|
|
||||||
asyncCallChain->m_contextGroupId != contextGroupId) {
|
|
||||||
asyncCallChain = nullptr;
|
|
||||||
creationStack = nullptr;
|
|
||||||
maxAsyncCallChainDepth = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only the top stack in the chain may be empty and doesn't contain creation
|
|
||||||
// stack , so ensure that second stack is non-empty (it's the top of appended
|
|
||||||
// chain).
|
|
||||||
if (asyncCallChain && !creationStack && !asyncCallChain->m_creation &&
|
|
||||||
asyncCallChain->isEmpty()) {
|
|
||||||
asyncCallChain = asyncCallChain->m_parent.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (frames.empty() && !creationStack && !asyncCallChain) return nullptr;
|
|
||||||
|
|
||||||
// When async call chain is empty but doesn't contain useful schedule stack
|
|
||||||
// and parent async call chain contains creationg stack but doesn't
|
|
||||||
// synchronous we can merge them together.
|
|
||||||
// e.g. Promise ThenableJob.
|
|
||||||
if (asyncCallChain && frames.empty() &&
|
|
||||||
asyncCallChain->m_description == description && !creationStack) {
|
|
||||||
return asyncCallChain->cloneImpl();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<V8StackTraceImpl> result(new V8StackTraceImpl(
|
|
||||||
contextGroupId, description, frames,
|
|
||||||
asyncCallChain ? asyncCallChain->cloneImpl() : nullptr,
|
|
||||||
creationStack ? creationStack->cloneImpl() : nullptr));
|
|
||||||
|
|
||||||
// Crop to not exceed maxAsyncCallChainDepth.
|
|
||||||
V8StackTraceImpl* deepest = result.get();
|
|
||||||
while (deepest && maxAsyncCallChainDepth) {
|
|
||||||
deepest = deepest->m_parent.get();
|
|
||||||
maxAsyncCallChainDepth--;
|
|
||||||
}
|
|
||||||
if (deepest) deepest->m_parent.reset();
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
std::unique_ptr<V8StackTraceImpl> V8StackTraceImpl::capture(
|
std::unique_ptr<V8StackTraceImpl> V8StackTraceImpl::capture(
|
||||||
V8Debugger* debugger, int contextGroupId, size_t maxStackSize,
|
V8Debugger* debugger, int contextGroupId, int maxStackSize) {
|
||||||
const String16& description) {
|
|
||||||
DCHECK(debugger);
|
DCHECK(debugger);
|
||||||
v8::Isolate* isolate = debugger->inspector()->isolate();
|
v8::Isolate* isolate = debugger->isolate();
|
||||||
v8::HandleScope handleScope(isolate);
|
v8::HandleScope handleScope(isolate);
|
||||||
v8::Local<v8::StackTrace> stackTrace;
|
v8::Local<v8::StackTrace> v8StackTrace;
|
||||||
if (isolate->InContext()) {
|
if (isolate->InContext()) {
|
||||||
stackTrace = v8::StackTrace::CurrentStackTrace(
|
v8StackTrace = v8::StackTrace::CurrentStackTrace(isolate, maxStackSize,
|
||||||
isolate, static_cast<int>(maxStackSize), stackTraceOptions);
|
stackTraceOptions);
|
||||||
}
|
}
|
||||||
return V8StackTraceImpl::create(debugger, contextGroupId, stackTrace,
|
return V8StackTraceImpl::create(debugger, contextGroupId, v8StackTrace,
|
||||||
maxStackSize, description);
|
maxStackSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<V8StackTraceImpl> V8StackTraceImpl::cloneImpl() {
|
V8StackTraceImpl::V8StackTraceImpl(
|
||||||
std::vector<Frame> framesCopy(m_frames);
|
const std::vector<Frame> frames, int maxAsyncDepth,
|
||||||
std::unique_ptr<V8StackTraceImpl> copy(
|
std::shared_ptr<AsyncStackTrace> asyncParent,
|
||||||
new V8StackTraceImpl(m_contextGroupId, m_description, framesCopy,
|
std::shared_ptr<AsyncStackTrace> asyncCreation)
|
||||||
m_parent ? m_parent->cloneImpl() : nullptr,
|
: m_frames(frames),
|
||||||
m_creation ? m_creation->cloneImpl() : nullptr));
|
m_maxAsyncDepth(maxAsyncDepth),
|
||||||
return copy;
|
m_asyncParent(asyncParent),
|
||||||
}
|
m_asyncCreation(asyncCreation) {}
|
||||||
|
|
||||||
std::unique_ptr<V8StackTrace> V8StackTraceImpl::clone() {
|
|
||||||
std::vector<Frame> frames;
|
|
||||||
for (size_t i = 0; i < m_frames.size(); i++) {
|
|
||||||
frames.push_back(m_frames.at(i));
|
|
||||||
}
|
|
||||||
return std::unique_ptr<V8StackTraceImpl>(new V8StackTraceImpl(
|
|
||||||
m_contextGroupId, m_description, frames, nullptr, nullptr));
|
|
||||||
}
|
|
||||||
|
|
||||||
V8StackTraceImpl::V8StackTraceImpl(int contextGroupId,
|
|
||||||
const String16& description,
|
|
||||||
std::vector<Frame>& frames,
|
|
||||||
std::unique_ptr<V8StackTraceImpl> parent,
|
|
||||||
std::unique_ptr<V8StackTraceImpl> creation)
|
|
||||||
: m_contextGroupId(contextGroupId),
|
|
||||||
m_description(description),
|
|
||||||
m_parent(std::move(parent)),
|
|
||||||
m_creation(std::move(creation)) {
|
|
||||||
m_frames.swap(frames);
|
|
||||||
}
|
|
||||||
|
|
||||||
V8StackTraceImpl::~V8StackTraceImpl() {}
|
V8StackTraceImpl::~V8StackTraceImpl() {}
|
||||||
|
|
||||||
|
std::unique_ptr<V8StackTrace> V8StackTraceImpl::clone() {
|
||||||
|
return std::unique_ptr<V8StackTrace>(
|
||||||
|
new V8StackTraceImpl(m_frames, 0, std::shared_ptr<AsyncStackTrace>(),
|
||||||
|
std::shared_ptr<AsyncStackTrace>()));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool V8StackTraceImpl::isEmpty() const { return m_frames.empty(); }
|
||||||
|
|
||||||
StringView V8StackTraceImpl::topSourceURL() const {
|
StringView V8StackTraceImpl::topSourceURL() const {
|
||||||
DCHECK(m_frames.size());
|
return toStringView(m_frames[0].sourceURL());
|
||||||
return toStringView(m_frames[0].m_scriptName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int V8StackTraceImpl::topLineNumber() const {
|
int V8StackTraceImpl::topLineNumber() const {
|
||||||
DCHECK(m_frames.size());
|
return m_frames[0].lineNumber() + 1;
|
||||||
return m_frames[0].m_lineNumber;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int V8StackTraceImpl::topColumnNumber() const {
|
int V8StackTraceImpl::topColumnNumber() const {
|
||||||
DCHECK(m_frames.size());
|
return m_frames[0].columnNumber() + 1;
|
||||||
return m_frames[0].m_columnNumber;
|
|
||||||
}
|
|
||||||
|
|
||||||
StringView V8StackTraceImpl::topFunctionName() const {
|
|
||||||
DCHECK(m_frames.size());
|
|
||||||
return toStringView(m_frames[0].m_functionName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
StringView V8StackTraceImpl::topScriptId() const {
|
StringView V8StackTraceImpl::topScriptId() const {
|
||||||
DCHECK(m_frames.size());
|
return toStringView(m_frames[0].scriptId());
|
||||||
return toStringView(m_frames[0].m_scriptId);
|
}
|
||||||
|
|
||||||
|
StringView V8StackTraceImpl::topFunctionName() const {
|
||||||
|
return toStringView(m_frames[0].functionName());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<protocol::Runtime::StackTrace>
|
std::unique_ptr<protocol::Runtime::StackTrace>
|
||||||
V8StackTraceImpl::buildInspectorObjectImpl() const {
|
V8StackTraceImpl::buildInspectorObjectImpl() const {
|
||||||
return buildInspectorObjectImpl(nullptr);
|
return buildInspectorObjectCommon(m_frames, m_asyncParent.lock(),
|
||||||
}
|
m_asyncCreation.lock(), m_maxAsyncDepth);
|
||||||
|
|
||||||
std::unique_ptr<protocol::Runtime::StackTrace>
|
|
||||||
V8StackTraceImpl::buildInspectorObjectImpl(V8StackTraceImpl* creation) const {
|
|
||||||
std::unique_ptr<protocol::Array<protocol::Runtime::CallFrame>> frames =
|
|
||||||
protocol::Array<protocol::Runtime::CallFrame>::create();
|
|
||||||
for (size_t i = 0; i < m_frames.size(); i++)
|
|
||||||
frames->addItem(m_frames.at(i).buildInspectorObject());
|
|
||||||
|
|
||||||
std::unique_ptr<protocol::Runtime::StackTrace> stackTrace =
|
|
||||||
protocol::Runtime::StackTrace::create()
|
|
||||||
.setCallFrames(std::move(frames))
|
|
||||||
.build();
|
|
||||||
if (!m_description.isEmpty()) stackTrace->setDescription(m_description);
|
|
||||||
if (m_parent) {
|
|
||||||
stackTrace->setParent(m_parent->buildInspectorObjectImpl(m_creation.get()));
|
|
||||||
}
|
|
||||||
if (creation && creation->m_frames.size()) {
|
|
||||||
stackTrace->setPromiseCreationFrame(
|
|
||||||
creation->m_frames[0].buildInspectorObject());
|
|
||||||
}
|
|
||||||
return stackTrace;
|
|
||||||
}
|
|
||||||
|
|
||||||
// static
|
|
||||||
std::unique_ptr<protocol::Runtime::StackTrace>
|
|
||||||
V8StackTraceImpl::buildInspectorObjectForTail(V8Debugger* debugger) {
|
|
||||||
DCHECK(debugger);
|
|
||||||
v8::HandleScope handleScope(debugger->inspector()->isolate());
|
|
||||||
// Next call collapses possible empty stack and ensures
|
|
||||||
// maxAsyncCallChainDepth.
|
|
||||||
V8StackTraceImpl* asyncChain = debugger->currentAsyncCallChain();
|
|
||||||
if (!asyncChain) return nullptr;
|
|
||||||
std::unique_ptr<V8StackTraceImpl> fullChain = V8StackTraceImpl::create(
|
|
||||||
debugger, asyncChain->m_contextGroupId, v8::Local<v8::StackTrace>(),
|
|
||||||
V8StackTraceImpl::maxCallStackSizeToCapture);
|
|
||||||
if (!fullChain || !fullChain->m_parent) return nullptr;
|
|
||||||
return fullChain->m_parent->buildInspectorObjectImpl(
|
|
||||||
fullChain->m_creation.get());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<protocol::Runtime::API::StackTrace>
|
std::unique_ptr<protocol::Runtime::API::StackTrace>
|
||||||
V8StackTraceImpl::buildInspectorObject() const {
|
V8StackTraceImpl::buildInspectorObject() const {
|
||||||
return buildInspectorObjectImpl(nullptr);
|
return buildInspectorObjectImpl();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<StringBuffer> V8StackTraceImpl::toString() const {
|
std::unique_ptr<StringBuffer> V8StackTraceImpl::toString() const {
|
||||||
@ -297,13 +233,86 @@ std::unique_ptr<StringBuffer> V8StackTraceImpl::toString() const {
|
|||||||
stackTrace.append(" (");
|
stackTrace.append(" (");
|
||||||
stackTrace.append(frame.sourceURL());
|
stackTrace.append(frame.sourceURL());
|
||||||
stackTrace.append(':');
|
stackTrace.append(':');
|
||||||
stackTrace.append(String16::fromInteger(frame.lineNumber()));
|
stackTrace.append(String16::fromInteger(frame.lineNumber() + 1));
|
||||||
stackTrace.append(':');
|
stackTrace.append(':');
|
||||||
stackTrace.append(String16::fromInteger(frame.columnNumber()));
|
stackTrace.append(String16::fromInteger(frame.columnNumber() + 1));
|
||||||
stackTrace.append(')');
|
stackTrace.append(')');
|
||||||
}
|
}
|
||||||
String16 string = stackTrace.toString();
|
String16 string = stackTrace.toString();
|
||||||
return StringBufferImpl::adopt(string);
|
return StringBufferImpl::adopt(string);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
std::shared_ptr<AsyncStackTrace> AsyncStackTrace::capture(
|
||||||
|
V8Debugger* debugger, int contextGroupId, const String16& description,
|
||||||
|
int maxStackSize) {
|
||||||
|
DCHECK(debugger);
|
||||||
|
|
||||||
|
v8::Isolate* isolate = debugger->isolate();
|
||||||
|
v8::HandleScope handleScope(isolate);
|
||||||
|
|
||||||
|
std::vector<V8StackTraceImpl::Frame> frames;
|
||||||
|
if (isolate->InContext()) {
|
||||||
|
v8::Local<v8::StackTrace> v8StackTrace = v8::StackTrace::CurrentStackTrace(
|
||||||
|
isolate, maxStackSize, stackTraceOptions);
|
||||||
|
frames = toFramesVector(debugger, v8StackTrace, maxStackSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<AsyncStackTrace> asyncParent;
|
||||||
|
std::shared_ptr<AsyncStackTrace> asyncCreation;
|
||||||
|
calculateAsyncChain(debugger, contextGroupId, &asyncParent, &asyncCreation,
|
||||||
|
nullptr);
|
||||||
|
|
||||||
|
if (frames.empty() && !asyncCreation && !asyncParent) return nullptr;
|
||||||
|
|
||||||
|
// When async call chain is empty but doesn't contain useful schedule stack
|
||||||
|
// and parent async call chain contains creationg stack but doesn't
|
||||||
|
// synchronous we can merge them together.
|
||||||
|
// e.g. Promise ThenableJob.
|
||||||
|
if (asyncParent && frames.empty() &&
|
||||||
|
asyncParent->m_description == description && !asyncCreation) {
|
||||||
|
return asyncParent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::shared_ptr<AsyncStackTrace>(new AsyncStackTrace(
|
||||||
|
contextGroupId, description, frames, asyncParent, asyncCreation));
|
||||||
|
}
|
||||||
|
|
||||||
|
AsyncStackTrace::AsyncStackTrace(
|
||||||
|
int contextGroupId, const String16& description,
|
||||||
|
const std::vector<V8StackTraceImpl::Frame>& frames,
|
||||||
|
std::shared_ptr<AsyncStackTrace> asyncParent,
|
||||||
|
std::shared_ptr<AsyncStackTrace> asyncCreation)
|
||||||
|
: m_contextGroupId(contextGroupId),
|
||||||
|
m_description(description),
|
||||||
|
m_frames(frames),
|
||||||
|
m_asyncParent(asyncParent),
|
||||||
|
m_asyncCreation(asyncCreation) {}
|
||||||
|
|
||||||
|
std::unique_ptr<protocol::Runtime::StackTrace>
|
||||||
|
AsyncStackTrace::buildInspectorObject(AsyncStackTrace* asyncCreation,
|
||||||
|
int maxAsyncDepth) const {
|
||||||
|
std::unique_ptr<protocol::Runtime::StackTrace> stackTrace =
|
||||||
|
buildInspectorObjectCommon(m_frames, m_asyncParent.lock(),
|
||||||
|
m_asyncCreation.lock(), maxAsyncDepth);
|
||||||
|
if (!m_description.isEmpty()) stackTrace->setDescription(m_description);
|
||||||
|
if (asyncCreation && !asyncCreation->isEmpty()) {
|
||||||
|
stackTrace->setPromiseCreationFrame(
|
||||||
|
asyncCreation->m_frames[0].buildInspectorObject());
|
||||||
|
}
|
||||||
|
return stackTrace;
|
||||||
|
}
|
||||||
|
|
||||||
|
int AsyncStackTrace::contextGroupId() const { return m_contextGroupId; }
|
||||||
|
|
||||||
|
std::weak_ptr<AsyncStackTrace> AsyncStackTrace::parent() const {
|
||||||
|
return m_asyncParent;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::weak_ptr<AsyncStackTrace> AsyncStackTrace::creation() const {
|
||||||
|
return m_asyncCreation;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AsyncStackTrace::isEmpty() const { return m_frames.empty(); }
|
||||||
|
|
||||||
} // namespace v8_inspector
|
} // namespace v8_inspector
|
||||||
|
@ -5,93 +5,115 @@
|
|||||||
#ifndef V8_INSPECTOR_V8STACKTRACEIMPL_H_
|
#ifndef V8_INSPECTOR_V8STACKTRACEIMPL_H_
|
||||||
#define V8_INSPECTOR_V8STACKTRACEIMPL_H_
|
#define V8_INSPECTOR_V8STACKTRACEIMPL_H_
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "src/base/macros.h"
|
|
||||||
#include "src/inspector/protocol/Forward.h"
|
|
||||||
#include "src/inspector/protocol/Runtime.h"
|
|
||||||
|
|
||||||
#include "include/v8-inspector.h"
|
#include "include/v8-inspector.h"
|
||||||
|
#include "include/v8.h"
|
||||||
|
#include "src/base/macros.h"
|
||||||
|
#include "src/inspector/protocol/Runtime.h"
|
||||||
|
#include "src/inspector/string-16.h"
|
||||||
|
|
||||||
namespace v8_inspector {
|
namespace v8_inspector {
|
||||||
|
|
||||||
|
class AsyncStackTrace;
|
||||||
class V8Debugger;
|
class V8Debugger;
|
||||||
|
class WasmTranslation;
|
||||||
|
|
||||||
// Note: async stack trace may have empty top stack with non-empty tail to
|
class V8StackTraceImpl : public V8StackTrace {
|
||||||
// indicate that current native-only state had some async story.
|
|
||||||
// On the other hand, any non-top async stack is guaranteed to be non-empty.
|
|
||||||
class V8StackTraceImpl final : public V8StackTrace {
|
|
||||||
public:
|
public:
|
||||||
static const size_t maxCallStackSizeToCapture = 200;
|
|
||||||
|
|
||||||
class Frame {
|
|
||||||
public:
|
|
||||||
Frame(const String16& functionName, const String16& scriptId,
|
|
||||||
const String16& scriptName, int lineNumber, int column = 0);
|
|
||||||
~Frame() = default;
|
|
||||||
|
|
||||||
const String16& functionName() const { return m_functionName; }
|
|
||||||
const String16& scriptId() const { return m_scriptId; }
|
|
||||||
const String16& sourceURL() const { return m_scriptName; }
|
|
||||||
int lineNumber() const { return m_lineNumber; }
|
|
||||||
int columnNumber() const { return m_columnNumber; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
friend class V8StackTraceImpl;
|
|
||||||
std::unique_ptr<protocol::Runtime::CallFrame> buildInspectorObject() const;
|
|
||||||
|
|
||||||
String16 m_functionName;
|
|
||||||
String16 m_scriptId;
|
|
||||||
String16 m_scriptName;
|
|
||||||
int m_lineNumber;
|
|
||||||
int m_columnNumber;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void setCaptureStackTraceForUncaughtExceptions(v8::Isolate*,
|
static void setCaptureStackTraceForUncaughtExceptions(v8::Isolate*,
|
||||||
bool capture);
|
bool capture);
|
||||||
static std::unique_ptr<V8StackTraceImpl> create(
|
static const int maxCallStackSizeToCapture = 200;
|
||||||
V8Debugger*, int contextGroupId, v8::Local<v8::StackTrace>,
|
static std::unique_ptr<V8StackTraceImpl> create(V8Debugger*,
|
||||||
size_t maxStackSize, const String16& description = String16());
|
int contextGroupId,
|
||||||
static std::unique_ptr<V8StackTraceImpl> capture(
|
v8::Local<v8::StackTrace>,
|
||||||
V8Debugger*, int contextGroupId, size_t maxStackSize,
|
int maxStackSize);
|
||||||
const String16& description = String16());
|
static std::unique_ptr<V8StackTraceImpl> capture(V8Debugger*,
|
||||||
|
int contextGroupId,
|
||||||
|
int maxStackSize);
|
||||||
|
|
||||||
// This method drops the async chain. Use cloneImpl() instead.
|
~V8StackTraceImpl() override;
|
||||||
std::unique_ptr<V8StackTrace> clone() override;
|
|
||||||
std::unique_ptr<V8StackTraceImpl> cloneImpl();
|
|
||||||
static std::unique_ptr<protocol::Runtime::StackTrace>
|
|
||||||
buildInspectorObjectForTail(V8Debugger*);
|
|
||||||
std::unique_ptr<protocol::Runtime::StackTrace> buildInspectorObjectImpl()
|
std::unique_ptr<protocol::Runtime::StackTrace> buildInspectorObjectImpl()
|
||||||
const;
|
const;
|
||||||
~V8StackTraceImpl() override;
|
|
||||||
|
|
||||||
// V8StackTrace implementation.
|
// V8StackTrace implementation.
|
||||||
bool isEmpty() const override { return !m_frames.size(); };
|
// This method drops the async stack trace.
|
||||||
|
std::unique_ptr<V8StackTrace> clone() override;
|
||||||
|
bool isEmpty() const override;
|
||||||
StringView topSourceURL() const override;
|
StringView topSourceURL() const override;
|
||||||
int topLineNumber() const override;
|
int topLineNumber() const override; // 1-based.
|
||||||
int topColumnNumber() const override;
|
int topColumnNumber() const override; // 1-based.
|
||||||
StringView topScriptId() const override;
|
StringView topScriptId() const override;
|
||||||
StringView topFunctionName() const override;
|
StringView topFunctionName() const override;
|
||||||
std::unique_ptr<protocol::Runtime::API::StackTrace> buildInspectorObject()
|
std::unique_ptr<protocol::Runtime::API::StackTrace> buildInspectorObject()
|
||||||
const override;
|
const override;
|
||||||
std::unique_ptr<StringBuffer> toString() const override;
|
std::unique_ptr<StringBuffer> toString() const override;
|
||||||
|
|
||||||
private:
|
class Frame {
|
||||||
V8StackTraceImpl(int contextGroupId, const String16& description,
|
public:
|
||||||
std::vector<Frame>& frames,
|
explicit Frame(v8::Local<v8::StackFrame> frame);
|
||||||
std::unique_ptr<V8StackTraceImpl> parent,
|
~Frame() = default;
|
||||||
std::unique_ptr<V8StackTraceImpl> creation);
|
|
||||||
|
|
||||||
std::unique_ptr<protocol::Runtime::StackTrace> buildInspectorObjectImpl(
|
void translate(WasmTranslation* wasmTranslation);
|
||||||
V8StackTraceImpl* creation) const;
|
|
||||||
|
const String16& functionName() const;
|
||||||
|
const String16& scriptId() const;
|
||||||
|
const String16& sourceURL() const;
|
||||||
|
int lineNumber() const; // 0-based.
|
||||||
|
int columnNumber() const; // 0-based.
|
||||||
|
std::unique_ptr<protocol::Runtime::CallFrame> buildInspectorObject() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
String16 m_functionName;
|
||||||
|
String16 m_scriptId;
|
||||||
|
String16 m_sourceURL;
|
||||||
|
int m_lineNumber; // 0-based.
|
||||||
|
int m_columnNumber; // 0-based.
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
V8StackTraceImpl(const std::vector<Frame> frames, int maxAsyncDepth,
|
||||||
|
std::shared_ptr<AsyncStackTrace> asyncParent,
|
||||||
|
std::shared_ptr<AsyncStackTrace> asyncCreation);
|
||||||
|
|
||||||
|
std::vector<Frame> m_frames;
|
||||||
|
int m_maxAsyncDepth;
|
||||||
|
std::weak_ptr<AsyncStackTrace> m_asyncParent;
|
||||||
|
std::weak_ptr<AsyncStackTrace> m_asyncCreation;
|
||||||
|
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(V8StackTraceImpl);
|
||||||
|
};
|
||||||
|
|
||||||
|
class AsyncStackTrace {
|
||||||
|
public:
|
||||||
|
static std::shared_ptr<AsyncStackTrace> capture(V8Debugger*,
|
||||||
|
int contextGroupId,
|
||||||
|
const String16& description,
|
||||||
|
int maxStackSize);
|
||||||
|
|
||||||
|
std::unique_ptr<protocol::Runtime::StackTrace> buildInspectorObject(
|
||||||
|
AsyncStackTrace* asyncCreation, int maxAsyncDepth) const;
|
||||||
|
|
||||||
|
int contextGroupId() const;
|
||||||
|
std::weak_ptr<AsyncStackTrace> parent() const;
|
||||||
|
std::weak_ptr<AsyncStackTrace> creation() const;
|
||||||
|
bool isEmpty() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
AsyncStackTrace(int contextGroupId, const String16& description,
|
||||||
|
const std::vector<V8StackTraceImpl::Frame>& frames,
|
||||||
|
std::shared_ptr<AsyncStackTrace> asyncParent,
|
||||||
|
std::shared_ptr<AsyncStackTrace> asyncCreation);
|
||||||
|
|
||||||
int m_contextGroupId;
|
int m_contextGroupId;
|
||||||
String16 m_description;
|
String16 m_description;
|
||||||
std::vector<Frame> m_frames;
|
|
||||||
std::unique_ptr<V8StackTraceImpl> m_parent;
|
|
||||||
std::unique_ptr<V8StackTraceImpl> m_creation;
|
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(V8StackTraceImpl);
|
std::vector<V8StackTraceImpl::Frame> m_frames;
|
||||||
|
std::weak_ptr<AsyncStackTrace> m_asyncParent;
|
||||||
|
std::weak_ptr<AsyncStackTrace> m_asyncCreation;
|
||||||
|
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(AsyncStackTrace);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace v8_inspector
|
} // namespace v8_inspector
|
||||||
|
@ -1,140 +0,0 @@
|
|||||||
Checks that async stacks works good with different limits
|
|
||||||
|
|
||||||
Running test: testZeroLimit
|
|
||||||
foo1 (test.js:11:2)
|
|
||||||
|
|
||||||
|
|
||||||
Running test: testTwoLimit
|
|
||||||
foo1 (test.js:11:2)
|
|
||||||
-- Promise.resolve --
|
|
||||||
promise (test.js:23:2)
|
|
||||||
(anonymous) (expr.js:0:0)
|
|
||||||
|
|
||||||
|
|
||||||
Running test: testOneLimitTwoPromises
|
|
||||||
foo1 (test.js:11:2)
|
|
||||||
|
|
||||||
foo2 (test.js:15:2)
|
|
||||||
|
|
||||||
|
|
||||||
Running test: testFourLimitTwoPromises
|
|
||||||
foo1 (test.js:11:2)
|
|
||||||
|
|
||||||
foo2 (test.js:15:2)
|
|
||||||
|
|
||||||
|
|
||||||
Running test: testSixLimitTwoPromises
|
|
||||||
foo1 (test.js:11:2)
|
|
||||||
|
|
||||||
foo2 (test.js:15:2)
|
|
||||||
-- Promise.resolve --
|
|
||||||
twoPromises (test.js:35:2)
|
|
||||||
(anonymous) (expr.js:0:0)
|
|
||||||
|
|
||||||
|
|
||||||
Running test: testTwoLimitTwoSetTimeouts
|
|
||||||
foo1 (test.js:11:2)
|
|
||||||
|
|
||||||
foo2 (test.js:15:2)
|
|
||||||
-- setTimeout --
|
|
||||||
twoSetTimeout (test.js:41:2)
|
|
||||||
(anonymous) (expr.js:0:0)
|
|
||||||
|
|
||||||
|
|
||||||
Running test: testThreeLimitTwoSetTimeouts
|
|
||||||
foo1 (test.js:11:2)
|
|
||||||
-- setTimeout --
|
|
||||||
twoSetTimeout (test.js:40:2)
|
|
||||||
(anonymous) (expr.js:0:0)
|
|
||||||
|
|
||||||
foo2 (test.js:15:2)
|
|
||||||
-- setTimeout --
|
|
||||||
twoSetTimeout (test.js:41:2)
|
|
||||||
(anonymous) (expr.js:0:0)
|
|
||||||
|
|
||||||
|
|
||||||
Running test: testTenLimitTwentySetTimeouts
|
|
||||||
foo1 (:0:17)
|
|
||||||
(anonymous) (:0:28)
|
|
||||||
|
|
||||||
foo2 (:0:17)
|
|
||||||
(anonymous) (:0:28)
|
|
||||||
|
|
||||||
foo3 (:0:17)
|
|
||||||
(anonymous) (:0:28)
|
|
||||||
|
|
||||||
foo4 (:0:17)
|
|
||||||
(anonymous) (:0:28)
|
|
||||||
|
|
||||||
foo5 (:0:17)
|
|
||||||
(anonymous) (:0:28)
|
|
||||||
|
|
||||||
foo6 (:0:17)
|
|
||||||
(anonymous) (:0:28)
|
|
||||||
|
|
||||||
foo7 (:0:17)
|
|
||||||
(anonymous) (:0:28)
|
|
||||||
|
|
||||||
foo8 (:0:17)
|
|
||||||
(anonymous) (:0:28)
|
|
||||||
|
|
||||||
foo9 (:0:17)
|
|
||||||
(anonymous) (:0:28)
|
|
||||||
|
|
||||||
foo10 (:0:18)
|
|
||||||
(anonymous) (:0:29)
|
|
||||||
|
|
||||||
foo11 (:0:18)
|
|
||||||
(anonymous) (:0:29)
|
|
||||||
-- setTimeout --
|
|
||||||
twentySetTimeout (test.js:49:4)
|
|
||||||
(anonymous) (expr.js:0:0)
|
|
||||||
|
|
||||||
foo12 (:0:18)
|
|
||||||
(anonymous) (:0:29)
|
|
||||||
-- setTimeout --
|
|
||||||
twentySetTimeout (test.js:49:4)
|
|
||||||
(anonymous) (expr.js:0:0)
|
|
||||||
|
|
||||||
foo13 (:0:18)
|
|
||||||
(anonymous) (:0:29)
|
|
||||||
-- setTimeout --
|
|
||||||
twentySetTimeout (test.js:49:4)
|
|
||||||
(anonymous) (expr.js:0:0)
|
|
||||||
|
|
||||||
foo14 (:0:18)
|
|
||||||
(anonymous) (:0:29)
|
|
||||||
-- setTimeout --
|
|
||||||
twentySetTimeout (test.js:49:4)
|
|
||||||
(anonymous) (expr.js:0:0)
|
|
||||||
|
|
||||||
foo15 (:0:18)
|
|
||||||
(anonymous) (:0:29)
|
|
||||||
-- setTimeout --
|
|
||||||
twentySetTimeout (test.js:49:4)
|
|
||||||
(anonymous) (expr.js:0:0)
|
|
||||||
|
|
||||||
foo16 (:0:18)
|
|
||||||
(anonymous) (:0:29)
|
|
||||||
-- setTimeout --
|
|
||||||
twentySetTimeout (test.js:49:4)
|
|
||||||
(anonymous) (expr.js:0:0)
|
|
||||||
|
|
||||||
foo17 (:0:18)
|
|
||||||
(anonymous) (:0:29)
|
|
||||||
-- setTimeout --
|
|
||||||
twentySetTimeout (test.js:49:4)
|
|
||||||
(anonymous) (expr.js:0:0)
|
|
||||||
|
|
||||||
foo18 (:0:18)
|
|
||||||
(anonymous) (:0:29)
|
|
||||||
-- setTimeout --
|
|
||||||
twentySetTimeout (test.js:49:4)
|
|
||||||
(anonymous) (expr.js:0:0)
|
|
||||||
|
|
||||||
foo19 (:0:18)
|
|
||||||
(anonymous) (:0:29)
|
|
||||||
-- setTimeout --
|
|
||||||
twentySetTimeout (test.js:49:4)
|
|
||||||
(anonymous) (expr.js:0:0)
|
|
||||||
|
|
@ -1,164 +0,0 @@
|
|||||||
// 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.
|
|
||||||
|
|
||||||
InspectorTest.log('Checks that async stacks works good with different limits');
|
|
||||||
|
|
||||||
InspectorTest.addScript(`
|
|
||||||
var resolveTest;
|
|
||||||
|
|
||||||
function foo1() {
|
|
||||||
debugger;
|
|
||||||
}
|
|
||||||
|
|
||||||
function foo2() {
|
|
||||||
debugger;
|
|
||||||
if (resolveTest) resolveTest();
|
|
||||||
}
|
|
||||||
|
|
||||||
function promise() {
|
|
||||||
var resolve1;
|
|
||||||
var p1 = new Promise(resolve => resolve1 = resolve);
|
|
||||||
var p2 = p1.then(foo1);
|
|
||||||
resolve1();
|
|
||||||
return p2;
|
|
||||||
}
|
|
||||||
|
|
||||||
function twoPromises() {
|
|
||||||
var resolve1;
|
|
||||||
var resolve2;
|
|
||||||
var p1 = new Promise(resolve => resolve1 = resolve);
|
|
||||||
var p2 = new Promise(resolve => resolve2 = resolve);
|
|
||||||
var p3 = p1.then(foo1);
|
|
||||||
var p4 = p2.then(foo2);
|
|
||||||
resolve1();
|
|
||||||
resolve2();
|
|
||||||
return Promise.all([p3, p4]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function twoSetTimeout() {
|
|
||||||
setTimeout(foo1, 0);
|
|
||||||
setTimeout(foo2, 0);
|
|
||||||
return new Promise(resolve => resolveTest = resolve);
|
|
||||||
}
|
|
||||||
|
|
||||||
function twentySetTimeout() {
|
|
||||||
var resolve1;
|
|
||||||
var p1 = new Promise(resolve => resolve1 = resolve);
|
|
||||||
for (var i = 1; i <= 19; ++i)
|
|
||||||
setTimeout('(function foo' + i + '(){debugger;})()',0);
|
|
||||||
setTimeout(resolve1, 0);
|
|
||||||
return p1;
|
|
||||||
}
|
|
||||||
|
|
||||||
//# sourceURL=test.js`, 7, 26);
|
|
||||||
|
|
||||||
InspectorTest.setupScriptMap();
|
|
||||||
Protocol.Debugger.onPaused(message => {
|
|
||||||
InspectorTest.logCallFrames(message.params.callFrames);
|
|
||||||
var asyncStackTrace = message.params.asyncStackTrace;
|
|
||||||
while (asyncStackTrace) {
|
|
||||||
InspectorTest.log(`-- ${asyncStackTrace.description} --`);
|
|
||||||
InspectorTest.logCallFrames(asyncStackTrace.callFrames);
|
|
||||||
asyncStackTrace = asyncStackTrace.parent;
|
|
||||||
}
|
|
||||||
InspectorTest.log('');
|
|
||||||
Protocol.Debugger.resume();
|
|
||||||
});
|
|
||||||
|
|
||||||
Protocol.Debugger.enable();
|
|
||||||
Protocol.Debugger.setAsyncCallStackDepth({ maxDepth: 128 });
|
|
||||||
InspectorTest.runTestSuite([
|
|
||||||
function testZeroLimit(next) {
|
|
||||||
Protocol.Runtime.evaluate({
|
|
||||||
expression: 'setMaxAsyncTaskStacks(0)//# sourceURL=expr.js'})
|
|
||||||
.then(() => Protocol.Runtime.evaluate({
|
|
||||||
expression: 'promise()//# sourceURL=expr.js', awaitPromise: true
|
|
||||||
}))
|
|
||||||
.then(() => cancelAllAsyncTasks())
|
|
||||||
.then(next);
|
|
||||||
},
|
|
||||||
|
|
||||||
function testTwoLimit(next) {
|
|
||||||
// we need one stack for parent task and one for next task.
|
|
||||||
Protocol.Runtime
|
|
||||||
.evaluate({expression: 'setMaxAsyncTaskStacks(2)//# sourceURL=expr.js'})
|
|
||||||
.then(() => Protocol.Runtime.evaluate({
|
|
||||||
expression: 'promise()//# sourceURL=expr.js',
|
|
||||||
awaitPromise: true
|
|
||||||
}))
|
|
||||||
.then(() => cancelAllAsyncTasks())
|
|
||||||
.then(next);
|
|
||||||
},
|
|
||||||
|
|
||||||
function testOneLimitTwoPromises(next) {
|
|
||||||
// Should be no async stacks because when first microtask is finished
|
|
||||||
// it will resolve and schedule p3 - will remove async stack for scheduled
|
|
||||||
// p2.
|
|
||||||
Protocol.Runtime.evaluate({
|
|
||||||
expression: 'setMaxAsyncTaskStacks(1)//# sourceURL=expr.js'})
|
|
||||||
.then(() => Protocol.Runtime.evaluate({
|
|
||||||
expression: 'twoPromises()//# sourceURL=expr.js', awaitPromise: true
|
|
||||||
}))
|
|
||||||
.then(() => cancelAllAsyncTasks())
|
|
||||||
.then(next);
|
|
||||||
},
|
|
||||||
|
|
||||||
function testFourLimitTwoPromises(next) {
|
|
||||||
Protocol.Runtime
|
|
||||||
.evaluate({expression: 'setMaxAsyncTaskStacks(4)//# sourceURL=expr.js'})
|
|
||||||
.then(() => Protocol.Runtime.evaluate({
|
|
||||||
expression: 'twoPromises()//# sourceURL=expr.js',
|
|
||||||
awaitPromise: true
|
|
||||||
}))
|
|
||||||
.then(() => cancelAllAsyncTasks())
|
|
||||||
.then(next);
|
|
||||||
},
|
|
||||||
|
|
||||||
function testSixLimitTwoPromises(next) {
|
|
||||||
Protocol.Runtime
|
|
||||||
.evaluate({expression: 'setMaxAsyncTaskStacks(6)//# sourceURL=expr.js'})
|
|
||||||
.then(() => Protocol.Runtime.evaluate({
|
|
||||||
expression: 'twoPromises()//# sourceURL=expr.js',
|
|
||||||
awaitPromise: true
|
|
||||||
}))
|
|
||||||
.then(() => cancelAllAsyncTasks())
|
|
||||||
.then(next);
|
|
||||||
},
|
|
||||||
|
|
||||||
function testTwoLimitTwoSetTimeouts(next) {
|
|
||||||
Protocol.Runtime.evaluate({
|
|
||||||
expression: 'setMaxAsyncTaskStacks(2)//# sourceURL=expr.js'})
|
|
||||||
.then(() => Protocol.Runtime.evaluate({
|
|
||||||
expression: 'twoSetTimeout()//# sourceURL=expr.js', awaitPromise: true
|
|
||||||
}))
|
|
||||||
.then(() => cancelAllAsyncTasks())
|
|
||||||
.then(next);
|
|
||||||
},
|
|
||||||
|
|
||||||
function testThreeLimitTwoSetTimeouts(next) {
|
|
||||||
Protocol.Runtime.evaluate({
|
|
||||||
expression: 'setMaxAsyncTaskStacks(3)//# sourceURL=expr.js'})
|
|
||||||
.then(() => Protocol.Runtime.evaluate({
|
|
||||||
expression: 'twoSetTimeout()//# sourceURL=expr.js', awaitPromise: true
|
|
||||||
}))
|
|
||||||
.then(() => cancelAllAsyncTasks())
|
|
||||||
.then(next);
|
|
||||||
},
|
|
||||||
|
|
||||||
function testTenLimitTwentySetTimeouts(next) {
|
|
||||||
Protocol.Runtime.evaluate({
|
|
||||||
expression: 'setMaxAsyncTaskStacks(10)//# sourceURL=expr.js'})
|
|
||||||
.then(() => Protocol.Runtime.evaluate({
|
|
||||||
expression: 'twentySetTimeout()//# sourceURL=expr.js',
|
|
||||||
awaitPromise: true
|
|
||||||
}))
|
|
||||||
.then(() => cancelAllAsyncTasks())
|
|
||||||
.then(next);
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
|
|
||||||
function cancelAllAsyncTasks() {
|
|
||||||
return Protocol.Debugger.setAsyncCallStackDepth({ maxDepth: 0 })
|
|
||||||
.then(() => Protocol.Debugger.setAsyncCallStackDepth({ maxDepth: 128 }));
|
|
||||||
}
|
|
@ -0,0 +1,211 @@
|
|||||||
|
Checks that we drop old async call chains.
|
||||||
|
|
||||||
|
Running test: testInfrastructure
|
||||||
|
setMaxAsyncTaskStacks(1024)
|
||||||
|
Run expression 'console.trace(42)' with async chain len: 1
|
||||||
|
actual async chain len: 1
|
||||||
|
|
||||||
|
setMaxAsyncTaskStacks(1024)
|
||||||
|
Run expression 'console.trace(42)' with async chain len: 2
|
||||||
|
actual async chain len: 2
|
||||||
|
|
||||||
|
setMaxAsyncTaskStacks(1024)
|
||||||
|
Run expression 'console.trace(42)' with async chain len: 5
|
||||||
|
actual async chain len: 5
|
||||||
|
|
||||||
|
setMaxAsyncTaskStacks(1024)
|
||||||
|
Run expression 'console.trace(42)' with async chain len: 1
|
||||||
|
actual async chain len: 1
|
||||||
|
|
||||||
|
setMaxAsyncTaskStacks(1024)
|
||||||
|
Run expression 'console.trace(42)' with async chain len: 2
|
||||||
|
actual async chain len: 2
|
||||||
|
|
||||||
|
setMaxAsyncTaskStacks(1024)
|
||||||
|
Run expression 'console.trace(42)' with async chain len: 5
|
||||||
|
actual async chain len: 5
|
||||||
|
|
||||||
|
|
||||||
|
Running test: testZeroLimit
|
||||||
|
setMaxAsyncTaskStacks(0)
|
||||||
|
Run expression 'console.trace(42)' with async chain len: 1
|
||||||
|
actual async chain len: 0
|
||||||
|
|
||||||
|
setMaxAsyncTaskStacks(0)
|
||||||
|
Run expression 'console.trace(42)' with async chain len: 2
|
||||||
|
actual async chain len: 0
|
||||||
|
|
||||||
|
setMaxAsyncTaskStacks(0)
|
||||||
|
Run expression 'console.trace(42)' with async chain len: 1
|
||||||
|
actual async chain len: 0
|
||||||
|
|
||||||
|
setMaxAsyncTaskStacks(0)
|
||||||
|
Run expression 'console.trace(42)' with async chain len: 2
|
||||||
|
actual async chain len: 0
|
||||||
|
|
||||||
|
|
||||||
|
Running test: testOneLimit
|
||||||
|
setMaxAsyncTaskStacks(1)
|
||||||
|
Run expression 'console.trace(42)' with async chain len: 1
|
||||||
|
actual async chain len: 0
|
||||||
|
|
||||||
|
setMaxAsyncTaskStacks(1)
|
||||||
|
Run expression 'console.trace(42)' with async chain len: 2
|
||||||
|
actual async chain len: 0
|
||||||
|
|
||||||
|
setMaxAsyncTaskStacks(1)
|
||||||
|
Run expression 'console.trace(42)' with async chain len: 1
|
||||||
|
actual async chain len: 1
|
||||||
|
|
||||||
|
setMaxAsyncTaskStacks(1)
|
||||||
|
Run expression 'console.trace(42)' with async chain len: 2
|
||||||
|
actual async chain len: 1
|
||||||
|
|
||||||
|
|
||||||
|
Running test: testTwoLimit
|
||||||
|
setMaxAsyncTaskStacks(2)
|
||||||
|
Run expression 'console.trace(42)' with async chain len: 1
|
||||||
|
actual async chain len: 0
|
||||||
|
|
||||||
|
setMaxAsyncTaskStacks(2)
|
||||||
|
Run expression 'console.trace(42)' with async chain len: 2
|
||||||
|
actual async chain len: 0
|
||||||
|
|
||||||
|
setMaxAsyncTaskStacks(2)
|
||||||
|
Run expression 'console.trace(42)' with async chain len: 3
|
||||||
|
actual async chain len: 0
|
||||||
|
|
||||||
|
setMaxAsyncTaskStacks(2)
|
||||||
|
Run expression 'console.trace(42)' with async chain len: 1
|
||||||
|
actual async chain len: 1
|
||||||
|
|
||||||
|
setMaxAsyncTaskStacks(2)
|
||||||
|
Run expression 'console.trace(42)' with async chain len: 2
|
||||||
|
actual async chain len: 2
|
||||||
|
|
||||||
|
setMaxAsyncTaskStacks(2)
|
||||||
|
Run expression 'console.trace(42)' with async chain len: 3
|
||||||
|
actual async chain len: 1
|
||||||
|
|
||||||
|
|
||||||
|
Running test: testMoreThanTwoLimit
|
||||||
|
setMaxAsyncTaskStacks(3)
|
||||||
|
Run expression 'console.trace(42)' with async chain len: 1
|
||||||
|
actual async chain len: 1
|
||||||
|
|
||||||
|
setMaxAsyncTaskStacks(3)
|
||||||
|
Run expression 'console.trace(42)' with async chain len: 2
|
||||||
|
actual async chain len: 0
|
||||||
|
|
||||||
|
setMaxAsyncTaskStacks(3)
|
||||||
|
Run expression 'console.trace(42)' with async chain len: 3
|
||||||
|
actual async chain len: 0
|
||||||
|
|
||||||
|
setMaxAsyncTaskStacks(3)
|
||||||
|
Run expression 'console.trace(42)' with async chain len: 1
|
||||||
|
actual async chain len: 1
|
||||||
|
|
||||||
|
setMaxAsyncTaskStacks(3)
|
||||||
|
Run expression 'console.trace(42)' with async chain len: 2
|
||||||
|
actual async chain len: 2
|
||||||
|
|
||||||
|
setMaxAsyncTaskStacks(3)
|
||||||
|
Run expression 'console.trace(42)' with async chain len: 3
|
||||||
|
actual async chain len: 3
|
||||||
|
|
||||||
|
setMaxAsyncTaskStacks(4)
|
||||||
|
Run expression 'console.trace(42)' with async chain len: 1
|
||||||
|
actual async chain len: 1
|
||||||
|
|
||||||
|
setMaxAsyncTaskStacks(4)
|
||||||
|
Run expression 'console.trace(42)' with async chain len: 2
|
||||||
|
actual async chain len: 1
|
||||||
|
|
||||||
|
setMaxAsyncTaskStacks(4)
|
||||||
|
Run expression 'console.trace(42)' with async chain len: 3
|
||||||
|
actual async chain len: 0
|
||||||
|
|
||||||
|
setMaxAsyncTaskStacks(4)
|
||||||
|
Run expression 'console.trace(42)' with async chain len: 1
|
||||||
|
actual async chain len: 1
|
||||||
|
|
||||||
|
setMaxAsyncTaskStacks(4)
|
||||||
|
Run expression 'console.trace(42)' with async chain len: 2
|
||||||
|
actual async chain len: 2
|
||||||
|
|
||||||
|
setMaxAsyncTaskStacks(4)
|
||||||
|
Run expression 'console.trace(42)' with async chain len: 3
|
||||||
|
actual async chain len: 3
|
||||||
|
|
||||||
|
setMaxAsyncTaskStacks(5)
|
||||||
|
Run expression 'console.trace(42)' with async chain len: 1
|
||||||
|
actual async chain len: 1
|
||||||
|
|
||||||
|
setMaxAsyncTaskStacks(5)
|
||||||
|
Run expression 'console.trace(42)' with async chain len: 2
|
||||||
|
actual async chain len: 2
|
||||||
|
|
||||||
|
setMaxAsyncTaskStacks(5)
|
||||||
|
Run expression 'console.trace(42)' with async chain len: 3
|
||||||
|
actual async chain len: 2
|
||||||
|
|
||||||
|
setMaxAsyncTaskStacks(5)
|
||||||
|
Run expression 'console.trace(42)' with async chain len: 1
|
||||||
|
actual async chain len: 1
|
||||||
|
|
||||||
|
setMaxAsyncTaskStacks(5)
|
||||||
|
Run expression 'console.trace(42)' with async chain len: 2
|
||||||
|
actual async chain len: 2
|
||||||
|
|
||||||
|
setMaxAsyncTaskStacks(5)
|
||||||
|
Run expression 'console.trace(42)' with async chain len: 3
|
||||||
|
actual async chain len: 3
|
||||||
|
|
||||||
|
setMaxAsyncTaskStacks(6)
|
||||||
|
Run expression 'console.trace(42)' with async chain len: 1
|
||||||
|
actual async chain len: 1
|
||||||
|
|
||||||
|
setMaxAsyncTaskStacks(6)
|
||||||
|
Run expression 'console.trace(42)' with async chain len: 2
|
||||||
|
actual async chain len: 2
|
||||||
|
|
||||||
|
setMaxAsyncTaskStacks(6)
|
||||||
|
Run expression 'console.trace(42)' with async chain len: 3
|
||||||
|
actual async chain len: 2
|
||||||
|
|
||||||
|
setMaxAsyncTaskStacks(6)
|
||||||
|
Run expression 'console.trace(42)' with async chain len: 1
|
||||||
|
actual async chain len: 1
|
||||||
|
|
||||||
|
setMaxAsyncTaskStacks(6)
|
||||||
|
Run expression 'console.trace(42)' with async chain len: 2
|
||||||
|
actual async chain len: 2
|
||||||
|
|
||||||
|
setMaxAsyncTaskStacks(6)
|
||||||
|
Run expression 'console.trace(42)' with async chain len: 3
|
||||||
|
actual async chain len: 3
|
||||||
|
|
||||||
|
setMaxAsyncTaskStacks(7)
|
||||||
|
Run expression 'console.trace(42)' with async chain len: 1
|
||||||
|
actual async chain len: 1
|
||||||
|
|
||||||
|
setMaxAsyncTaskStacks(7)
|
||||||
|
Run expression 'console.trace(42)' with async chain len: 2
|
||||||
|
actual async chain len: 2
|
||||||
|
|
||||||
|
setMaxAsyncTaskStacks(7)
|
||||||
|
Run expression 'console.trace(42)' with async chain len: 3
|
||||||
|
actual async chain len: 3
|
||||||
|
|
||||||
|
setMaxAsyncTaskStacks(7)
|
||||||
|
Run expression 'console.trace(42)' with async chain len: 1
|
||||||
|
actual async chain len: 1
|
||||||
|
|
||||||
|
setMaxAsyncTaskStacks(7)
|
||||||
|
Run expression 'console.trace(42)' with async chain len: 2
|
||||||
|
actual async chain len: 2
|
||||||
|
|
||||||
|
setMaxAsyncTaskStacks(7)
|
||||||
|
Run expression 'console.trace(42)' with async chain len: 3
|
||||||
|
actual async chain len: 3
|
||||||
|
|
171
test/inspector/debugger/collect-old-async-call-chains.js
Normal file
171
test/inspector/debugger/collect-old-async-call-chains.js
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
InspectorTest.log('Checks that we drop old async call chains.');
|
||||||
|
|
||||||
|
Protocol.Debugger.enable();
|
||||||
|
Protocol.Runtime.enable();
|
||||||
|
InspectorTest.runAsyncTestSuite([
|
||||||
|
async function testInfrastructure() {
|
||||||
|
Protocol.Debugger.setAsyncCallStackDepth({maxDepth: 128});
|
||||||
|
await setMaxAsyncTaskStacks(1024);
|
||||||
|
runWithAsyncChainPromise(1, 'console.trace(42)');
|
||||||
|
dumpAsyncChainLength(await Protocol.Runtime.onceConsoleAPICalled());
|
||||||
|
|
||||||
|
await setMaxAsyncTaskStacks(1024);
|
||||||
|
runWithAsyncChainPromise(2, 'console.trace(42)');
|
||||||
|
dumpAsyncChainLength(await Protocol.Runtime.onceConsoleAPICalled());
|
||||||
|
|
||||||
|
await setMaxAsyncTaskStacks(1024);
|
||||||
|
runWithAsyncChainPromise(5, 'console.trace(42)');
|
||||||
|
dumpAsyncChainLength(await Protocol.Runtime.onceConsoleAPICalled());
|
||||||
|
|
||||||
|
await setMaxAsyncTaskStacks(1024);
|
||||||
|
runWithAsyncChainSetTimeout(1, 'console.trace(42)');
|
||||||
|
dumpAsyncChainLength(await Protocol.Runtime.onceConsoleAPICalled());
|
||||||
|
|
||||||
|
await setMaxAsyncTaskStacks(1024);
|
||||||
|
runWithAsyncChainSetTimeout(2, 'console.trace(42)');
|
||||||
|
dumpAsyncChainLength(await Protocol.Runtime.onceConsoleAPICalled());
|
||||||
|
|
||||||
|
await setMaxAsyncTaskStacks(1024);
|
||||||
|
runWithAsyncChainSetTimeout(5, 'console.trace(42)');
|
||||||
|
dumpAsyncChainLength(await Protocol.Runtime.onceConsoleAPICalled());
|
||||||
|
},
|
||||||
|
|
||||||
|
async function testZeroLimit() {
|
||||||
|
const limit = 0;
|
||||||
|
Protocol.Debugger.setAsyncCallStackDepth({maxDepth: 128});
|
||||||
|
|
||||||
|
await setMaxAsyncTaskStacks(limit);
|
||||||
|
runWithAsyncChainPromise(1, 'console.trace(42)');
|
||||||
|
dumpAsyncChainLength(await Protocol.Runtime.onceConsoleAPICalled());
|
||||||
|
|
||||||
|
await setMaxAsyncTaskStacks(limit);
|
||||||
|
runWithAsyncChainPromise(2, 'console.trace(42)');
|
||||||
|
dumpAsyncChainLength(await Protocol.Runtime.onceConsoleAPICalled());
|
||||||
|
|
||||||
|
await setMaxAsyncTaskStacks(limit);
|
||||||
|
runWithAsyncChainSetTimeout(1, 'console.trace(42)');
|
||||||
|
dumpAsyncChainLength(await Protocol.Runtime.onceConsoleAPICalled());
|
||||||
|
|
||||||
|
await setMaxAsyncTaskStacks(limit);
|
||||||
|
runWithAsyncChainSetTimeout(2, 'console.trace(42)');
|
||||||
|
dumpAsyncChainLength(await Protocol.Runtime.onceConsoleAPICalled());
|
||||||
|
},
|
||||||
|
|
||||||
|
async function testOneLimit() {
|
||||||
|
const limit = 1;
|
||||||
|
Protocol.Debugger.setAsyncCallStackDepth({maxDepth: 128});
|
||||||
|
|
||||||
|
await setMaxAsyncTaskStacks(limit);
|
||||||
|
runWithAsyncChainPromise(1, 'console.trace(42)');
|
||||||
|
dumpAsyncChainLength(await Protocol.Runtime.onceConsoleAPICalled());
|
||||||
|
|
||||||
|
await setMaxAsyncTaskStacks(limit);
|
||||||
|
runWithAsyncChainPromise(2, 'console.trace(42)');
|
||||||
|
dumpAsyncChainLength(await Protocol.Runtime.onceConsoleAPICalled());
|
||||||
|
|
||||||
|
await setMaxAsyncTaskStacks(limit);
|
||||||
|
runWithAsyncChainSetTimeout(1, 'console.trace(42)');
|
||||||
|
dumpAsyncChainLength(await Protocol.Runtime.onceConsoleAPICalled());
|
||||||
|
|
||||||
|
await setMaxAsyncTaskStacks(limit);
|
||||||
|
runWithAsyncChainSetTimeout(2, 'console.trace(42)');
|
||||||
|
dumpAsyncChainLength(await Protocol.Runtime.onceConsoleAPICalled());
|
||||||
|
},
|
||||||
|
|
||||||
|
async function testTwoLimit() {
|
||||||
|
const limit = 2;
|
||||||
|
Protocol.Debugger.setAsyncCallStackDepth({maxDepth: 128});
|
||||||
|
|
||||||
|
await setMaxAsyncTaskStacks(limit);
|
||||||
|
runWithAsyncChainPromise(1, 'console.trace(42)');
|
||||||
|
dumpAsyncChainLength(await Protocol.Runtime.onceConsoleAPICalled());
|
||||||
|
|
||||||
|
await setMaxAsyncTaskStacks(limit);
|
||||||
|
runWithAsyncChainPromise(2, 'console.trace(42)');
|
||||||
|
dumpAsyncChainLength(await Protocol.Runtime.onceConsoleAPICalled());
|
||||||
|
|
||||||
|
await setMaxAsyncTaskStacks(limit);
|
||||||
|
runWithAsyncChainPromise(3, 'console.trace(42)');
|
||||||
|
dumpAsyncChainLength(await Protocol.Runtime.onceConsoleAPICalled());
|
||||||
|
|
||||||
|
await setMaxAsyncTaskStacks(limit);
|
||||||
|
runWithAsyncChainSetTimeout(1, 'console.trace(42)');
|
||||||
|
dumpAsyncChainLength(await Protocol.Runtime.onceConsoleAPICalled());
|
||||||
|
|
||||||
|
await setMaxAsyncTaskStacks(limit);
|
||||||
|
runWithAsyncChainSetTimeout(2, 'console.trace(42)');
|
||||||
|
dumpAsyncChainLength(await Protocol.Runtime.onceConsoleAPICalled());
|
||||||
|
|
||||||
|
await setMaxAsyncTaskStacks(limit);
|
||||||
|
runWithAsyncChainSetTimeout(3, 'console.trace(42)');
|
||||||
|
dumpAsyncChainLength(await Protocol.Runtime.onceConsoleAPICalled());
|
||||||
|
},
|
||||||
|
|
||||||
|
async function testMoreThanTwoLimit() {
|
||||||
|
for (let limit = 3; limit <= 7; ++limit) {
|
||||||
|
Protocol.Debugger.setAsyncCallStackDepth({maxDepth: 128});
|
||||||
|
|
||||||
|
await setMaxAsyncTaskStacks(limit);
|
||||||
|
runWithAsyncChainPromise(1, 'console.trace(42)');
|
||||||
|
dumpAsyncChainLength(await Protocol.Runtime.onceConsoleAPICalled());
|
||||||
|
|
||||||
|
await setMaxAsyncTaskStacks(limit);
|
||||||
|
runWithAsyncChainPromise(2, 'console.trace(42)');
|
||||||
|
dumpAsyncChainLength(await Protocol.Runtime.onceConsoleAPICalled());
|
||||||
|
|
||||||
|
await setMaxAsyncTaskStacks(limit);
|
||||||
|
runWithAsyncChainPromise(3, 'console.trace(42)');
|
||||||
|
dumpAsyncChainLength(await Protocol.Runtime.onceConsoleAPICalled());
|
||||||
|
|
||||||
|
await setMaxAsyncTaskStacks(limit);
|
||||||
|
runWithAsyncChainSetTimeout(1, 'console.trace(42)');
|
||||||
|
dumpAsyncChainLength(await Protocol.Runtime.onceConsoleAPICalled());
|
||||||
|
|
||||||
|
await setMaxAsyncTaskStacks(limit);
|
||||||
|
runWithAsyncChainSetTimeout(2, 'console.trace(42)');
|
||||||
|
dumpAsyncChainLength(await Protocol.Runtime.onceConsoleAPICalled());
|
||||||
|
|
||||||
|
await setMaxAsyncTaskStacks(limit);
|
||||||
|
runWithAsyncChainSetTimeout(3, 'console.trace(42)');
|
||||||
|
dumpAsyncChainLength(await Protocol.Runtime.onceConsoleAPICalled());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
function runWithAsyncChainPromise(len, source) {
|
||||||
|
InspectorTest.log(`Run expression '${source}' with async chain len: ${len}`);
|
||||||
|
let then = '.then(() => 1)';
|
||||||
|
let pause = `.then(() => { ${source} })`;
|
||||||
|
Protocol.Runtime.evaluate({
|
||||||
|
expression: `Promise.resolve()${then.repeat(len - 1)}${pause}`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function runWithAsyncChainSetTimeout(len, source) {
|
||||||
|
InspectorTest.log(`Run expression '${source}' with async chain len: ${len}`);
|
||||||
|
let setTimeoutPrefix = '() => setTimeout(';
|
||||||
|
let setTimeoutSuffix = ', 0)';
|
||||||
|
Protocol.Runtime.evaluate({
|
||||||
|
expression: `setTimeout(${setTimeoutPrefix.repeat(len - 1)}'${source}'${setTimeoutSuffix.repeat(len - 1)}, 0)`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function dumpAsyncChainLength(message) {
|
||||||
|
let stackTrace = message.params.asyncStackTrace || message.params.stackTrace.parent;
|
||||||
|
let asyncChainCount = 0;
|
||||||
|
while (stackTrace) {
|
||||||
|
++asyncChainCount;
|
||||||
|
stackTrace = stackTrace.parent;
|
||||||
|
}
|
||||||
|
InspectorTest.log(`actual async chain len: ${asyncChainCount}\n`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function setMaxAsyncTaskStacks(max) {
|
||||||
|
let expression = `setMaxAsyncTaskStacks(${max})`;
|
||||||
|
InspectorTest.log(expression);
|
||||||
|
await Protocol.Runtime.evaluate({expression});
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
Checks that we report not more then maxDepth call chains.
|
||||||
|
|
||||||
|
Running test: testPaused
|
||||||
|
Actual call chain length: 8
|
||||||
|
setAsyncCallStackDepth(maxDepth): 16
|
||||||
|
reported: 8
|
||||||
|
|
||||||
|
Actual call chain length: 8
|
||||||
|
setAsyncCallStackDepth(maxDepth): 8
|
||||||
|
reported: 8
|
||||||
|
|
||||||
|
Actual call chain length: 8
|
||||||
|
setAsyncCallStackDepth(maxDepth): 7
|
||||||
|
reported: 7
|
||||||
|
|
||||||
|
Actual call chain length: 8
|
||||||
|
setAsyncCallStackDepth(maxDepth): 0
|
||||||
|
reported: 0
|
||||||
|
|
||||||
|
|
||||||
|
Running test: testConsoleTrace
|
||||||
|
Actual call chain length: 8
|
||||||
|
setAsyncCallStackDepth(maxDepth): 16
|
||||||
|
reported: 8
|
||||||
|
|
||||||
|
Actual call chain length: 8
|
||||||
|
setAsyncCallStackDepth(maxDepth): 8
|
||||||
|
reported: 8
|
||||||
|
|
||||||
|
Actual call chain length: 8
|
||||||
|
setAsyncCallStackDepth(maxDepth): 7
|
||||||
|
reported: 7
|
||||||
|
|
||||||
|
Actual call chain length: 8
|
||||||
|
setAsyncCallStackDepth(maxDepth): 0
|
||||||
|
reported: 0
|
||||||
|
|
77
test/inspector/debugger/set-async-call-stack-depth.js
Normal file
77
test/inspector/debugger/set-async-call-stack-depth.js
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
InspectorTest.log('Checks that we report not more then maxDepth call chains.');
|
||||||
|
|
||||||
|
InspectorTest.addScript(`
|
||||||
|
function promisesChain(num) {
|
||||||
|
var p = Promise.resolve();
|
||||||
|
for (var i = 0; i < num - 1; ++i) {
|
||||||
|
p = p.then(() => 42);
|
||||||
|
}
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
|
Protocol.Debugger.enable();
|
||||||
|
InspectorTest.runAsyncTestSuite([
|
||||||
|
async function testPaused() {
|
||||||
|
let callback = '() => { debugger; }';
|
||||||
|
startTest({ generated: 8, limit: 16, callback});
|
||||||
|
dumpCaptured((await Protocol.Debugger.oncePaused()).params.asyncStackTrace);
|
||||||
|
await Protocol.Debugger.resume();
|
||||||
|
|
||||||
|
startTest({ generated: 8, limit: 8, callback});
|
||||||
|
dumpCaptured((await Protocol.Debugger.oncePaused()).params.asyncStackTrace);
|
||||||
|
await Protocol.Debugger.resume();
|
||||||
|
|
||||||
|
startTest({ generated: 8, limit: 7, callback});
|
||||||
|
dumpCaptured((await Protocol.Debugger.oncePaused()).params.asyncStackTrace);
|
||||||
|
await Protocol.Debugger.resume();
|
||||||
|
|
||||||
|
startTest({ generated: 8, limit: 0, callback});
|
||||||
|
dumpCaptured((await Protocol.Debugger.oncePaused()).params.asyncStackTrace);
|
||||||
|
await Protocol.Debugger.resume();
|
||||||
|
},
|
||||||
|
|
||||||
|
async function testConsoleTrace() {
|
||||||
|
await Protocol.Runtime.enable();
|
||||||
|
let callback = '() => { console.trace(42); }';
|
||||||
|
startTest({ generated: 8, limit: 16, callback});
|
||||||
|
let msg = await Protocol.Runtime.onceConsoleAPICalled();
|
||||||
|
dumpCaptured(msg.params.stackTrace.parent);
|
||||||
|
|
||||||
|
startTest({ generated: 8, limit: 8, callback});
|
||||||
|
msg = await Protocol.Runtime.onceConsoleAPICalled();
|
||||||
|
dumpCaptured(msg.params.stackTrace.parent);
|
||||||
|
|
||||||
|
startTest({ generated: 8, limit: 7, callback});
|
||||||
|
msg = await Protocol.Runtime.onceConsoleAPICalled();
|
||||||
|
dumpCaptured(msg.params.stackTrace.parent);
|
||||||
|
|
||||||
|
startTest({ generated: 8, limit: 0, callback});
|
||||||
|
msg = await Protocol.Runtime.onceConsoleAPICalled();
|
||||||
|
dumpCaptured(msg.params.stackTrace.parent);
|
||||||
|
|
||||||
|
await Protocol.Runtime.disable();
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
function startTest(params) {
|
||||||
|
InspectorTest.log('Actual call chain length: ' + params.generated);
|
||||||
|
InspectorTest.log('setAsyncCallStackDepth(maxDepth): ' + params.limit);
|
||||||
|
|
||||||
|
Protocol.Debugger.setAsyncCallStackDepth({maxDepth: params.limit});
|
||||||
|
Protocol.Runtime.evaluate({expression:
|
||||||
|
`promisesChain(${params.generated}).then(${params.callback})`});
|
||||||
|
}
|
||||||
|
|
||||||
|
function dumpCaptured(stack) {
|
||||||
|
let count = 0;
|
||||||
|
while (stack) {
|
||||||
|
++count;
|
||||||
|
stack = stack.parent;
|
||||||
|
}
|
||||||
|
InspectorTest.log('reported: ' + count + '\n');
|
||||||
|
}
|
@ -85,6 +85,25 @@ void MessageHandler(v8::Local<v8::Message> message,
|
|||||||
inspector->createStackTrace(stack), script_id);
|
inspector->createStackTrace(stack), script_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
v8::Local<v8::String> ToString(v8::Isolate* isolate,
|
||||||
|
const v8_inspector::StringView& string) {
|
||||||
|
if (string.is8Bit())
|
||||||
|
return v8::String::NewFromOneByte(isolate, string.characters8(),
|
||||||
|
v8::NewStringType::kNormal,
|
||||||
|
static_cast<int>(string.length()))
|
||||||
|
.ToLocalChecked();
|
||||||
|
else
|
||||||
|
return v8::String::NewFromTwoByte(isolate, string.characters16(),
|
||||||
|
v8::NewStringType::kNormal,
|
||||||
|
static_cast<int>(string.length()))
|
||||||
|
.ToLocalChecked();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Print(v8::Isolate* isolate, const v8_inspector::StringView& string) {
|
||||||
|
v8::Local<v8::String> v8_string = ToString(isolate, string);
|
||||||
|
v8::String::Utf8Value utf8_string(v8_string);
|
||||||
|
fwrite(*utf8_string, sizeof(**utf8_string), utf8_string.length(), stdout);
|
||||||
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
class ConnectTask : public TaskRunner::Task {
|
class ConnectTask : public TaskRunner::Task {
|
||||||
@ -265,6 +284,10 @@ void InspectorClientImpl::setMemoryInfoForTest(
|
|||||||
memory_info_.Reset(isolate_, memory_info);
|
memory_info_.Reset(isolate_, memory_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void InspectorClientImpl::setLogConsoleApiMessageCalls(bool log) {
|
||||||
|
log_console_api_message_calls_ = log;
|
||||||
|
}
|
||||||
|
|
||||||
v8::MaybeLocal<v8::Value> InspectorClientImpl::memoryInfo(
|
v8::MaybeLocal<v8::Value> InspectorClientImpl::memoryInfo(
|
||||||
v8::Isolate* isolate, v8::Local<v8::Context>) {
|
v8::Isolate* isolate, v8::Local<v8::Context>) {
|
||||||
if (memory_info_.IsEmpty()) return v8::MaybeLocal<v8::Value>();
|
if (memory_info_.IsEmpty()) return v8::MaybeLocal<v8::Value>();
|
||||||
@ -279,6 +302,20 @@ void InspectorClientImpl::quitMessageLoopOnPause() {
|
|||||||
task_runner_->QuitMessageLoop();
|
task_runner_->QuitMessageLoop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void InspectorClientImpl::consoleAPIMessage(
|
||||||
|
int contextGroupId, v8::Isolate::MessageErrorLevel level,
|
||||||
|
const v8_inspector::StringView& message,
|
||||||
|
const v8_inspector::StringView& url, unsigned lineNumber,
|
||||||
|
unsigned columnNumber, v8_inspector::V8StackTrace* stack) {
|
||||||
|
if (!log_console_api_message_calls_) return;
|
||||||
|
Print(isolate_, message);
|
||||||
|
fprintf(stdout, " (");
|
||||||
|
Print(isolate_, url);
|
||||||
|
fprintf(stdout, ":%d:%d)", lineNumber, columnNumber);
|
||||||
|
Print(isolate_, stack->toString()->string());
|
||||||
|
fprintf(stdout, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
v8_inspector::V8Inspector* InspectorClientImpl::InspectorFromContext(
|
v8_inspector::V8Inspector* InspectorClientImpl::InspectorFromContext(
|
||||||
v8::Local<v8::Context> context) {
|
v8::Local<v8::Context> context) {
|
||||||
return InspectorClientFromContext(context)->inspector_.get();
|
return InspectorClientFromContext(context)->inspector_.get();
|
||||||
|
@ -40,6 +40,7 @@ class InspectorClientImpl : public v8_inspector::V8InspectorClient {
|
|||||||
|
|
||||||
void setCurrentTimeMSForTest(double time);
|
void setCurrentTimeMSForTest(double time);
|
||||||
void setMemoryInfoForTest(v8::Local<v8::Value> memory_info);
|
void setMemoryInfoForTest(v8::Local<v8::Value> memory_info);
|
||||||
|
void setLogConsoleApiMessageCalls(bool log);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// V8InspectorClient implementation.
|
// V8InspectorClient implementation.
|
||||||
@ -51,7 +52,12 @@ class InspectorClientImpl : public v8_inspector::V8InspectorClient {
|
|||||||
v8::Local<v8::Context>) override;
|
v8::Local<v8::Context>) override;
|
||||||
void runMessageLoopOnPause(int context_group_id) override;
|
void runMessageLoopOnPause(int context_group_id) override;
|
||||||
void quitMessageLoopOnPause() override;
|
void quitMessageLoopOnPause() override;
|
||||||
|
void consoleAPIMessage(int contextGroupId,
|
||||||
|
v8::Isolate::MessageErrorLevel level,
|
||||||
|
const v8_inspector::StringView& message,
|
||||||
|
const v8_inspector::StringView& url,
|
||||||
|
unsigned lineNumber, unsigned columnNumber,
|
||||||
|
v8_inspector::V8StackTrace*) override;
|
||||||
friend class SendMessageToBackendTask;
|
friend class SendMessageToBackendTask;
|
||||||
|
|
||||||
friend class ConnectTask;
|
friend class ConnectTask;
|
||||||
@ -75,6 +81,7 @@ class InspectorClientImpl : public v8_inspector::V8InspectorClient {
|
|||||||
|
|
||||||
bool current_time_set_for_test_ = false;
|
bool current_time_set_for_test_ = false;
|
||||||
double current_time_ = 0.0;
|
double current_time_ = 0.0;
|
||||||
|
bool log_console_api_message_calls_ = false;
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(InspectorClientImpl);
|
DISALLOW_COPY_AND_ASSIGN(InspectorClientImpl);
|
||||||
};
|
};
|
||||||
|
@ -61,6 +61,7 @@ class UtilsExtension : public v8::Extension {
|
|||||||
"native function schedulePauseOnNextStatement();"
|
"native function schedulePauseOnNextStatement();"
|
||||||
"native function cancelPauseOnNextStatement();"
|
"native function cancelPauseOnNextStatement();"
|
||||||
"native function reconnect();"
|
"native function reconnect();"
|
||||||
|
"native function setLogConsoleApiMessageCalls();"
|
||||||
"native function createContextGroup();") {}
|
"native function createContextGroup();") {}
|
||||||
virtual v8::Local<v8::FunctionTemplate> GetNativeFunctionTemplate(
|
virtual v8::Local<v8::FunctionTemplate> GetNativeFunctionTemplate(
|
||||||
v8::Isolate* isolate, v8::Local<v8::String> name) {
|
v8::Isolate* isolate, v8::Local<v8::String> name) {
|
||||||
@ -136,6 +137,14 @@ class UtilsExtension : public v8::Extension {
|
|||||||
.ToLocalChecked())
|
.ToLocalChecked())
|
||||||
.FromJust()) {
|
.FromJust()) {
|
||||||
return v8::FunctionTemplate::New(isolate, UtilsExtension::Reconnect);
|
return v8::FunctionTemplate::New(isolate, UtilsExtension::Reconnect);
|
||||||
|
} else if (name->Equals(context,
|
||||||
|
v8::String::NewFromUtf8(
|
||||||
|
isolate, "setLogConsoleApiMessageCalls",
|
||||||
|
v8::NewStringType::kNormal)
|
||||||
|
.ToLocalChecked())
|
||||||
|
.FromJust()) {
|
||||||
|
return v8::FunctionTemplate::New(
|
||||||
|
isolate, UtilsExtension::SetLogConsoleApiMessageCalls);
|
||||||
} else if (name->Equals(context, v8::String::NewFromUtf8(
|
} else if (name->Equals(context, v8::String::NewFromUtf8(
|
||||||
isolate, "createContextGroup",
|
isolate, "createContextGroup",
|
||||||
v8::NewStringType::kNormal)
|
v8::NewStringType::kNormal)
|
||||||
@ -318,6 +327,16 @@ class UtilsExtension : public v8::Extension {
|
|||||||
ready_semaphore.Wait();
|
ready_semaphore.Wait();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void SetLogConsoleApiMessageCalls(
|
||||||
|
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||||
|
if (args.Length() != 1 || !args[0]->IsBoolean()) {
|
||||||
|
fprintf(stderr, "Internal error: setLogConsoleApiMessageCalls(bool).");
|
||||||
|
Exit();
|
||||||
|
}
|
||||||
|
inspector_client_->setLogConsoleApiMessageCalls(
|
||||||
|
args[0].As<v8::Boolean>()->Value());
|
||||||
|
}
|
||||||
|
|
||||||
static void CreateContextGroup(
|
static void CreateContextGroup(
|
||||||
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||||
if (args.Length() != 0) {
|
if (args.Length() != 0) {
|
||||||
|
@ -57,6 +57,8 @@ var utils = {};
|
|||||||
this.cancelPauseOnNextStatement = null;
|
this.cancelPauseOnNextStatement = null;
|
||||||
utils.reconnect = reconnect;
|
utils.reconnect = reconnect;
|
||||||
this.reconnect = null;
|
this.reconnect = null;
|
||||||
|
utils.setLogConsoleApiMessageCalls = setLogConsoleApiMessageCalls;
|
||||||
|
this.setLogConsoleApiMessageCalls = null;
|
||||||
utils.createContextGroup = createContextGroup;
|
utils.createContextGroup = createContextGroup;
|
||||||
this.createContextGroup = null;
|
this.createContextGroup = null;
|
||||||
})();
|
})();
|
||||||
|
@ -0,0 +1,8 @@
|
|||||||
|
Checks that we passed correct arguments in V8InspectorClient::consoleAPIMessage. Note: lines and columns are 1-based.
|
||||||
|
42 (:1:9)
|
||||||
|
at (anonymous function) (:1:9)
|
||||||
|
239 (:13:15)
|
||||||
|
at b (:13:15)
|
||||||
|
at a (:15:5)
|
||||||
|
at consoleTrace (:17:3)
|
||||||
|
at (anonymous function) (:1:1)
|
28
test/inspector/runtime/client-console-api-message.js
Normal file
28
test/inspector/runtime/client-console-api-message.js
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
InspectorTest.log('Checks that we passed correct arguments in ' +
|
||||||
|
'V8InspectorClient::consoleAPIMessage. Note: lines and columns are 1-based.');
|
||||||
|
|
||||||
|
InspectorTest.addScript(`
|
||||||
|
function consoleTrace() {
|
||||||
|
function a() {
|
||||||
|
function b() {
|
||||||
|
console.trace(239);
|
||||||
|
}
|
||||||
|
b();
|
||||||
|
}
|
||||||
|
a();
|
||||||
|
}
|
||||||
|
`, 8, 26);
|
||||||
|
|
||||||
|
Protocol.Runtime.enable();
|
||||||
|
utils.setLogConsoleApiMessageCalls(true);
|
||||||
|
(async function test() {
|
||||||
|
Protocol.Runtime.evaluate({expression: 'console.log(42)'});
|
||||||
|
await Protocol.Runtime.onceConsoleAPICalled()
|
||||||
|
Protocol.Runtime.evaluate({expression: 'consoleTrace()'});
|
||||||
|
await Protocol.Runtime.onceConsoleAPICalled()
|
||||||
|
InspectorTest.completeTest();
|
||||||
|
})();
|
Loading…
Reference in New Issue
Block a user