summaryrefslogtreecommitdiff
path: root/deps/v8/src/debug.cc
diff options
context:
space:
mode:
authorRyan <ry@tinyclouds.org>2009-09-09 22:01:54 +0200
committerRyan <ry@tinyclouds.org>2009-09-09 22:01:54 +0200
commitfcff66bf29fef8a9d568ebb4cb7192ab32afe3f7 (patch)
tree8ea321ed7f248a54403d86154d4df325960d41ef /deps/v8/src/debug.cc
parentefb2b703a655b29a692819c8bdb191792da6416e (diff)
downloadnode-new-fcff66bf29fef8a9d568ebb4cb7192ab32afe3f7.tar.gz
Upgrade v8 to 1.3.10
Diffstat (limited to 'deps/v8/src/debug.cc')
-rw-r--r--deps/v8/src/debug.cc213
1 files changed, 182 insertions, 31 deletions
diff --git a/deps/v8/src/debug.cc b/deps/v8/src/debug.cc
index e341022aa4..f3e11ae751 100644
--- a/deps/v8/src/debug.cc
+++ b/deps/v8/src/debug.cc
@@ -75,6 +75,9 @@ BreakLocationIterator::BreakLocationIterator(Handle<DebugInfo> debug_info,
BreakLocatorType type) {
debug_info_ = debug_info;
type_ = type;
+ // Get the stub early to avoid possible GC during iterations. We may need
+ // this stub to detect debugger calls generated from debugger statements.
+ debug_break_stub_ = RuntimeStub(Runtime::kDebugBreak, 0).GetCode();
reloc_iterator_ = NULL;
reloc_iterator_original_ = NULL;
Reset(); // Initialize the rest of the member variables.
@@ -126,6 +129,10 @@ void BreakLocationIterator::Next() {
return;
}
if (code->kind() == Code::STUB) {
+ if (IsDebuggerStatement()) {
+ break_point_++;
+ return;
+ }
if (type_ == ALL_BREAK_LOCATIONS) {
if (Debug::IsBreakStub(code)) {
break_point_++;
@@ -238,7 +245,7 @@ void BreakLocationIterator::SetBreakPoint(Handle<Object> break_point_object) {
if (!HasBreakPoint()) {
SetDebugBreak();
}
- ASSERT(IsDebugBreak());
+ ASSERT(IsDebugBreak() || IsDebuggerStatement());
// Set the break point information.
DebugInfo::SetBreakPoint(debug_info_, code_position(),
position(), statement_position(),
@@ -258,6 +265,11 @@ void BreakLocationIterator::ClearBreakPoint(Handle<Object> break_point_object) {
void BreakLocationIterator::SetOneShot() {
+ // Debugger statement always calls debugger. No need to modify it.
+ if (IsDebuggerStatement()) {
+ return;
+ }
+
// If there is a real break point here no more to do.
if (HasBreakPoint()) {
ASSERT(IsDebugBreak());
@@ -270,6 +282,11 @@ void BreakLocationIterator::SetOneShot() {
void BreakLocationIterator::ClearOneShot() {
+ // Debugger statement always calls debugger. No need to modify it.
+ if (IsDebuggerStatement()) {
+ return;
+ }
+
// If there is a real break point here no more to do.
if (HasBreakPoint()) {
ASSERT(IsDebugBreak());
@@ -283,6 +300,11 @@ void BreakLocationIterator::ClearOneShot() {
void BreakLocationIterator::SetDebugBreak() {
+ // Debugger statement always calls debugger. No need to modify it.
+ if (IsDebuggerStatement()) {
+ return;
+ }
+
// If there is already a break point here just return. This might happen if
// the same code is flooded with break points twice. Flooding the same
// function twice might happen when stepping in a function with an exception
@@ -303,6 +325,11 @@ void BreakLocationIterator::SetDebugBreak() {
void BreakLocationIterator::ClearDebugBreak() {
+ // Debugger statement always calls debugger. No need to modify it.
+ if (IsDebuggerStatement()) {
+ return;
+ }
+
if (RelocInfo::IsJSReturn(rmode())) {
// Restore the frame exit code.
ClearDebugBreakAtReturn();
@@ -317,10 +344,10 @@ void BreakLocationIterator::ClearDebugBreak() {
void BreakLocationIterator::PrepareStepIn() {
HandleScope scope;
- // Step in can only be prepared if currently positioned on an IC call or
- // construct call.
+ // Step in can only be prepared if currently positioned on an IC call,
+ // construct call or CallFunction stub call.
Address target = rinfo()->target_address();
- Code* code = Code::GetCodeFromTargetAddress(target);
+ Handle<Code> code(Code::GetCodeFromTargetAddress(target));
if (code->is_call_stub()) {
// Step in through IC call is handled by the runtime system. Therefore make
// sure that the any current IC is cleared and the runtime system is
@@ -334,11 +361,29 @@ void BreakLocationIterator::PrepareStepIn() {
rinfo()->set_target_address(stub->entry());
}
} else {
+#ifdef DEBUG
+ // All the following stuff is needed only for assertion checks so the code
+ // is wrapped in ifdef.
+ Handle<Code> maybe_call_function_stub = code;
+ if (IsDebugBreak()) {
+ Address original_target = original_rinfo()->target_address();
+ maybe_call_function_stub =
+ Handle<Code>(Code::GetCodeFromTargetAddress(original_target));
+ }
+ bool is_call_function_stub =
+ (maybe_call_function_stub->kind() == Code::STUB &&
+ maybe_call_function_stub->major_key() == CodeStub::CallFunction);
+
// Step in through construct call requires no changes to the running code.
// Step in through getters/setters should already be prepared as well
// because caller of this function (Debug::PrepareStep) is expected to
// flood the top frame's function with one shot breakpoints.
- ASSERT(RelocInfo::IsConstructCall(rmode()) || code->is_inline_cache_stub());
+ // Step in through CallFunction stub should also be prepared by caller of
+ // this function (Debug::PrepareStep) which should flood target function
+ // with breakpoints.
+ ASSERT(RelocInfo::IsConstructCall(rmode()) || code->is_inline_cache_stub()
+ || is_call_function_stub);
+#endif
}
}
@@ -409,6 +454,21 @@ void BreakLocationIterator::ClearDebugBreakAtIC() {
}
+bool BreakLocationIterator::IsDebuggerStatement() {
+ if (RelocInfo::IsCodeTarget(rmode())) {
+ Address target = original_rinfo()->target_address();
+ Code* code = Code::GetCodeFromTargetAddress(target);
+ if (code->kind() == Code::STUB) {
+ CodeStub::Major major_key = code->major_key();
+ if (major_key == CodeStub::Runtime) {
+ return (*debug_break_stub_ == code);
+ }
+ }
+ }
+ return false;
+}
+
+
Object* BreakLocationIterator::BreakPointObjects() {
return debug_info_->GetBreakPointObjects(code_position());
}
@@ -458,6 +518,7 @@ void Debug::ThreadInit() {
thread_local_.step_count_ = 0;
thread_local_.last_fp_ = 0;
thread_local_.step_into_fp_ = 0;
+ thread_local_.step_out_fp_ = 0;
thread_local_.after_break_target_ = 0;
thread_local_.debugger_entry_ = NULL;
thread_local_.pending_interrupts_ = 0;
@@ -502,7 +563,6 @@ bool Debug::break_on_exception_ = false;
bool Debug::break_on_uncaught_exception_ = true;
Handle<Context> Debug::debug_context_ = Handle<Context>();
-Code* Debug::debug_break_return_entry_ = NULL;
Code* Debug::debug_break_return_ = NULL;
@@ -583,11 +643,6 @@ void ScriptCache::HandleWeakScript(v8::Persistent<v8::Value> obj, void* data) {
void Debug::Setup(bool create_heap_objects) {
ThreadInit();
if (create_heap_objects) {
- // Get code to handle entry to debug break on return.
- debug_break_return_entry_ =
- Builtins::builtin(Builtins::Return_DebugBreakEntry);
- ASSERT(debug_break_return_entry_->IsCode());
-
// Get code to handle debug break on return.
debug_break_return_ =
Builtins::builtin(Builtins::Return_DebugBreak);
@@ -749,7 +804,6 @@ void Debug::PreemptionWhileInDebugger() {
void Debug::Iterate(ObjectVisitor* v) {
- v->VisitPointer(bit_cast<Object**, Code**>(&(debug_break_return_entry_)));
v->VisitPointer(bit_cast<Object**, Code**>(&(debug_break_return_)));
}
@@ -804,11 +858,18 @@ Object* Debug::Break(Arguments args) {
break_points_hit = CheckBreakPoints(break_point_objects);
}
- // Notify debugger if a real break point is triggered or if performing single
- // stepping with no more steps to perform. Otherwise do another step.
- if (!break_points_hit->IsUndefined() ||
- (thread_local_.last_step_action_ != StepNone &&
- thread_local_.step_count_ == 0)) {
+ // If step out is active skip everything until the frame where we need to step
+ // out to is reached, unless real breakpoint is hit.
+ if (Debug::StepOutActive() && frame->fp() != Debug::step_out_fp() &&
+ break_points_hit->IsUndefined() ) {
+ // Step count should always be 0 for StepOut.
+ ASSERT(thread_local_.step_count_ == 0);
+ } else if (!break_points_hit->IsUndefined() ||
+ (thread_local_.last_step_action_ != StepNone &&
+ thread_local_.step_count_ == 0)) {
+ // Notify debugger if a real break point is triggered or if performing
+ // single stepping with no more steps to perform. Otherwise do another step.
+
// Clear all current stepping setup.
ClearStepping();
@@ -1044,7 +1105,13 @@ void Debug::PrepareStep(StepAction step_action, int step_count) {
// Remember this step action and count.
thread_local_.last_step_action_ = step_action;
- thread_local_.step_count_ = step_count;
+ if (step_action == StepOut) {
+ // For step out target frame will be found on the stack so there is no need
+ // to set step counter for it. It's expected to always be 0 for StepOut.
+ thread_local_.step_count_ = 0;
+ } else {
+ thread_local_.step_count_ = step_count;
+ }
// Get the frame where the execution has stopped and skip the debug frame if
// any. The debug frame will only be present if execution was stopped due to
@@ -1092,6 +1159,7 @@ void Debug::PrepareStep(StepAction step_action, int step_count) {
bool is_call_target = false;
bool is_load_or_store = false;
bool is_inline_cache_stub = false;
+ Handle<Code> call_function_stub;
if (RelocInfo::IsCodeTarget(it.rinfo()->rmode())) {
Address target = it.rinfo()->target_address();
Code* code = Code::GetCodeFromTargetAddress(target);
@@ -1102,19 +1170,51 @@ void Debug::PrepareStep(StepAction step_action, int step_count) {
is_inline_cache_stub = true;
is_load_or_store = !is_call_target;
}
+
+ // Check if target code is CallFunction stub.
+ Code* maybe_call_function_stub = code;
+ // If there is a breakpoint at this line look at the original code to
+ // check if it is a CallFunction stub.
+ if (it.IsDebugBreak()) {
+ Address original_target = it.original_rinfo()->target_address();
+ maybe_call_function_stub =
+ Code::GetCodeFromTargetAddress(original_target);
+ }
+ if (maybe_call_function_stub->kind() == Code::STUB &&
+ maybe_call_function_stub->major_key() == CodeStub::CallFunction) {
+ // 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);
+ }
}
// If this is the last break code target step out is the only possibility.
if (it.IsExit() || step_action == StepOut) {
+ if (step_action == StepOut) {
+ // Skip step_count frames starting with the current one.
+ while (step_count-- > 0 && !frames_it.done()) {
+ frames_it.Advance();
+ }
+ } else {
+ ASSERT(it.IsExit());
+ frames_it.Advance();
+ }
+ // Skip builtin functions on the stack.
+ while (!frames_it.done() &&
+ JSFunction::cast(frames_it.frame()->function())->IsBuiltin()) {
+ frames_it.Advance();
+ }
// Step out: If there is a JavaScript caller frame, we need to
// flood it with breakpoints.
- frames_it.Advance();
if (!frames_it.done()) {
// Fill the function to return to with one-shot break points.
JSFunction* function = JSFunction::cast(frames_it.frame()->function());
FloodWithOneShot(Handle<SharedFunctionInfo>(function->shared()));
+ // Set target frame pointer.
+ ActivateStepOut(frames_it.frame());
}
- } else if (!(is_inline_cache_stub || RelocInfo::IsConstructCall(it.rmode()))
+ } else if (!(is_inline_cache_stub || RelocInfo::IsConstructCall(it.rmode()) ||
+ !call_function_stub.is_null())
|| step_action == StepNext || step_action == StepMin) {
// Step next or step min.
@@ -1126,6 +1226,45 @@ void Debug::PrepareStep(StepAction step_action, int step_count) {
debug_info->code()->SourceStatementPosition(frame->pc());
thread_local_.last_fp_ = frame->fp();
} else {
+ // If it's CallFunction stub ensure target function is compiled and flood
+ // it with one shot breakpoints.
+ if (!call_function_stub.is_null()) {
+ // Find out number of arguments from the stub minor key.
+ // Reverse lookup required as the minor key cannot be retrieved
+ // from the code object.
+ Handle<Object> obj(
+ Heap::code_stubs()->SlowReverseLookup(*call_function_stub));
+ ASSERT(*obj != Heap::undefined_value());
+ ASSERT(obj->IsSmi());
+ // Get the STUB key and extract major and minor key.
+ uint32_t key = Smi::cast(*obj)->value();
+ // Argc in the stub is the number of arguments passed - not the
+ // expected arguments of the called function.
+ int call_function_arg_count = CodeStub::MinorKeyFromKey(key);
+ ASSERT(call_function_stub->major_key() ==
+ CodeStub::MajorKeyFromKey(key));
+
+ // Find target function on the expression stack.
+ // Expression stack lools like this (top to bottom):
+ // argN
+ // ...
+ // arg0
+ // Receiver
+ // Function to call
+ int expressions_count = frame->ComputeExpressionsCount();
+ ASSERT(expressions_count - 2 - call_function_arg_count >= 0);
+ Object* fun = frame->GetExpression(
+ expressions_count - 2 - call_function_arg_count);
+ if (fun->IsJSFunction()) {
+ Handle<JSFunction> js_function(JSFunction::cast(fun));
+ // Don't step into builtins.
+ if (!js_function->IsBuiltin()) {
+ // It will also compile target function if it's not compiled yet.
+ FloodWithOneShot(Handle<SharedFunctionInfo>(js_function->shared()));
+ }
+ }
+ }
+
// Fill the current function with one-shot break points even for step in on
// 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
@@ -1328,6 +1467,7 @@ void Debug::ClearStepping() {
// Clear the various stepping setup.
ClearOneShot();
ClearStepIn();
+ ClearStepOut();
ClearStepNext();
// Clear multiple step counter.
@@ -1355,6 +1495,7 @@ void Debug::ClearOneShot() {
void Debug::ActivateStepIn(StackFrame* frame) {
+ ASSERT(!StepOutActive());
thread_local_.step_into_fp_ = frame->fp();
}
@@ -1364,6 +1505,17 @@ void Debug::ClearStepIn() {
}
+void Debug::ActivateStepOut(StackFrame* frame) {
+ ASSERT(!StepInActive());
+ thread_local_.step_out_fp_ = frame->fp();
+}
+
+
+void Debug::ClearStepOut() {
+ thread_local_.step_out_fp_ = 0;
+}
+
+
void Debug::ClearStepNext() {
thread_local_.last_step_action_ = StepNone;
thread_local_.last_statement_position_ = RelocInfo::kNoPosition;
@@ -1455,26 +1607,25 @@ void Debug::SetAfterBreakTarget(JavaScriptFrame* frame) {
Address addr = frame->pc() - Assembler::kPatchReturnSequenceLength;
// Check if the location is at JS exit.
- bool at_js_exit = false;
+ bool at_js_return = false;
+ bool break_at_js_return_active = false;
RelocIterator it(debug_info->code());
while (!it.done()) {
if (RelocInfo::IsJSReturn(it.rinfo()->rmode())) {
- at_js_exit = (it.rinfo()->pc() ==
- addr - Assembler::kPatchReturnSequenceAddressOffset);
+ at_js_return = (it.rinfo()->pc() ==
+ addr - Assembler::kPatchReturnSequenceAddressOffset);
+ break_at_js_return_active = it.rinfo()->IsCallInstruction();
}
it.next();
}
// Handle the jump to continue execution after break point depending on the
// break location.
- if (at_js_exit) {
- // First check if the call in the code is still the debug break return
- // entry code. If it is the break point is still active. If not the break
- // point was removed during break point processing.
- if (Assembler::target_address_at(addr) ==
- debug_break_return_entry()->entry()) {
- // Break point still active. Jump to the corresponding place in the
- // original code.
+ if (at_js_return) {
+ // If the break point as return is still active jump to the corresponding
+ // place in the original code. If not the break point was removed during
+ // break point processing.
+ if (break_at_js_return_active) {
addr += original_code->instruction_start() - code->instruction_start();
}