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
This commit is contained in:
prybin@chromium.org 2013-09-08 19:05:29 +00:00
parent 070d18f9ca
commit 48cae75df8
7 changed files with 171 additions and 10 deletions

View File

@ -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,

View File

@ -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();

View File

@ -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; }

View File

@ -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<StepAction>(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<StepAction>(step_action),
step_count);
step_count,
frame_id);
return isolate->heap()->undefined_value();
}

View File

@ -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) \

View File

@ -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);
}

View File

@ -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");