From: prybin@chromium.org Date: Sun, 8 Sep 2013 19:05:29 +0000 (+0000) Subject: Debug: parameterize 'step over' action with a frame where the step must be performed X-Git-Tag: upstream/4.7.83~12621 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=48cae75df809b0784c7ee8f5ec0601724c91c71b;p=platform%2Fupstream%2Fv8.git Debug: parameterize 'step over' action with a frame where the step must be performed R=yangguo@chromium.org Review URL: https://codereview.chromium.org/23533015 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@16581 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- diff --git a/src/debug-debugger.js b/src/debug-debugger.js index a588b4c..19209d4 100644 --- a/src/debug-debugger.js +++ b/src/debug-debugger.js @@ -957,12 +957,17 @@ function ExecutionState(break_id) { this.selected_frame = 0; } -ExecutionState.prototype.prepareStep = function(opt_action, opt_count) { +ExecutionState.prototype.prepareStep = function(opt_action, opt_count, + opt_callframe) { var action = Debug.StepAction.StepIn; if (!IS_UNDEFINED(opt_action)) action = %ToNumber(opt_action); var count = opt_count ? %ToNumber(opt_count) : 1; + var callFrameId = 0; + if (!IS_UNDEFINED(opt_callframe)) { + callFrameId = opt_callframe.details_.frameId(); + } - return %PrepareStep(this.break_id, action, count); + return %PrepareStep(this.break_id, action, count, callFrameId); }; ExecutionState.prototype.evaluateGlobal = function(source, disable_break, diff --git a/src/debug.cc b/src/debug.cc index c82a2aa..7624f26 100644 --- a/src/debug.cc +++ b/src/debug.cc @@ -1017,7 +1017,7 @@ Object* Debug::Break(Arguments args) { // Clear queue thread_local_.queued_step_count_ = 0; - PrepareStep(StepNext, step_count); + PrepareStep(StepNext, step_count, StackFrame::NO_ID); } else { // Notify the debug event listeners. isolate_->debugger()->OnDebugBreak(break_points_hit, false); @@ -1055,7 +1055,7 @@ Object* Debug::Break(Arguments args) { ClearStepping(); // Set up for the remaining steps. - PrepareStep(step_action, step_count); + PrepareStep(step_action, step_count, StackFrame::NO_ID); } if (thread_local_.frame_drop_mode_ == FRAMES_UNTOUCHED) { @@ -1376,7 +1376,9 @@ bool Debug::IsBreakOnException(ExceptionBreakType type) { } -void Debug::PrepareStep(StepAction step_action, int step_count) { +void Debug::PrepareStep(StepAction step_action, + int step_count, + StackFrame::Id frame_id) { HandleScope scope(isolate_); PrepareForBreakPoints(); @@ -1402,6 +1404,9 @@ void Debug::PrepareStep(StepAction step_action, int step_count) { // If there is no JavaScript stack don't do anything. return; } + if (frame_id != StackFrame::NO_ID) { + id = frame_id; + } JavaScriptFrameIterator frames_it(isolate_, id); JavaScriptFrame* frame = frames_it.frame(); diff --git a/src/debug.h b/src/debug.h index 85f7583..03ee40e 100644 --- a/src/debug.h +++ b/src/debug.h @@ -261,7 +261,9 @@ class Debug { void FloodHandlerWithOneShot(); void ChangeBreakOnException(ExceptionBreakType type, bool enable); bool IsBreakOnException(ExceptionBreakType type); - void PrepareStep(StepAction step_action, int step_count); + void PrepareStep(StepAction step_action, + int step_count, + StackFrame::Id frame_id); void ClearStepping(); void ClearStepOut(); bool IsStepping() { return thread_local_.step_count_ > 0; } diff --git a/src/runtime.cc b/src/runtime.cc index 9e44b58..56558e0 100644 --- a/src/runtime.cc +++ b/src/runtime.cc @@ -12484,7 +12484,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_IsBreakOnException) { // of frames to step down. RUNTIME_FUNCTION(MaybeObject*, Runtime_PrepareStep) { HandleScope scope(isolate); - ASSERT(args.length() == 3); + ASSERT(args.length() == 4); // Check arguments. Object* check; { MaybeObject* maybe_check = Runtime_CheckExecutionState( @@ -12495,6 +12495,15 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_PrepareStep) { return isolate->Throw(isolate->heap()->illegal_argument_string()); } + CONVERT_NUMBER_CHECKED(int, wrapped_frame_id, Int32, args[3]); + + StackFrame::Id frame_id; + if (wrapped_frame_id == 0) { + frame_id = StackFrame::NO_ID; + } else { + frame_id = UnwrapFrameId(wrapped_frame_id); + } + // Get the step action and check validity. StepAction step_action = static_cast(NumberToInt32(args[1])); if (step_action != StepIn && @@ -12505,6 +12514,11 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_PrepareStep) { return isolate->Throw(isolate->heap()->illegal_argument_string()); } + if (frame_id != StackFrame::NO_ID && step_action != StepNext && + step_action != StepMin && step_action != StepOut) { + return isolate->ThrowIllegalOperation(); + } + // Get the number of steps. int step_count = NumberToInt32(args[2]); if (step_count < 1) { @@ -12516,7 +12530,8 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_PrepareStep) { // Prepare step. isolate->debug()->PrepareStep(static_cast(step_action), - step_count); + step_count, + frame_id); return isolate->heap()->undefined_value(); } diff --git a/src/runtime.h b/src/runtime.h index 9054fc7..4c76098 100644 --- a/src/runtime.h +++ b/src/runtime.h @@ -504,7 +504,7 @@ namespace internal { F(ClearBreakPoint, 1, 1) \ F(ChangeBreakOnException, 2, 1) \ F(IsBreakOnException, 1, 1) \ - F(PrepareStep, 3, 1) \ + F(PrepareStep, 4, 1) \ F(ClearStepping, 0, 1) \ F(DebugEvaluate, 6, 1) \ F(DebugEvaluateGlobal, 4, 1) \ diff --git a/test/cctest/test-debug.cc b/test/cctest/test-debug.cc index 6bd6fee..2540a3d 100644 --- a/test/cctest/test-debug.cc +++ b/test/cctest/test-debug.cc @@ -37,6 +37,7 @@ #include "compilation-cache.h" #include "debug.h" #include "deoptimizer.h" +#include "frames.h" #include "platform.h" #include "platform/condition-variable.h" #include "platform/socket.h" @@ -60,6 +61,7 @@ using ::v8::internal::Debug; using ::v8::internal::Debugger; using ::v8::internal::CommandMessage; using ::v8::internal::CommandMessageQueue; +using ::v8::internal::StackFrame; using ::v8::internal::StepAction; using ::v8::internal::StepIn; // From StepAction enum using ::v8::internal::StepNext; // From StepAction enum @@ -390,7 +392,7 @@ static void ChangeBreakOnExceptionFromJS(bool caught, bool uncaught) { // Prepare to step to next break location. static void PrepareStep(StepAction step_action) { v8::internal::Debug* debug = v8::internal::Isolate::Current()->debug(); - debug->PrepareStep(step_action, 1); + debug->PrepareStep(step_action, 1, StackFrame::NO_ID); } diff --git a/test/mjsunit/debug-step-4-in-frame.js b/test/mjsunit/debug-step-4-in-frame.js new file mode 100644 index 0000000..65ac490 --- /dev/null +++ b/test/mjsunit/debug-step-4-in-frame.js @@ -0,0 +1,132 @@ +// Copyright 2008 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --expose-debug-as debug +// Get the Debug object exposed from the debug context global object. +Debug = debug.Debug + +// Tests how debugger can step over not necessarily in the top frame. + +// Simple 3 functions, that protocol their execution state in global +// variable state. +var state; + +function f() { + var a = 1978; + for (state[2] = 0; state[2] < 5; state[2]++) { + void String(a); + } +} +function g() { + for (state[1] = 0; state[1] < 5; state[1]++) { + f(); + } +} +function h() { + state = [-1, -1, -1]; + for (state[0] = 0; state[0] < 5; state[0]++) { + g(); + } +} + +function TestCase(frame_index, step_count, expected_final_state) { + print("Test case, parameters " + frame_index + "/" + step_count); + + var listener_exception = null; + var state_snapshot; + var listener_state; + var bp; + + function listener(event, exec_state, event_data, data) { + print("Here ("+event+"/"+listener_state+"): " + + exec_state.frame(0).sourceLineText()); + try { + if (event == Debug.DebugEvent.Break) { + if (listener_state == 0) { + Debug.clearBreakPoint(bp); + var context_frame; + if (frame_index !== undefined) { + context_frame = exec_state.frame(frame_index); + } + exec_state.prepareStep(Debug.StepAction.StepNext, + step_count, context_frame); + listener_state = 1; + } else if (listener_state == 1) { + state_snapshot = String(state); + print("State: " + state_snapshot); + Debug.setListener(null); + listener_state = 2; + } + } + } catch (e) { + listener_exception = e; + } + } + + + // Add the debug event listener. + listener_state = 0; + Debug.setListener(listener); + bp = Debug.setBreakPoint(f, 1); + + h(); + Debug.setListener(null); + if (listener_exception !== null) { + print("Exception caught: " + listener_exception); + assertUnreachable(); + } + + assertEquals(expected_final_state, state_snapshot); +} + + +// Warm-up -- make sure all is compiled and ready for breakpoint. +h(); + + +// Stepping in the default (top) frame. +TestCase(undefined, 0, "0,0,-1"); +TestCase(undefined, 1, "0,0,-1"); +TestCase(undefined, 2, "0,0,0"); +TestCase(undefined, 5, "0,0,1"); +TestCase(undefined, 8, "0,0,3"); + +// Stepping in the frame #0 (should be exactly the same as above). +TestCase(0, 0, "0,0,-1"); +TestCase(0, 1, "0,0,-1"); +TestCase(0, 2, "0,0,0"); +TestCase(0, 5, "0,0,1"); +TestCase(0, 8, "0,0,3"); + +// Stepping in the frame #1. +TestCase(1, 0, "0,0,5"); +TestCase(1, 3, "0,1,5"); +TestCase(1, 8, "0,4,5"); + +// Stepping in the frame #2. +TestCase(2, 3, "1,5,5"); +TestCase(2, 8, "4,5,5");