Commit 630d6b8b authored by yangguo@chromium.org's avatar yangguo@chromium.org

Introduce new stepping mode to step into another frame.

R=aandrey@chromium.org
BUG=chromium:267592
LOG=Y

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

Cr-Commit-Position: refs/heads/master@{#25130}
git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@25130 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent bd9d317e
......@@ -33,7 +33,8 @@ Debug.StepAction = { StepOut: 0,
StepNext: 1,
StepIn: 2,
StepMin: 3,
StepInMin: 4 };
StepInMin: 4,
StepFrame: 5 };
// The different types of scripts matching enum ScriptType in objects.h.
Debug.ScriptType = { Native: 0,
......
......@@ -120,21 +120,37 @@ void BreakLocationIterator::Next() {
DCHECK(statement_position_ >= 0);
}
if (IsDebugBreakSlot()) {
// There is always a possible break point at a debug break slot.
// Check for break at return.
if (RelocInfo::IsJSReturn(rmode())) {
// Set the positions to the end of the function.
if (debug_info_->shared()->HasSourceCode()) {
position_ = debug_info_->shared()->end_position() -
debug_info_->shared()->start_position() - 1;
} else {
position_ = 0;
}
statement_position_ = position_;
break_point_++;
return;
} else if (RelocInfo::IsCodeTarget(rmode())) {
}
if (RelocInfo::IsCodeTarget(rmode())) {
// Check for breakable code target. Look in the original code as setting
// break points can cause the code targets in the running (debugged) code
// to be of a different kind than in the original code.
Address target = original_rinfo()->target_address();
Code* code = Code::GetCodeFromTargetAddress(target);
if ((code->is_inline_cache_stub() &&
!code->is_binary_op_stub() &&
!code->is_compare_ic_stub() &&
!code->is_to_boolean_ic_stub()) ||
RelocInfo::IsConstructCall(rmode())) {
if (RelocInfo::IsConstructCall(rmode()) || code->is_call_stub()) {
break_point_++;
return;
}
// Skip below if we only want locations for calls and returns.
if (type_ == CALLS_AND_RETURNS) continue;
if ((code->is_inline_cache_stub() && !code->is_binary_op_stub() &&
!code->is_compare_ic_stub() && !code->is_to_boolean_ic_stub())) {
break_point_++;
return;
}
......@@ -157,16 +173,8 @@ void BreakLocationIterator::Next() {
}
}
// Check for break at return.
if (RelocInfo::IsJSReturn(rmode())) {
// Set the positions to the end of the function.
if (debug_info_->shared()->HasSourceCode()) {
position_ = debug_info_->shared()->end_position() -
debug_info_->shared()->start_position() - 1;
} else {
position_ = 0;
}
statement_position_ = position_;
if (IsDebugBreakSlot() && type_ != CALLS_AND_RETURNS) {
// There is always a possible break point at a debug break slot.
break_point_++;
return;
}
......@@ -1189,7 +1197,8 @@ void Debug::ClearAllBreakPoints() {
}
void Debug::FloodWithOneShot(Handle<JSFunction> function) {
void Debug::FloodWithOneShot(Handle<JSFunction> function,
BreakLocatorType type) {
PrepareForBreakPoints();
// Make sure the function is compiled and has set up the debug info.
......@@ -1200,7 +1209,7 @@ void Debug::FloodWithOneShot(Handle<JSFunction> function) {
}
// Flood the function with break points.
BreakLocationIterator it(GetDebugInfo(shared), ALL_BREAK_LOCATIONS);
BreakLocationIterator it(GetDebugInfo(shared), type);
while (!it.Done()) {
it.SetOneShot();
it.Next();
......@@ -1216,7 +1225,7 @@ void Debug::FloodBoundFunctionWithOneShot(Handle<JSFunction> function) {
if (!bindee.is_null() && bindee->IsJSFunction() &&
!JSFunction::cast(*bindee)->IsFromNativeScript()) {
Handle<JSFunction> bindee_function(JSFunction::cast(*bindee));
Debug::FloodWithOneShot(bindee_function);
FloodWithOneShot(bindee_function);
}
}
......@@ -1296,7 +1305,7 @@ void Debug::PrepareStep(StepAction step_action,
FloodHandlerWithOneShot();
// If the function on the top frame is unresolved perform step out. This will
// be the case when calling unknown functions and having the debugger stopped
// be the case when calling unknown function and having the debugger stopped
// in an unhandled exception.
if (!frame->function()->IsJSFunction()) {
// Step out: Find the calling JavaScript frame and flood it with
......@@ -1354,7 +1363,7 @@ void Debug::PrepareStep(StepAction step_action,
if ((maybe_call_function_stub->kind() == Code::STUB &&
CodeStub::GetMajorKey(maybe_call_function_stub) ==
CodeStub::CallFunction) ||
maybe_call_function_stub->kind() == Code::CALL_IC) {
maybe_call_function_stub->is_call_stub()) {
// Save reference to the code as we may need it to find out arguments
// count for 'step in' later.
call_function_stub = Handle<Code>(maybe_call_function_stub);
......@@ -1395,7 +1404,9 @@ void Debug::PrepareStep(StepAction step_action,
// Step next or step min.
// Fill the current function with one-shot break points.
FloodWithOneShot(function);
// If we are stepping into another frame, only fill calls and returns.
FloodWithOneShot(function, step_action == StepFrame ? CALLS_AND_RETURNS
: ALL_BREAK_LOCATIONS);
// Remember source position and frame to handle step next.
thread_local_.last_statement_position_ =
......@@ -1454,7 +1465,7 @@ void Debug::PrepareStep(StepAction step_action,
if (fun->IsJSFunction()) {
Handle<JSFunction> js_function(JSFunction::cast(fun));
if (js_function->shared()->bound()) {
Debug::FloodBoundFunctionWithOneShot(js_function);
FloodBoundFunctionWithOneShot(js_function);
} else if (!js_function->IsFromNativeScript()) {
// Don't step into builtins.
// It will also compile target function if it's not compiled yet.
......@@ -1467,7 +1478,9 @@ void Debug::PrepareStep(StepAction step_action,
// a call target as the function called might be a native function for
// which step in will not stop. It also prepares for stepping in
// getters/setters.
FloodWithOneShot(function);
// If we are stepping into another frame, only fill calls and returns.
FloodWithOneShot(function, step_action == StepFrame ? CALLS_AND_RETURNS
: ALL_BREAK_LOCATIONS);
if (is_load_or_store) {
// Remember source position and frame to handle step in getter/setter. If
......@@ -1496,15 +1509,20 @@ bool Debug::StepNextContinue(BreakLocationIterator* break_location_iterator,
JavaScriptFrame* frame) {
// StepNext and StepOut shouldn't bring us deeper in code, so last frame
// shouldn't be a parent of current frame.
if (thread_local_.last_step_action_ == StepNext ||
thread_local_.last_step_action_ == StepOut) {
StepAction step_action = thread_local_.last_step_action_;
if (step_action == StepNext || step_action == StepOut) {
if (frame->fp() < thread_local_.last_fp_) return true;
}
// We stepped into a new frame if the frame pointer changed.
if (step_action == StepFrame) {
return frame->UnpaddedFP() == thread_local_.last_fp_;
}
// If the step last action was step next or step in make sure that a new
// statement is hit.
if (thread_local_.last_step_action_ == StepNext ||
thread_local_.last_step_action_ == StepIn) {
if (step_action == StepNext || step_action == StepIn) {
// Never continue if returning from function.
if (break_location_iterator->IsExit()) return false;
......@@ -1571,10 +1589,13 @@ Handle<Object> Debug::GetSourceBreakLocations(
// Handle stepping into a function.
void Debug::HandleStepIn(Handle<JSFunction> function,
Handle<Object> holder,
Address fp,
bool is_constructor) {
void Debug::HandleStepIn(Handle<Object> function_obj, Handle<Object> holder,
Address fp, bool is_constructor) {
// Flood getter/setter if we either step in or step to another frame.
bool step_frame = thread_local_.last_step_action_ == StepFrame;
if (!StepInActive() && !step_frame) return;
if (!function_obj->IsJSFunction()) return;
Handle<JSFunction> function = Handle<JSFunction>::cast(function_obj);
Isolate* isolate = function->GetIsolate();
// If the frame pointer is not supplied by the caller find it.
if (fp == 0) {
......@@ -1589,11 +1610,11 @@ void Debug::HandleStepIn(Handle<JSFunction> function,
}
// Flood the function with one-shot break points if it is called from where
// step into was requested.
if (fp == thread_local_.step_into_fp_) {
// step into was requested, or when stepping into a new frame.
if (fp == thread_local_.step_into_fp_ || step_frame) {
if (function->shared()->bound()) {
// Handle Function.prototype.bind
Debug::FloodBoundFunctionWithOneShot(function);
FloodBoundFunctionWithOneShot(function);
} else if (!function->IsFromNativeScript()) {
// Don't allow step into functions in the native context.
if (function->shared()->code() ==
......@@ -1607,14 +1628,14 @@ void Debug::HandleStepIn(Handle<JSFunction> function,
if (!holder.is_null() && holder->IsJSFunction()) {
Handle<JSFunction> js_function = Handle<JSFunction>::cast(holder);
if (!js_function->IsFromNativeScript()) {
Debug::FloodWithOneShot(js_function);
FloodWithOneShot(js_function);
} else if (js_function->shared()->bound()) {
// Handle Function.prototype.bind
Debug::FloodBoundFunctionWithOneShot(js_function);
FloodBoundFunctionWithOneShot(js_function);
}
}
} else {
Debug::FloodWithOneShot(function);
FloodWithOneShot(function);
}
}
}
......
......@@ -32,13 +32,14 @@ class DebugScope;
// Step actions. NOTE: These values are in macros.py as well.
enum StepAction {
StepNone = -1, // Stepping not prepared.
StepOut = 0, // Step out of the current function.
StepNext = 1, // Step to the next statement in the current function.
StepIn = 2, // Step into new functions invoked or the next statement
// in the current function.
StepMin = 3, // Perform a minimum step in the current function.
StepInMin = 4 // Step into new functions invoked or perform a minimum step
// in the current function.
StepOut = 0, // Step out of the current function.
StepNext = 1, // Step to the next statement in the current function.
StepIn = 2, // Step into new functions invoked or the next statement
// in the current function.
StepMin = 3, // Perform a minimum step in the current function.
StepInMin = 4, // Step into new functions invoked or perform a minimum step
// in the current function.
StepFrame = 5 // Step into a new frame or return to previous frame.
};
......@@ -49,10 +50,11 @@ enum ExceptionBreakType {
};
// Type of exception break. NOTE: These values are in macros.py as well.
// Type of exception break.
enum BreakLocatorType {
ALL_BREAK_LOCATIONS = 0,
SOURCE_BREAK_LOCATIONS = 1
SOURCE_BREAK_LOCATIONS = 1,
CALLS_AND_RETURNS = 2,
};
......@@ -385,7 +387,8 @@ class Debug {
BreakPositionAlignment alignment);
void ClearBreakPoint(Handle<Object> break_point_object);
void ClearAllBreakPoints();
void FloodWithOneShot(Handle<JSFunction> function);
void FloodWithOneShot(Handle<JSFunction> function,
BreakLocatorType type = ALL_BREAK_LOCATIONS);
void FloodBoundFunctionWithOneShot(Handle<JSFunction> function);
void FloodHandlerWithOneShot();
void ChangeBreakOnException(ExceptionBreakType type, bool enable);
......@@ -401,10 +404,8 @@ class Debug {
bool StepNextContinue(BreakLocationIterator* break_location_iterator,
JavaScriptFrame* frame);
bool StepInActive() { return thread_local_.step_into_fp_ != 0; }
void HandleStepIn(Handle<JSFunction> function,
Handle<Object> holder,
Address fp,
bool is_constructor);
void HandleStepIn(Handle<Object> function_obj, Handle<Object> holder,
Address fp, bool is_constructor);
bool StepOutActive() { return thread_local_.step_out_fp_ != 0; }
// Purge all code objects that have no debug break slots.
......@@ -623,7 +624,7 @@ class Debug {
// Number of steps left to perform before debug event.
int step_count_;
// Frame pointer from last step next action.
// Frame pointer from last step next or step frame action.
Address last_fp_;
// Number of queued steps left to perform before debug event.
......
......@@ -542,9 +542,8 @@ MaybeHandle<Object> Object::GetPropertyWithDefinedGetter(
Debug* debug = isolate->debug();
// Handle stepping into a getter if step into is active.
// TODO(rossberg): should this apply to getters that are function proxies?
if (debug->StepInActive() && getter->IsJSFunction()) {
debug->HandleStepIn(
Handle<JSFunction>::cast(getter), Handle<Object>::null(), 0, false);
if (debug->is_active()) {
debug->HandleStepIn(getter, Handle<Object>::null(), 0, false);
}
return Execution::Call(isolate, getter, receiver, 0, NULL, true);
......@@ -560,9 +559,8 @@ MaybeHandle<Object> Object::SetPropertyWithDefinedSetter(
Debug* debug = isolate->debug();
// Handle stepping into a setter if step into is active.
// TODO(rossberg): should this apply to getters that are function proxies?
if (debug->StepInActive() && setter->IsJSFunction()) {
debug->HandleStepIn(
Handle<JSFunction>::cast(setter), Handle<Object>::null(), 0, false);
if (debug->is_active()) {
debug->HandleStepIn(setter, Handle<Object>::null(), 0, false);
}
Handle<Object> argv[] = { value };
......
......@@ -1984,7 +1984,7 @@ RUNTIME_FUNCTION(Runtime_PrepareStep) {
StepAction step_action = static_cast<StepAction>(NumberToInt32(args[1]));
if (step_action != StepIn && step_action != StepNext &&
step_action != StepOut && step_action != StepInMin &&
step_action != StepMin) {
step_action != StepMin && step_action != StepFrame) {
return isolate->Throw(isolate->heap()->illegal_argument_string());
}
......
// Copyright 2014 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --expose-debug-as debug
function f0() {
var v00 = 0; // Break 1
var v01 = 1;
// Normal function call in a catch scope.
try {
throw 1;
} catch (e) {
try{
f1();
} catch (e) {
var v02 = 2; // Break 13
}
}
var v03 = 3;
var v04 = 4;
}
function f1() {
var v10 = 0; // Break 2
var v11 = 1;
// Getter call.
var v12 = o.get;
var v13 = 3 // Break 4
// Setter call.
o.set = 2;
var v14 = 4; // Break 6
// Function.prototype.call.
f2.call();
var v15 = 5; // Break 12
var v16 = 6;
// Exit function by throw.
throw 1;
var v17 = 7;
}
function get() {
var g0 = 0; // Break 3
var g1 = 1;
return 3;
}
function set() {
var s0 = 0; // Break 5
return 3;
}
function f2() {
var v20 = 0; // Break 7
// Construct call.
var v21 = new c0();
var v22 = 2; // Break 9
// Bound function.
b0();
return 2; // Break 11
}
function c0() {
this.v0 = 0; // Break 8
this.v1 = 1;
}
function f3() {
var v30 = 0; // Break 10
var v31 = 1;
return 3;
}
var b0 = f3.bind(o);
var o = {};
Object.defineProperty(o, "get", { get : get });
Object.defineProperty(o, "set", { set : set });
Debug = debug.Debug;
var break_count = 0
var exception = null;
var step_size;
function listener(event, exec_state, event_data, data) {
if (event != Debug.DebugEvent.Break) return;
try {
var line = exec_state.frame(0).sourceLineText();
print(line);
var match = line.match(/\/\/ Break (\d+)$/);
assertEquals(2, match.length);
assertEquals(break_count, parseInt(match[1]));
break_count += step_size;
exec_state.prepareStep(Debug.StepAction.StepFrame, step_size);
} catch (e) {
print(e + e.stack);
exception = e;
}
}
for (step_size = 1; step_size < 6; step_size++) {
print("step size = " + step_size);
break_count = 0;
Debug.setListener(listener);
debugger; // Break 0
f0();
Debug.setListener(null); // Break 14
assertTrue(break_count > 14);
}
assertNull(exception);
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