[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;
|
||||
if (title.isEmpty()) {
|
||||
std::unique_ptr<V8StackTraceImpl> stackTrace =
|
||||
V8StackTraceImpl::capture(m_inspector->debugger(), 0, 1);
|
||||
V8StackTraceImpl::capture(m_inspector->debugger(), helper.groupId(), 1);
|
||||
if (stackTrace && !stackTrace->isEmpty()) {
|
||||
identifier = toString16(stackTrace->topSourceURL()) + ":" +
|
||||
String16::fromInteger(stackTrace->topLineNumber());
|
||||
|
@ -34,7 +34,6 @@ using protocol::Debugger::BreakpointId;
|
||||
using protocol::Debugger::CallFrame;
|
||||
using protocol::Runtime::ExceptionDetails;
|
||||
using protocol::Runtime::ScriptId;
|
||||
using protocol::Runtime::StackTrace;
|
||||
using protocol::Runtime::RemoteObject;
|
||||
|
||||
namespace DebuggerAgentState {
|
||||
@ -598,7 +597,8 @@ Response V8DebuggerAgentImpl::searchInContent(
|
||||
Response V8DebuggerAgentImpl::setScriptSource(
|
||||
const String16& scriptId, const String16& newContent, Maybe<bool> dryRun,
|
||||
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) {
|
||||
if (!enabled()) return Response::Error(kDebuggerNotEnabled);
|
||||
|
||||
@ -631,7 +631,7 @@ Response V8DebuggerAgentImpl::setScriptSource(
|
||||
Response V8DebuggerAgentImpl::restartFrame(
|
||||
const String16& callFrameId,
|
||||
std::unique_ptr<Array<CallFrame>>* newCallFrames,
|
||||
Maybe<StackTrace>* asyncStackTrace) {
|
||||
Maybe<protocol::Runtime::StackTrace>* asyncStackTrace) {
|
||||
if (!isPaused()) return Response::Error(kDebuggerNotPaused);
|
||||
InjectedScript::CallFrameScope scope(m_inspector, m_session->contextGroupId(),
|
||||
callFrameId);
|
||||
@ -1028,9 +1028,14 @@ Response V8DebuggerAgentImpl::currentCallFrames(
|
||||
return Response::OK();
|
||||
}
|
||||
|
||||
std::unique_ptr<StackTrace> V8DebuggerAgentImpl::currentAsyncStackTrace() {
|
||||
if (!isPaused()) return nullptr;
|
||||
return V8StackTraceImpl::buildInspectorObjectForTail(m_debugger);
|
||||
std::unique_ptr<protocol::Runtime::StackTrace>
|
||||
V8DebuggerAgentImpl::currentAsyncStackTrace() {
|
||||
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(); }
|
||||
|
@ -23,7 +23,6 @@ class V8DebuggerScript;
|
||||
class V8InspectorImpl;
|
||||
class V8InspectorSessionImpl;
|
||||
class V8Regex;
|
||||
class V8StackTraceImpl;
|
||||
|
||||
using protocol::Maybe;
|
||||
using protocol::Response;
|
||||
|
@ -21,10 +21,7 @@ namespace v8_inspector {
|
||||
|
||||
namespace {
|
||||
|
||||
// Based on DevTools frontend measurement, with asyncCallStackDepth = 4,
|
||||
// average async call stack tail requires ~1 Kb. Let's reserve ~ 128 Mb
|
||||
// for async stacks.
|
||||
static const int kMaxAsyncTaskStacks = 128 * 1024;
|
||||
static const int kMaxAsyncTaskStacks = 1024 * 1024;
|
||||
|
||||
inline v8::Local<v8::Boolean> v8Boolean(bool value, v8::Isolate* isolate) {
|
||||
return value ? v8::True(isolate) : v8::False(isolate);
|
||||
@ -167,7 +164,6 @@ V8Debugger::V8Debugger(v8::Isolate* isolate, V8InspectorImpl* inspector)
|
||||
m_runningNestedMessageLoop(false),
|
||||
m_ignoreScriptParsedEventsCounter(0),
|
||||
m_maxAsyncCallStacks(kMaxAsyncTaskStacks),
|
||||
m_lastTaskId(0),
|
||||
m_maxAsyncCallStackDepth(0),
|
||||
m_pauseOnExceptionsState(v8::debug::NoBreakOnException),
|
||||
m_wasmTranslation(isolate) {}
|
||||
@ -662,10 +658,6 @@ void V8Debugger::PromiseEventOccurred(v8::debug::PromiseDebugActionType type,
|
||||
case v8::debug::kDebugEnqueuePromiseReject:
|
||||
asyncTaskScheduledForStack("Promise.reject", task, true);
|
||||
break;
|
||||
case v8::debug::kDebugPromiseCollected:
|
||||
asyncTaskCanceledForStack(task);
|
||||
asyncTaskCanceledForStepping(task);
|
||||
break;
|
||||
case v8::debug::kDebugWillHandle:
|
||||
asyncTaskStartedForStack(task);
|
||||
asyncTaskStartedForStepping(task);
|
||||
@ -674,17 +666,20 @@ void V8Debugger::PromiseEventOccurred(v8::debug::PromiseDebugActionType type,
|
||||
asyncTaskFinishedForStack(task);
|
||||
asyncTaskFinishedForStepping(task);
|
||||
break;
|
||||
case v8::debug::kDebugPromiseCollected:
|
||||
asyncTaskCanceledForStack(task);
|
||||
asyncTaskCanceledForStepping(task);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
V8StackTraceImpl* V8Debugger::currentAsyncCallChain() {
|
||||
if (!m_currentStacks.size()) return nullptr;
|
||||
return m_currentStacks.back().get();
|
||||
std::shared_ptr<AsyncStackTrace> V8Debugger::currentAsyncParent() {
|
||||
return m_currentAsyncParent.empty() ? nullptr : m_currentAsyncParent.back();
|
||||
}
|
||||
|
||||
V8StackTraceImpl* V8Debugger::currentAsyncTaskCreationStack() {
|
||||
if (!m_currentCreationStacks.size()) return nullptr;
|
||||
return m_currentCreationStacks.back().get();
|
||||
std::shared_ptr<AsyncStackTrace> V8Debugger::currentAsyncCreation() {
|
||||
return m_currentAsyncCreation.empty() ? nullptr
|
||||
: m_currentAsyncCreation.back();
|
||||
}
|
||||
|
||||
void V8Debugger::compileDebuggerScript() {
|
||||
@ -825,8 +820,8 @@ v8::MaybeLocal<v8::Array> V8Debugger::internalProperties(
|
||||
}
|
||||
|
||||
std::unique_ptr<V8StackTraceImpl> V8Debugger::createStackTrace(
|
||||
v8::Local<v8::StackTrace> stackTrace) {
|
||||
return V8StackTraceImpl::create(this, currentContextGroupId(), stackTrace,
|
||||
v8::Local<v8::StackTrace> v8StackTrace) {
|
||||
return V8StackTraceImpl::create(this, currentContextGroupId(), v8StackTrace,
|
||||
V8StackTraceImpl::maxCallStackSizeToCapture);
|
||||
}
|
||||
|
||||
@ -847,31 +842,18 @@ void V8Debugger::setAsyncCallStackDepth(V8DebuggerAgentImpl* agent, int depth) {
|
||||
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) {
|
||||
if (!m_maxAsyncCallStackDepth) return;
|
||||
if (parentTask) m_parentTask[task] = parentTask;
|
||||
v8::HandleScope scope(m_isolate);
|
||||
// We don't need to pass context group id here because we get this callback
|
||||
// from V8 for promise events only.
|
||||
// Passing one as maxStackSize forces no async chain for the new stack and
|
||||
// allows us to not grow exponentially.
|
||||
std::unique_ptr<V8StackTraceImpl> creationStack =
|
||||
V8StackTraceImpl::capture(this, 0, 1, String16());
|
||||
if (creationStack && !creationStack->isEmpty()) {
|
||||
m_asyncTaskCreationStacks[task] = std::move(creationStack);
|
||||
registerAsyncTaskIfNeeded(task);
|
||||
std::shared_ptr<AsyncStackTrace> asyncCreation =
|
||||
AsyncStackTrace::capture(this, currentContextGroupId(), String16(), 1);
|
||||
// Passing one as maxStackSize forces no async chain for the new stack.
|
||||
if (asyncCreation && !asyncCreation->isEmpty()) {
|
||||
m_asyncTaskCreationStacks[task] = asyncCreation;
|
||||
m_allAsyncStacks.push_back(asyncCreation);
|
||||
++m_asyncStacksCount;
|
||||
collectOldAsyncStacksIfNeeded();
|
||||
}
|
||||
}
|
||||
|
||||
@ -900,13 +882,16 @@ void V8Debugger::asyncTaskScheduledForStack(const String16& taskName,
|
||||
void* task, bool recurring) {
|
||||
if (!m_maxAsyncCallStackDepth) return;
|
||||
v8::HandleScope scope(m_isolate);
|
||||
std::unique_ptr<V8StackTraceImpl> chain = V8StackTraceImpl::capture(
|
||||
this, currentContextGroupId(),
|
||||
V8StackTraceImpl::maxCallStackSizeToCapture, taskName);
|
||||
if (chain) {
|
||||
m_asyncTaskStacks[task] = std::move(chain);
|
||||
std::shared_ptr<AsyncStackTrace> asyncStack =
|
||||
AsyncStackTrace::capture(this, currentContextGroupId(), taskName,
|
||||
V8StackTraceImpl::maxCallStackSizeToCapture);
|
||||
if (asyncStack) {
|
||||
m_asyncTaskStacks[task] = asyncStack;
|
||||
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_parentTask.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) {
|
||||
@ -935,28 +916,28 @@ void V8Debugger::asyncTaskStartedForStack(void* task) {
|
||||
// - asyncTaskCanceled <-- canceled before finished
|
||||
// <-- async stack requested here -->
|
||||
// - asyncTaskFinished
|
||||
std::unique_ptr<V8StackTraceImpl> stack;
|
||||
if (stackIt != m_asyncTaskStacks.end() && stackIt->second)
|
||||
stack = stackIt->second->cloneImpl();
|
||||
std::shared_ptr<AsyncStackTrace> asyncParent;
|
||||
if (stackIt != m_asyncTaskStacks.end()) asyncParent = stackIt->second.lock();
|
||||
auto itCreation = m_asyncTaskCreationStacks.find(task);
|
||||
if (stack && itCreation != m_asyncTaskCreationStacks.end()) {
|
||||
m_currentCreationStacks.push_back(itCreation->second->cloneImpl());
|
||||
if (asyncParent && itCreation != m_asyncTaskCreationStacks.end()) {
|
||||
m_currentAsyncCreation.push_back(itCreation->second.lock());
|
||||
} 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) {
|
||||
if (!m_maxAsyncCallStackDepth) return;
|
||||
// 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);
|
||||
m_currentTasks.pop_back();
|
||||
|
||||
DCHECK(m_currentStacks.size() == m_currentCreationStacks.size());
|
||||
m_currentStacks.pop_back();
|
||||
m_currentCreationStacks.pop_back();
|
||||
DCHECK(m_currentAsyncParent.size() == m_currentAsyncCreation.size());
|
||||
m_currentAsyncParent.pop_back();
|
||||
m_currentAsyncCreation.pop_back();
|
||||
|
||||
if (m_recurringTasks.find(task) == m_recurringTasks.end()) {
|
||||
asyncTaskCanceledForStack(task);
|
||||
}
|
||||
@ -993,14 +974,14 @@ void V8Debugger::asyncTaskCanceledForStepping(void* task) {
|
||||
void V8Debugger::allAsyncTasksCanceled() {
|
||||
m_asyncTaskStacks.clear();
|
||||
m_recurringTasks.clear();
|
||||
m_currentStacks.clear();
|
||||
m_currentCreationStacks.clear();
|
||||
m_currentAsyncParent.clear();
|
||||
m_currentAsyncCreation.clear();
|
||||
m_currentTasks.clear();
|
||||
m_parentTask.clear();
|
||||
m_asyncTaskCreationStacks.clear();
|
||||
m_idToTask.clear();
|
||||
m_taskToId.clear();
|
||||
m_lastTaskId = 0;
|
||||
|
||||
m_allAsyncStacks.clear();
|
||||
m_asyncStacksCount = 0;
|
||||
}
|
||||
|
||||
void V8Debugger::muteScriptParsedEvents() {
|
||||
@ -1020,11 +1001,10 @@ std::unique_ptr<V8StackTraceImpl> V8Debugger::captureStackTrace(
|
||||
int contextGroupId = currentContextGroupId();
|
||||
if (!contextGroupId) return nullptr;
|
||||
|
||||
size_t stackSize =
|
||||
fullStack ? V8StackTraceImpl::maxCallStackSizeToCapture : 1;
|
||||
if (m_inspector->enabledRuntimeAgentForGroup(contextGroupId))
|
||||
int stackSize = 1;
|
||||
if (fullStack || m_inspector->enabledRuntimeAgentForGroup(contextGroupId)) {
|
||||
stackSize = V8StackTraceImpl::maxCallStackSizeToCapture;
|
||||
|
||||
}
|
||||
return V8StackTraceImpl::capture(this, contextGroupId, stackSize);
|
||||
}
|
||||
|
||||
@ -1033,4 +1013,30 @@ int V8Debugger::currentContextGroupId() {
|
||||
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
|
||||
|
@ -5,6 +5,7 @@
|
||||
#ifndef V8_INSPECTOR_V8DEBUGGER_H_
|
||||
#define V8_INSPECTOR_V8DEBUGGER_H_
|
||||
|
||||
#include <list>
|
||||
#include <vector>
|
||||
|
||||
#include "src/base/macros.h"
|
||||
@ -20,7 +21,9 @@
|
||||
|
||||
namespace v8_inspector {
|
||||
|
||||
class AsyncStackTrace;
|
||||
struct ScriptBreakpoint;
|
||||
class V8Debugger;
|
||||
class V8DebuggerAgentImpl;
|
||||
class V8InspectorImpl;
|
||||
class V8StackTraceImpl;
|
||||
@ -35,6 +38,7 @@ class V8Debugger : public v8::debug::DebugDelegate {
|
||||
~V8Debugger();
|
||||
|
||||
bool enabled() const;
|
||||
v8::Isolate* isolate() const { return m_isolate; }
|
||||
|
||||
String16 setBreakpoint(const ScriptBreakpoint&, int* actualLineNumber,
|
||||
int* actualColumnNumber);
|
||||
@ -76,9 +80,11 @@ class V8Debugger : public v8::debug::DebugDelegate {
|
||||
v8::Local<v8::Context> pausedContext() { return m_pausedContext; }
|
||||
|
||||
int maxAsyncCallChainDepth() { return m_maxAsyncCallStackDepth; }
|
||||
V8StackTraceImpl* currentAsyncCallChain();
|
||||
V8StackTraceImpl* currentAsyncTaskCreationStack();
|
||||
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> captureStackTrace(bool fullStack);
|
||||
|
||||
@ -99,7 +105,7 @@ class V8Debugger : public v8::debug::DebugDelegate {
|
||||
|
||||
WasmTranslation* wasmTranslation() { return &m_wasmTranslation; }
|
||||
|
||||
void setMaxAsyncTaskStacksForTest(int limit) { m_maxAsyncCallStacks = limit; }
|
||||
void setMaxAsyncTaskStacksForTest(int limit);
|
||||
|
||||
private:
|
||||
void compileDebuggerScript();
|
||||
@ -144,8 +150,6 @@ class V8Debugger : public v8::debug::DebugDelegate {
|
||||
void asyncTaskFinishedForStepping(void* task);
|
||||
void asyncTaskCanceledForStepping(void* task);
|
||||
|
||||
void registerAsyncTaskIfNeeded(void* task);
|
||||
|
||||
// v8::debug::DebugEventListener implementation.
|
||||
void PromiseEventOccurred(v8::debug::PromiseDebugActionType type, int id,
|
||||
int parentId, bool createdByUser) override;
|
||||
@ -178,18 +182,24 @@ class V8Debugger : public v8::debug::DebugDelegate {
|
||||
int m_targetContextGroupId = 0;
|
||||
|
||||
using AsyncTaskToStackTrace =
|
||||
protocol::HashMap<void*, std::unique_ptr<V8StackTraceImpl>>;
|
||||
protocol::HashMap<void*, std::weak_ptr<AsyncStackTrace>>;
|
||||
AsyncTaskToStackTrace m_asyncTaskStacks;
|
||||
AsyncTaskToStackTrace m_asyncTaskCreationStacks;
|
||||
int m_maxAsyncCallStacks;
|
||||
std::map<int, void*> m_idToTask;
|
||||
std::unordered_map<void*, int> m_taskToId;
|
||||
int m_lastTaskId;
|
||||
protocol::HashSet<void*> m_recurringTasks;
|
||||
int m_maxAsyncCallStackDepth;
|
||||
|
||||
std::vector<void*> m_currentTasks;
|
||||
std::vector<std::unique_ptr<V8StackTraceImpl>> m_currentStacks;
|
||||
std::vector<std::unique_ptr<V8StackTraceImpl>> m_currentCreationStacks;
|
||||
std::vector<std::shared_ptr<AsyncStackTrace>> m_currentAsyncParent;
|
||||
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<void*, void*> m_parentTask;
|
||||
protocol::HashMap<void*, void*> m_firstNextTask;
|
||||
|
@ -4,12 +4,10 @@
|
||||
|
||||
#include "src/inspector/v8-stack-trace-impl.h"
|
||||
|
||||
#include "src/inspector/string-util.h"
|
||||
#include "src/inspector/v8-debugger-agent-impl.h"
|
||||
#include "src/inspector/v8-debugger.h"
|
||||
#include "src/inspector/v8-inspector-impl.h"
|
||||
#include <algorithm>
|
||||
|
||||
#include "include/v8-version.h"
|
||||
#include "src/inspector/v8-debugger.h"
|
||||
#include "src/inspector/wasm-translation.h"
|
||||
|
||||
namespace v8_inspector {
|
||||
|
||||
@ -17,77 +15,115 @@ namespace {
|
||||
|
||||
static const v8::StackTrace::StackTraceOptions stackTraceOptions =
|
||||
static_cast<v8::StackTrace::StackTraceOptions>(
|
||||
v8::StackTrace::kLineNumber | v8::StackTrace::kColumnOffset |
|
||||
v8::StackTrace::kScriptId | v8::StackTrace::kScriptNameOrSourceURL |
|
||||
v8::StackTrace::kFunctionName |
|
||||
v8::StackTrace::kDetailed |
|
||||
v8::StackTrace::kExposeFramesAcrossSecurityOrigins);
|
||||
|
||||
V8StackTraceImpl::Frame toFrame(v8::Local<v8::StackFrame> frame,
|
||||
WasmTranslation* wasmTranslation) {
|
||||
String16 scriptId = String16::fromInteger(frame->GetScriptId());
|
||||
String16 sourceName;
|
||||
v8::Local<v8::String> sourceNameValue(frame->GetScriptNameOrSourceURL());
|
||||
if (!sourceNameValue.IsEmpty())
|
||||
sourceName = toProtocolString(sourceNameValue);
|
||||
|
||||
String16 functionName;
|
||||
v8::Local<v8::String> functionNameValue(frame->GetFunctionName());
|
||||
if (!functionNameValue.IsEmpty())
|
||||
functionName = toProtocolString(functionNameValue);
|
||||
|
||||
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);
|
||||
std::vector<V8StackTraceImpl::Frame> toFramesVector(
|
||||
V8Debugger* debugger, v8::Local<v8::StackTrace> v8StackTrace,
|
||||
int maxStackSize) {
|
||||
DCHECK(debugger->isolate()->InContext());
|
||||
int frameCount = std::min(v8StackTrace->GetFrameCount(), maxStackSize);
|
||||
std::vector<V8StackTraceImpl::Frame> frames;
|
||||
for (int i = 0; i < frameCount; ++i) {
|
||||
v8::Local<v8::StackFrame> v8Frame = v8StackTrace->GetFrame(i);
|
||||
frames.emplace_back(v8Frame);
|
||||
// TODO(clemensh): Figure out a way to do this translation only right before
|
||||
// sending the stack trace over wire.
|
||||
if (v8Frame->IsWasm()) frames.back().translate(debugger->wasmTranslation());
|
||||
}
|
||||
return V8StackTraceImpl::Frame(functionName, scriptId, sourceName,
|
||||
sourceLineNumber + 1, sourceColumn + 1);
|
||||
return frames;
|
||||
}
|
||||
|
||||
void toFramesVector(v8::Local<v8::StackTrace> stackTrace,
|
||||
std::vector<V8StackTraceImpl::Frame>& frames,
|
||||
size_t maxStackSize, v8::Isolate* isolate,
|
||||
V8Debugger* debugger) {
|
||||
DCHECK(isolate->InContext());
|
||||
int frameCount = stackTrace->GetFrameCount();
|
||||
if (frameCount > static_cast<int>(maxStackSize))
|
||||
frameCount = static_cast<int>(maxStackSize);
|
||||
WasmTranslation* wasmTranslation = debugger->wasmTranslation();
|
||||
for (int i = 0; i < frameCount; i++) {
|
||||
v8::Local<v8::StackFrame> stackFrame = stackTrace->GetFrame(i);
|
||||
frames.push_back(toFrame(stackFrame, wasmTranslation));
|
||||
void calculateAsyncChain(V8Debugger* debugger, int contextGroupId,
|
||||
std::shared_ptr<AsyncStackTrace>* asyncParent,
|
||||
std::shared_ptr<AsyncStackTrace>* asyncCreation,
|
||||
int* maxAsyncDepth) {
|
||||
*asyncParent = debugger->currentAsyncParent();
|
||||
*asyncCreation = debugger->currentAsyncCreation();
|
||||
if (maxAsyncDepth) *maxAsyncDepth = debugger->maxAsyncCallChainDepth();
|
||||
|
||||
DCHECK(!*asyncParent || !*asyncCreation ||
|
||||
(*asyncParent)->contextGroupId() ==
|
||||
(*asyncCreation)->contextGroupId());
|
||||
// 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
|
||||
|
||||
V8StackTraceImpl::Frame::Frame(const String16& functionName,
|
||||
const String16& scriptId,
|
||||
const String16& scriptName, int lineNumber,
|
||||
int column)
|
||||
: m_functionName(functionName),
|
||||
m_scriptId(scriptId),
|
||||
m_scriptName(scriptName),
|
||||
m_lineNumber(lineNumber),
|
||||
m_columnNumber(column) {
|
||||
DCHECK(m_lineNumber != v8::Message::kNoLineNumberInfo);
|
||||
DCHECK(m_columnNumber != v8::Message::kNoColumnInfo);
|
||||
V8StackTraceImpl::Frame::Frame(v8::Local<v8::StackFrame> v8Frame)
|
||||
: m_functionName(toProtocolString(v8Frame->GetFunctionName())),
|
||||
m_scriptId(String16::fromInteger(v8Frame->GetScriptId())),
|
||||
m_sourceURL(toProtocolString(v8Frame->GetScriptNameOrSourceURL())),
|
||||
m_lineNumber(v8Frame->GetLineNumber() - 1),
|
||||
m_columnNumber(v8Frame->GetColumn() - 1) {
|
||||
DCHECK(m_lineNumber + 1 != v8::Message::kNoLineNumberInfo);
|
||||
DCHECK(m_columnNumber + 1 != v8::Message::kNoColumnInfo);
|
||||
}
|
||||
|
||||
// buildInspectorObject() and SourceLocation's toTracedValue() should set the
|
||||
// same fields.
|
||||
// If either of them is modified, the other should be also modified.
|
||||
void V8StackTraceImpl::Frame::translate(WasmTranslation* wasmTranslation) {
|
||||
wasmTranslation->TranslateWasmScriptLocationToProtocolLocation(
|
||||
&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>
|
||||
V8StackTraceImpl::Frame::buildInspectorObject() const {
|
||||
return protocol::Runtime::CallFrame::create()
|
||||
.setFunctionName(m_functionName)
|
||||
.setScriptId(m_scriptId)
|
||||
.setUrl(m_scriptName)
|
||||
.setLineNumber(m_lineNumber - 1)
|
||||
.setColumnNumber(m_columnNumber - 1)
|
||||
.setUrl(m_sourceURL)
|
||||
.setLineNumber(m_lineNumber)
|
||||
.setColumnNumber(m_columnNumber)
|
||||
.build();
|
||||
}
|
||||
|
||||
@ -95,196 +131,96 @@ V8StackTraceImpl::Frame::buildInspectorObject() const {
|
||||
void V8StackTraceImpl::setCaptureStackTraceForUncaughtExceptions(
|
||||
v8::Isolate* isolate, bool capture) {
|
||||
isolate->SetCaptureStackTraceForUncaughtExceptions(
|
||||
capture, V8StackTraceImpl::maxCallStackSizeToCapture, stackTraceOptions);
|
||||
capture, V8StackTraceImpl::maxCallStackSizeToCapture);
|
||||
}
|
||||
|
||||
// static
|
||||
std::unique_ptr<V8StackTraceImpl> V8StackTraceImpl::create(
|
||||
V8Debugger* debugger, int contextGroupId,
|
||||
v8::Local<v8::StackTrace> stackTrace, size_t maxStackSize,
|
||||
const String16& description) {
|
||||
v8::Local<v8::StackTrace> v8StackTrace, int maxStackSize) {
|
||||
DCHECK(debugger);
|
||||
v8::Isolate* isolate = debugger->inspector()->isolate();
|
||||
|
||||
v8::Isolate* isolate = debugger->isolate();
|
||||
v8::HandleScope scope(isolate);
|
||||
|
||||
std::vector<V8StackTraceImpl::Frame> frames;
|
||||
if (!stackTrace.IsEmpty()) {
|
||||
toFramesVector(stackTrace, frames, maxStackSize, isolate, debugger);
|
||||
if (!v8StackTrace.IsEmpty() && v8StackTrace->GetFrameCount()) {
|
||||
frames = toFramesVector(debugger, v8StackTrace, maxStackSize);
|
||||
}
|
||||
|
||||
int maxAsyncCallChainDepth = 1;
|
||||
V8StackTraceImpl* asyncCallChain = nullptr;
|
||||
V8StackTraceImpl* creationStack = nullptr;
|
||||
if (maxStackSize > 1) {
|
||||
asyncCallChain = debugger->currentAsyncCallChain();
|
||||
creationStack = debugger->currentAsyncTaskCreationStack();
|
||||
maxAsyncCallChainDepth = debugger->maxAsyncCallChainDepth();
|
||||
}
|
||||
|
||||
// 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;
|
||||
int maxAsyncDepth = 0;
|
||||
std::shared_ptr<AsyncStackTrace> asyncParent;
|
||||
std::shared_ptr<AsyncStackTrace> asyncCreation;
|
||||
calculateAsyncChain(debugger, contextGroupId, &asyncParent, &asyncCreation,
|
||||
&maxAsyncDepth);
|
||||
if (frames.empty() && !asyncCreation && !asyncParent) return nullptr;
|
||||
return std::unique_ptr<V8StackTraceImpl>(
|
||||
new V8StackTraceImpl(frames, maxAsyncDepth, asyncParent, asyncCreation));
|
||||
}
|
||||
|
||||
// static
|
||||
std::unique_ptr<V8StackTraceImpl> V8StackTraceImpl::capture(
|
||||
V8Debugger* debugger, int contextGroupId, size_t maxStackSize,
|
||||
const String16& description) {
|
||||
V8Debugger* debugger, int contextGroupId, int maxStackSize) {
|
||||
DCHECK(debugger);
|
||||
v8::Isolate* isolate = debugger->inspector()->isolate();
|
||||
v8::Isolate* isolate = debugger->isolate();
|
||||
v8::HandleScope handleScope(isolate);
|
||||
v8::Local<v8::StackTrace> stackTrace;
|
||||
v8::Local<v8::StackTrace> v8StackTrace;
|
||||
if (isolate->InContext()) {
|
||||
stackTrace = v8::StackTrace::CurrentStackTrace(
|
||||
isolate, static_cast<int>(maxStackSize), stackTraceOptions);
|
||||
v8StackTrace = v8::StackTrace::CurrentStackTrace(isolate, maxStackSize,
|
||||
stackTraceOptions);
|
||||
}
|
||||
return V8StackTraceImpl::create(debugger, contextGroupId, stackTrace,
|
||||
maxStackSize, description);
|
||||
return V8StackTraceImpl::create(debugger, contextGroupId, v8StackTrace,
|
||||
maxStackSize);
|
||||
}
|
||||
|
||||
std::unique_ptr<V8StackTraceImpl> V8StackTraceImpl::cloneImpl() {
|
||||
std::vector<Frame> framesCopy(m_frames);
|
||||
std::unique_ptr<V8StackTraceImpl> copy(
|
||||
new V8StackTraceImpl(m_contextGroupId, m_description, framesCopy,
|
||||
m_parent ? m_parent->cloneImpl() : nullptr,
|
||||
m_creation ? m_creation->cloneImpl() : nullptr));
|
||||
return copy;
|
||||
}
|
||||
|
||||
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(
|
||||
const std::vector<Frame> frames, int maxAsyncDepth,
|
||||
std::shared_ptr<AsyncStackTrace> asyncParent,
|
||||
std::shared_ptr<AsyncStackTrace> asyncCreation)
|
||||
: m_frames(frames),
|
||||
m_maxAsyncDepth(maxAsyncDepth),
|
||||
m_asyncParent(asyncParent),
|
||||
m_asyncCreation(asyncCreation) {}
|
||||
|
||||
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 {
|
||||
DCHECK(m_frames.size());
|
||||
return toStringView(m_frames[0].m_scriptName);
|
||||
return toStringView(m_frames[0].sourceURL());
|
||||
}
|
||||
|
||||
int V8StackTraceImpl::topLineNumber() const {
|
||||
DCHECK(m_frames.size());
|
||||
return m_frames[0].m_lineNumber;
|
||||
return m_frames[0].lineNumber() + 1;
|
||||
}
|
||||
|
||||
int V8StackTraceImpl::topColumnNumber() const {
|
||||
DCHECK(m_frames.size());
|
||||
return m_frames[0].m_columnNumber;
|
||||
}
|
||||
|
||||
StringView V8StackTraceImpl::topFunctionName() const {
|
||||
DCHECK(m_frames.size());
|
||||
return toStringView(m_frames[0].m_functionName);
|
||||
return m_frames[0].columnNumber() + 1;
|
||||
}
|
||||
|
||||
StringView V8StackTraceImpl::topScriptId() const {
|
||||
DCHECK(m_frames.size());
|
||||
return toStringView(m_frames[0].m_scriptId);
|
||||
return toStringView(m_frames[0].scriptId());
|
||||
}
|
||||
|
||||
StringView V8StackTraceImpl::topFunctionName() const {
|
||||
return toStringView(m_frames[0].functionName());
|
||||
}
|
||||
|
||||
std::unique_ptr<protocol::Runtime::StackTrace>
|
||||
V8StackTraceImpl::buildInspectorObjectImpl() const {
|
||||
return buildInspectorObjectImpl(nullptr);
|
||||
}
|
||||
|
||||
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());
|
||||
return buildInspectorObjectCommon(m_frames, m_asyncParent.lock(),
|
||||
m_asyncCreation.lock(), m_maxAsyncDepth);
|
||||
}
|
||||
|
||||
std::unique_ptr<protocol::Runtime::API::StackTrace>
|
||||
V8StackTraceImpl::buildInspectorObject() const {
|
||||
return buildInspectorObjectImpl(nullptr);
|
||||
return buildInspectorObjectImpl();
|
||||
}
|
||||
|
||||
std::unique_ptr<StringBuffer> V8StackTraceImpl::toString() const {
|
||||
@ -297,13 +233,86 @@ std::unique_ptr<StringBuffer> V8StackTraceImpl::toString() const {
|
||||
stackTrace.append(" (");
|
||||
stackTrace.append(frame.sourceURL());
|
||||
stackTrace.append(':');
|
||||
stackTrace.append(String16::fromInteger(frame.lineNumber()));
|
||||
stackTrace.append(String16::fromInteger(frame.lineNumber() + 1));
|
||||
stackTrace.append(':');
|
||||
stackTrace.append(String16::fromInteger(frame.columnNumber()));
|
||||
stackTrace.append(String16::fromInteger(frame.columnNumber() + 1));
|
||||
stackTrace.append(')');
|
||||
}
|
||||
String16 string = stackTrace.toString();
|
||||
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
|
||||
|
@ -5,93 +5,115 @@
|
||||
#ifndef V8_INSPECTOR_V8STACKTRACEIMPL_H_
|
||||
#define V8_INSPECTOR_V8STACKTRACEIMPL_H_
|
||||
|
||||
#include <memory>
|
||||
#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.h"
|
||||
#include "src/base/macros.h"
|
||||
#include "src/inspector/protocol/Runtime.h"
|
||||
#include "src/inspector/string-16.h"
|
||||
|
||||
namespace v8_inspector {
|
||||
|
||||
class AsyncStackTrace;
|
||||
class V8Debugger;
|
||||
class WasmTranslation;
|
||||
|
||||
// Note: async stack trace may have empty top stack with non-empty tail to
|
||||
// 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 {
|
||||
class V8StackTraceImpl : public V8StackTrace {
|
||||
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*,
|
||||
bool capture);
|
||||
static std::unique_ptr<V8StackTraceImpl> create(
|
||||
V8Debugger*, int contextGroupId, v8::Local<v8::StackTrace>,
|
||||
size_t maxStackSize, const String16& description = String16());
|
||||
static std::unique_ptr<V8StackTraceImpl> capture(
|
||||
V8Debugger*, int contextGroupId, size_t maxStackSize,
|
||||
const String16& description = String16());
|
||||
static const int maxCallStackSizeToCapture = 200;
|
||||
static std::unique_ptr<V8StackTraceImpl> create(V8Debugger*,
|
||||
int contextGroupId,
|
||||
v8::Local<v8::StackTrace>,
|
||||
int maxStackSize);
|
||||
static std::unique_ptr<V8StackTraceImpl> capture(V8Debugger*,
|
||||
int contextGroupId,
|
||||
int maxStackSize);
|
||||
|
||||
// This method drops the async chain. Use cloneImpl() instead.
|
||||
std::unique_ptr<V8StackTrace> clone() override;
|
||||
std::unique_ptr<V8StackTraceImpl> cloneImpl();
|
||||
static std::unique_ptr<protocol::Runtime::StackTrace>
|
||||
buildInspectorObjectForTail(V8Debugger*);
|
||||
~V8StackTraceImpl() override;
|
||||
std::unique_ptr<protocol::Runtime::StackTrace> buildInspectorObjectImpl()
|
||||
const;
|
||||
~V8StackTraceImpl() override;
|
||||
|
||||
// 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;
|
||||
int topLineNumber() const override;
|
||||
int topColumnNumber() const override;
|
||||
int topLineNumber() const override; // 1-based.
|
||||
int topColumnNumber() const override; // 1-based.
|
||||
StringView topScriptId() const override;
|
||||
StringView topFunctionName() const override;
|
||||
std::unique_ptr<protocol::Runtime::API::StackTrace> buildInspectorObject()
|
||||
const override;
|
||||
std::unique_ptr<StringBuffer> toString() const override;
|
||||
|
||||
private:
|
||||
V8StackTraceImpl(int contextGroupId, const String16& description,
|
||||
std::vector<Frame>& frames,
|
||||
std::unique_ptr<V8StackTraceImpl> parent,
|
||||
std::unique_ptr<V8StackTraceImpl> creation);
|
||||
class Frame {
|
||||
public:
|
||||
explicit Frame(v8::Local<v8::StackFrame> frame);
|
||||
~Frame() = default;
|
||||
|
||||
std::unique_ptr<protocol::Runtime::StackTrace> buildInspectorObjectImpl(
|
||||
V8StackTraceImpl* creation) const;
|
||||
void translate(WasmTranslation* wasmTranslation);
|
||||
|
||||
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;
|
||||
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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
class ConnectTask : public TaskRunner::Task {
|
||||
@ -265,6 +284,10 @@ void InspectorClientImpl::setMemoryInfoForTest(
|
||||
memory_info_.Reset(isolate_, memory_info);
|
||||
}
|
||||
|
||||
void InspectorClientImpl::setLogConsoleApiMessageCalls(bool log) {
|
||||
log_console_api_message_calls_ = log;
|
||||
}
|
||||
|
||||
v8::MaybeLocal<v8::Value> InspectorClientImpl::memoryInfo(
|
||||
v8::Isolate* isolate, v8::Local<v8::Context>) {
|
||||
if (memory_info_.IsEmpty()) return v8::MaybeLocal<v8::Value>();
|
||||
@ -279,6 +302,20 @@ void InspectorClientImpl::quitMessageLoopOnPause() {
|
||||
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::Local<v8::Context> context) {
|
||||
return InspectorClientFromContext(context)->inspector_.get();
|
||||
|
@ -40,6 +40,7 @@ class InspectorClientImpl : public v8_inspector::V8InspectorClient {
|
||||
|
||||
void setCurrentTimeMSForTest(double time);
|
||||
void setMemoryInfoForTest(v8::Local<v8::Value> memory_info);
|
||||
void setLogConsoleApiMessageCalls(bool log);
|
||||
|
||||
private:
|
||||
// V8InspectorClient implementation.
|
||||
@ -51,7 +52,12 @@ class InspectorClientImpl : public v8_inspector::V8InspectorClient {
|
||||
v8::Local<v8::Context>) override;
|
||||
void runMessageLoopOnPause(int context_group_id) 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 ConnectTask;
|
||||
@ -75,6 +81,7 @@ class InspectorClientImpl : public v8_inspector::V8InspectorClient {
|
||||
|
||||
bool current_time_set_for_test_ = false;
|
||||
double current_time_ = 0.0;
|
||||
bool log_console_api_message_calls_ = false;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(InspectorClientImpl);
|
||||
};
|
||||
|
@ -61,6 +61,7 @@ class UtilsExtension : public v8::Extension {
|
||||
"native function schedulePauseOnNextStatement();"
|
||||
"native function cancelPauseOnNextStatement();"
|
||||
"native function reconnect();"
|
||||
"native function setLogConsoleApiMessageCalls();"
|
||||
"native function createContextGroup();") {}
|
||||
virtual v8::Local<v8::FunctionTemplate> GetNativeFunctionTemplate(
|
||||
v8::Isolate* isolate, v8::Local<v8::String> name) {
|
||||
@ -136,6 +137,14 @@ class UtilsExtension : public v8::Extension {
|
||||
.ToLocalChecked())
|
||||
.FromJust()) {
|
||||
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(
|
||||
isolate, "createContextGroup",
|
||||
v8::NewStringType::kNormal)
|
||||
@ -318,6 +327,16 @@ class UtilsExtension : public v8::Extension {
|
||||
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(
|
||||
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
if (args.Length() != 0) {
|
||||
|
@ -57,6 +57,8 @@ var utils = {};
|
||||
this.cancelPauseOnNextStatement = null;
|
||||
utils.reconnect = reconnect;
|
||||
this.reconnect = null;
|
||||
utils.setLogConsoleApiMessageCalls = setLogConsoleApiMessageCalls;
|
||||
this.setLogConsoleApiMessageCalls = null;
|
||||
utils.createContextGroup = createContextGroup;
|
||||
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