From 50ca2eb9f6dee79d2f4a92c3ec2fa6fd25d59626 Mon Sep 17 00:00:00 2001 From: "ulan@chromium.org" Date: Fri, 21 Mar 2014 12:30:58 +0000 Subject: [PATCH] Add option to run ScopeIterator faster giving up nested scope chain. We'd like to be able to trade nested scope chain info (consisting of with, block and catch scopes) in favor of speed in some cases. BUG=chromium:340285 LOG=N R=ulan@chromium.org, pfeldman, ulan, yangguo Review URL: https://codereview.chromium.org/203463011 Patch from Andrey Adaykin . git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@20162 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/mirror-debugger.js | 5 +++-- src/runtime.cc | 46 +++++++++++++++++++++++++++++++------------- src/runtime.h | 2 +- test/mjsunit/debug-scopes.js | 20 ++++++++++++++++--- 4 files changed, 54 insertions(+), 19 deletions(-) diff --git a/src/mirror-debugger.js b/src/mirror-debugger.js index f288123..b07d1fe 100644 --- a/src/mirror-debugger.js +++ b/src/mirror-debugger.js @@ -1692,10 +1692,11 @@ FrameMirror.prototype.scope = function(index) { }; -FrameMirror.prototype.allScopes = function() { +FrameMirror.prototype.allScopes = function(opt_ignore_nested_scopes) { var scopeDetails = %GetAllScopesDetails(this.break_id_, this.details_.frameId(), - this.details_.inlinedFrameIndex()); + this.details_.inlinedFrameIndex(), + !!opt_ignore_nested_scopes); var result = []; for (var i = 0; i < scopeDetails.length; ++i) { result.push(new ScopeMirror(this, UNDEFINED, i, scopeDetails[i])); diff --git a/src/runtime.cc b/src/runtime.cc index dcb9dc4..b4c34ef 100644 --- a/src/runtime.cc +++ b/src/runtime.cc @@ -11815,7 +11815,8 @@ class ScopeIterator { ScopeIterator(Isolate* isolate, JavaScriptFrame* frame, - int inlined_jsframe_index) + int inlined_jsframe_index, + bool ignore_nested_scopes = false) : isolate_(isolate), frame_(frame), inlined_jsframe_index_(inlined_jsframe_index), @@ -11839,19 +11840,31 @@ class ScopeIterator { // Return if ensuring debug info failed. return; } - Handle debug_info = Debug::GetDebugInfo(shared_info); - // Find the break point where execution has stopped. - BreakLocationIterator break_location_iterator(debug_info, - ALL_BREAK_LOCATIONS); - // pc points to the instruction after the current one, possibly a break - // location as well. So the "- 1" to exclude it from the search. - break_location_iterator.FindBreakLocationFromAddress(frame->pc() - 1); - if (break_location_iterator.IsExit()) { - // We are within the return sequence. At the momemt it is not possible to + // Currently it takes too much time to find nested scopes due to script + // parsing. Sometimes we want to run the ScopeIterator as fast as possible + // (for example, while collecting async call stacks on every + // addEventListener call), even if we drop some nested scopes. + // Later we may optimize getting the nested scopes (cache the result?) + // and include nested scopes into the "fast" iteration case as well. + if (!ignore_nested_scopes) { + Handle debug_info = Debug::GetDebugInfo(shared_info); + + // Find the break point where execution has stopped. + BreakLocationIterator break_location_iterator(debug_info, + ALL_BREAK_LOCATIONS); + // pc points to the instruction after the current one, possibly a break + // location as well. So the "- 1" to exclude it from the search. + break_location_iterator.FindBreakLocationFromAddress(frame->pc() - 1); + + // Within the return sequence at the moment it is not possible to // get a source position which is consistent with the current scope chain. // Thus all nested with, catch and block contexts are skipped and we only // provide the function scope. + ignore_nested_scopes = break_location_iterator.IsExit(); + } + + if (ignore_nested_scopes) { if (scope_info->HasContext()) { context_ = Handle(context_->declaration_context(), isolate_); } else { @@ -11859,7 +11872,7 @@ class ScopeIterator { context_ = Handle(context_->previous(), isolate_); } } - if (scope_info->scope_type() != EVAL_SCOPE) { + if (scope_info->scope_type() == FUNCTION_SCOPE) { nested_scope_chain_.Add(scope_info); } } else { @@ -12327,13 +12340,14 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetScopeDetails) { // args[0]: number: break id // args[1]: number: frame index // args[2]: number: inlined frame index +// args[3]: boolean: ignore nested scopes // // The array returned contains arrays with the following information: // 0: Scope type // 1: Scope object RUNTIME_FUNCTION(MaybeObject*, Runtime_GetAllScopesDetails) { HandleScope scope(isolate); - ASSERT(args.length() == 3); + ASSERT(args.length() == 3 || args.length() == 4); // Check arguments. Object* check; @@ -12344,13 +12358,19 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetAllScopesDetails) { CONVERT_SMI_ARG_CHECKED(wrapped_id, 1); CONVERT_NUMBER_CHECKED(int, inlined_jsframe_index, Int32, args[2]); + bool ignore_nested_scopes = false; + if (args.length() == 4) { + CONVERT_BOOLEAN_ARG_CHECKED(flag, 3); + ignore_nested_scopes = flag; + } + // Get the frame where the debugging is performed. StackFrame::Id id = UnwrapFrameId(wrapped_id); JavaScriptFrameIterator frame_it(isolate, id); JavaScriptFrame* frame = frame_it.frame(); List > result(4); - ScopeIterator it(isolate, frame, inlined_jsframe_index); + ScopeIterator it(isolate, frame, inlined_jsframe_index, ignore_nested_scopes); for (; !it.Done(); it.Next()) { Handle details = MaterializeScopeDetails(isolate, &it); RETURN_IF_EMPTY_HANDLE(isolate, details); diff --git a/src/runtime.h b/src/runtime.h index 3c8077f..aa811a6 100644 --- a/src/runtime.h +++ b/src/runtime.h @@ -495,7 +495,7 @@ namespace internal { F(GetScopeCount, 2, 1) \ F(GetStepInPositions, 2, 1) \ F(GetScopeDetails, 4, 1) \ - F(GetAllScopesDetails, 3, 1) \ + F(GetAllScopesDetails, 4, 1) \ F(GetFunctionScopeCount, 1, 1) \ F(GetFunctionScopeDetails, 2, 1) \ F(SetScopeVariableValue, 6, 1) \ diff --git a/test/mjsunit/debug-scopes.js b/test/mjsunit/debug-scopes.js index 57adf23..f5b5ec9 100644 --- a/test/mjsunit/debug-scopes.js +++ b/test/mjsunit/debug-scopes.js @@ -71,16 +71,29 @@ function BeginTest(name) { // Check result of a test. function EndTest() { assertTrue(listener_called, "listerner not called for " + test_name); - assertNull(exception, test_name); + assertNull(exception, test_name + " / " + exception); end_test_count++; } // Check that two scope are the same. -function CheckScopeMirrors(scope1, scope2) { +function assertScopeMirrorEquals(scope1, scope2) { assertEquals(scope1.scopeType(), scope2.scopeType()); assertEquals(scope1.frameIndex(), scope2.frameIndex()); assertEquals(scope1.scopeIndex(), scope2.scopeIndex()); + assertPropertiesEqual(scope1.scopeObject().value(), scope2.scopeObject().value()); +} + +function CheckFastAllScopes(scopes, exec_state) +{ + var fast_all_scopes = exec_state.frame().allScopes(true); + var length = fast_all_scopes.length; + assertTrue(scopes.length >= length); + for (var i = 0; i < scopes.length && i < length; i++) { + var scope = fast_all_scopes[length - i - 1]; + assertTrue(scope.isScope()); + assertEquals(scopes[scopes.length - i - 1], scope.scopeType()); + } } @@ -93,7 +106,7 @@ function CheckScopeChain(scopes, exec_state) { var scope = exec_state.frame().scope(i); assertTrue(scope.isScope()); assertEquals(scopes[i], scope.scopeType()); - CheckScopeMirrors(all_scopes[i], scope); + assertScopeMirrorEquals(all_scopes[i], scope); // Check the global object when hitting the global scope. if (scopes[i] == debug.ScopeType.Global) { @@ -102,6 +115,7 @@ function CheckScopeChain(scopes, exec_state) { assertPropertiesEqual(this, scope.scopeObject().value()); } } + CheckFastAllScopes(scopes, exec_state); // Get the debug command processor. var dcp = exec_state.debugCommandProcessor("unspecified_running_state"); -- 2.7.4