Commit 67333eb2 authored by aandrey@chromium.org's avatar aandrey@chromium.org

DevTools: Add an option to get current stack without scopes.

Getting scopes info from V8 debugger takes significant time.
Sometimes we don't need the scopes at all, thus we can save on this expensive work.
For async stacks we will be getting "fast" scope chains: up to the Local scope only (coming soon).

R=yurys

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

git-svn-id: svn://svn.chromium.org/blink/trunk@169854 bbb929c8-8fbe-4397-9dbb-9b2b20218538
parent c8686795
......@@ -33,11 +33,17 @@
var DebuggerScript = {};
DebuggerScript.PauseOnExceptionsState = {
DontPauseOnExceptions : 0,
PauseOnAllExceptions : 1,
DontPauseOnExceptions: 0,
PauseOnAllExceptions: 1,
PauseOnUncaughtExceptions: 2
};
DebuggerScript.ScopeInfoDetails = {
AllScopes: 0,
FastAsyncScopes: 1,
NoScopes: 2
};
DebuggerScript._pauseOnExceptionsState = DebuggerScript.PauseOnExceptionsState.DontPauseOnExceptions;
Debug.clearBreakOnException();
Debug.clearBreakOnUncaughtException();
......@@ -199,15 +205,18 @@ DebuggerScript.setPauseOnExceptionsState = function(newState)
Debug.clearBreakOnUncaughtException();
}
DebuggerScript.currentCallFrame = function(execState, maximumLimit)
DebuggerScript.currentCallFrame = function(execState, data)
{
var maximumLimit = data >> 2;
var scopeDetailsLevel = data & 3;
var frameCount = execState.frameCount();
if (maximumLimit >= 0 && maximumLimit < frameCount)
if (maximumLimit && maximumLimit < frameCount)
frameCount = maximumLimit;
var topFrame = undefined;
for (var i = frameCount - 1; i >= 0; i--) {
var frameMirror = execState.frame(i);
topFrame = DebuggerScript._frameMirrorToJSCallFrame(frameMirror, topFrame);
topFrame = DebuggerScript._frameMirrorToJSCallFrame(frameMirror, topFrame, scopeDetailsLevel);
}
return topFrame;
}
......@@ -313,7 +322,7 @@ DebuggerScript.isEvalCompilation = function(eventData)
// NOTE: This function is performance critical, as it can be run on every
// statement that generates an async event (like addEventListener) to support
// asynchronous call stacks. Thus, when possible, initialize the data lazily.
DebuggerScript._frameMirrorToJSCallFrame = function(frameMirror, callerFrame)
DebuggerScript._frameMirrorToJSCallFrame = function(frameMirror, callerFrame, scopeDetailsLevel)
{
// Stuff that can not be initialized lazily (i.e. valid while paused with a valid break_id).
// The frameMirror and scopeMirror can be accessed only while paused on the debugger.
......@@ -326,7 +335,7 @@ DebuggerScript._frameMirrorToJSCallFrame = function(frameMirror, callerFrame)
var isAtReturn = !!frameDetails.isAtReturn();
var returnValue = isAtReturn ? frameDetails.returnValue() : undefined;
var scopeMirrors = frameMirror.allScopes();
var scopeMirrors = (scopeDetailsLevel === DebuggerScript.ScopeInfoDetails.NoScopes ? [] : frameMirror.allScopes());
var scopeTypes = new Array(scopeMirrors.length);
var scopeObjects = new Array(scopeMirrors.length);
for (var i = 0; i < scopeMirrors.length; ++i) {
......
......@@ -314,15 +314,19 @@ bool ScriptDebugServer::setScriptSource(const String& sourceID, const String& ne
return false;
}
PassRefPtr<JavaScriptCallFrame> ScriptDebugServer::wrapCallFrames(v8::Handle<v8::Object> executionState, int maximumLimit)
PassRefPtr<JavaScriptCallFrame> ScriptDebugServer::wrapCallFrames(v8::Handle<v8::Object> executionState, int maximumLimit, ScopeInfoDetails scopeDetails)
{
const int scopeBits = 2;
COMPILE_ASSERT(NoScopes < (1 << scopeBits), not_enough_bits_to_encode_ScopeInfoDetails);
int data = (maximumLimit << scopeBits) | scopeDetails;
v8::Handle<v8::Value> currentCallFrameV8;
if (executionState.IsEmpty()) {
v8::Handle<v8::Function> currentCallFrameFunction = v8::Local<v8::Function>::Cast(m_debuggerScript.newLocal(m_isolate)->Get(v8AtomicString(m_isolate, "currentCallFrame")));
currentCallFrameV8 = v8::Debug::Call(currentCallFrameFunction, v8::Integer::New(m_isolate, maximumLimit));
currentCallFrameV8 = v8::Debug::Call(currentCallFrameFunction, v8::Integer::New(m_isolate, data));
} else {
v8::Handle<v8::Value> argv[] = { executionState, v8::Integer::New(m_isolate, maximumLimit) };
currentCallFrameV8 = callDebuggerMethod("currentCallFrame", 2, argv);
v8::Handle<v8::Value> argv[] = { executionState, v8::Integer::New(m_isolate, data) };
currentCallFrameV8 = callDebuggerMethod("currentCallFrame", WTF_ARRAY_LENGTH(argv), argv);
}
ASSERT(!currentCallFrameV8.IsEmpty());
if (!currentCallFrameV8->IsObject())
......@@ -330,14 +334,14 @@ PassRefPtr<JavaScriptCallFrame> ScriptDebugServer::wrapCallFrames(v8::Handle<v8:
return JavaScriptCallFrame::create(v8::Debug::GetDebugContext(), v8::Handle<v8::Object>::Cast(currentCallFrameV8));
}
ScriptValue ScriptDebugServer::currentCallFrames()
ScriptValue ScriptDebugServer::currentCallFramesInner(ScopeInfoDetails scopeDetails)
{
v8::HandleScope scope(m_isolate);
v8::Handle<v8::Context> pausedContext = m_pausedContext.IsEmpty() ? m_isolate->GetCurrentContext() : m_pausedContext;
if (pausedContext.IsEmpty())
return ScriptValue();
RefPtr<JavaScriptCallFrame> currentCallFrame = wrapCallFrames(m_executionState.newLocal(m_isolate), -1);
RefPtr<JavaScriptCallFrame> currentCallFrame = wrapCallFrames(m_executionState.newLocal(m_isolate), 0, scopeDetails);
if (!currentCallFrame)
return ScriptValue();
......@@ -345,6 +349,16 @@ ScriptValue ScriptDebugServer::currentCallFrames()
return ScriptValue(toV8(currentCallFrame.release(), v8::Handle<v8::Object>(), pausedContext->GetIsolate()), pausedContext->GetIsolate());
}
ScriptValue ScriptDebugServer::currentCallFrames()
{
return currentCallFramesInner(AllScopes);
}
ScriptValue ScriptDebugServer::currentCallFramesForAsyncStack()
{
return currentCallFramesInner(FastAsyncScopes);
}
void ScriptDebugServer::interruptAndRun(PassOwnPtr<Task> task, v8::Isolate* isolate)
{
v8::Debug::DebugBreakForCommand(new ClientDataImpl(task), isolate);
......@@ -463,7 +477,7 @@ void ScriptDebugServer::handleV8DebugEvent(const v8::Debug::EventDetails& eventD
// Stack trace is empty in case of syntax error. Silently continue execution in such cases.
if (!stackTrace->GetFrameCount())
return;
RefPtr<JavaScriptCallFrame> topFrame = wrapCallFrames(eventDetails.GetExecutionState(), 1);
RefPtr<JavaScriptCallFrame> topFrame = wrapCallFrames(eventDetails.GetExecutionState(), 1, NoScopes);
if (executeSkipPauseRequest(listener->shouldSkipExceptionPause(topFrame), eventDetails.GetExecutionState()))
return;
v8::Handle<v8::Object> eventData = eventDetails.GetEventData();
......@@ -476,7 +490,7 @@ void ScriptDebugServer::handleV8DebugEvent(const v8::Debug::EventDetails& eventD
v8::Handle<v8::Value> argv[] = { eventDetails.GetEventData() };
v8::Handle<v8::Value> hitBreakpoints = V8ScriptRunner::callInternalFunction(getBreakpointNumbersFunction, debuggerScript, WTF_ARRAY_LENGTH(argv), argv, m_isolate);
ASSERT(hitBreakpoints->IsArray());
RefPtr<JavaScriptCallFrame> topFrame = wrapCallFrames(eventDetails.GetExecutionState(), 1);
RefPtr<JavaScriptCallFrame> topFrame = wrapCallFrames(eventDetails.GetExecutionState(), 1, NoScopes);
ScriptDebugListener::SkipPauseRequest skipRequest;
if (v8::Handle<v8::Array>::Cast(hitBreakpoints)->Length())
skipRequest = listener->shouldSkipBreakpointPause(topFrame);
......
......@@ -79,6 +79,7 @@ public:
bool setScriptSource(const String& sourceID, const String& newContent, bool preview, String* error, RefPtr<TypeBuilder::Debugger::SetScriptSourceError>&, ScriptValue* newCallFrames, ScriptObject* result);
ScriptValue currentCallFrames();
ScriptValue currentCallFramesForAsyncStack();
class Task {
public:
......@@ -136,8 +137,16 @@ protected:
v8::Isolate* m_isolate;
private:
enum ScopeInfoDetails {
AllScopes,
FastAsyncScopes,
NoScopes // Should be the last option.
};
ScriptValue currentCallFramesInner(ScopeInfoDetails);
void stepCommandWithFrame(const char* functionName, const ScriptValue& frame);
PassRefPtr<JavaScriptCallFrame> wrapCallFrames(v8::Handle<v8::Object> executionState, int maximumLimit);
PassRefPtr<JavaScriptCallFrame> wrapCallFrames(v8::Handle<v8::Object> executionState, int maximumLimit, ScopeInfoDetails);
bool executeSkipPauseRequest(ScriptDebugListener::SkipPauseRequest, v8::Handle<v8::Object> executionState);
bool m_runningNestedMessageLoop;
......
......@@ -671,7 +671,7 @@ void InspectorDebuggerAgent::cancelPauseOnNextStatement()
void InspectorDebuggerAgent::didInstallTimer(ExecutionContext* context, int timerId, int timeout, bool singleShot)
{
if (m_asyncCallStackTracker.isEnabled())
m_asyncCallStackTracker.didInstallTimer(context, timerId, singleShot, scriptDebugServer().currentCallFrames());
m_asyncCallStackTracker.didInstallTimer(context, timerId, singleShot, scriptDebugServer().currentCallFramesForAsyncStack());
}
void InspectorDebuggerAgent::didRemoveTimer(ExecutionContext* context, int timerId)
......@@ -697,7 +697,7 @@ void InspectorDebuggerAgent::didFireTimer()
void InspectorDebuggerAgent::didRequestAnimationFrame(Document* document, int callbackId)
{
if (m_asyncCallStackTracker.isEnabled())
m_asyncCallStackTracker.didRequestAnimationFrame(document, callbackId, scriptDebugServer().currentCallFrames());
m_asyncCallStackTracker.didRequestAnimationFrame(document, callbackId, scriptDebugServer().currentCallFramesForAsyncStack());
}
void InspectorDebuggerAgent::didCancelAnimationFrame(Document* document, int callbackId)
......@@ -722,7 +722,7 @@ void InspectorDebuggerAgent::didFireAnimationFrame()
void InspectorDebuggerAgent::didAddEventListener(EventTarget* eventTarget, const AtomicString& eventType, EventListener* listener, bool useCapture)
{
if (m_asyncCallStackTracker.isEnabled())
m_asyncCallStackTracker.didAddEventListener(eventTarget, eventType, listener, useCapture, scriptDebugServer().currentCallFrames());
m_asyncCallStackTracker.didAddEventListener(eventTarget, eventType, listener, useCapture, scriptDebugServer().currentCallFramesForAsyncStack());
}
void InspectorDebuggerAgent::didRemoveEventListener(EventTarget* eventTarget, const AtomicString& eventType, EventListener* listener, bool useCapture)
......@@ -753,13 +753,13 @@ void InspectorDebuggerAgent::didHandleEvent()
void InspectorDebuggerAgent::willLoadXHR(XMLHttpRequest* xhr, ThreadableLoaderClient*, const AtomicString&, const KURL&, bool async, PassRefPtr<FormData>, const HTTPHeaderMap&, bool)
{
if (m_asyncCallStackTracker.isEnabled() && async)
m_asyncCallStackTracker.willLoadXHR(xhr, scriptDebugServer().currentCallFrames());
m_asyncCallStackTracker.willLoadXHR(xhr, scriptDebugServer().currentCallFramesForAsyncStack());
}
void InspectorDebuggerAgent::didEnqueueMutationRecord(ExecutionContext* context, MutationObserver* observer)
{
if (m_asyncCallStackTracker.isEnabled() && !m_asyncCallStackTracker.hasEnqueuedMutationRecord(context, observer))
m_asyncCallStackTracker.didEnqueueMutationRecord(context, observer, scriptDebugServer().currentCallFrames());
m_asyncCallStackTracker.didEnqueueMutationRecord(context, observer, scriptDebugServer().currentCallFramesForAsyncStack());
}
void InspectorDebuggerAgent::didClearAllMutationRecords(ExecutionContext* context, MutationObserver* observer)
......@@ -783,7 +783,7 @@ void InspectorDebuggerAgent::didDeliverMutationRecords()
void InspectorDebuggerAgent::didPostPromiseTask(ExecutionContext* context, ExecutionContextTask* task, bool isResolved)
{
if (m_asyncCallStackTracker.isEnabled())
m_asyncCallStackTracker.didPostPromiseTask(context, task, isResolved, scriptDebugServer().currentCallFrames());
m_asyncCallStackTracker.didPostPromiseTask(context, task, isResolved, scriptDebugServer().currentCallFramesForAsyncStack());
}
void InspectorDebuggerAgent::willPerformPromiseTask(ExecutionContext* context, ExecutionContextTask* task)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment