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