From fcff66bf29fef8a9d568ebb4cb7192ab32afe3f7 Mon Sep 17 00:00:00 2001 From: Ryan Date: Wed, 9 Sep 2009 22:01:54 +0200 Subject: Upgrade v8 to 1.3.10 --- deps/v8/src/debug.cc | 213 +++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 182 insertions(+), 31 deletions(-) (limited to 'deps/v8/src/debug.cc') 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 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 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 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::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 maybe_call_function_stub = code; + if (IsDebugBreak()) { + Address original_target = original_rinfo()->target_address(); + maybe_call_function_stub = + Handle(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 Debug::debug_context_ = Handle(); -Code* Debug::debug_break_return_entry_ = NULL; Code* Debug::debug_break_return_ = NULL; @@ -583,11 +643,6 @@ void ScriptCache::HandleWeakScript(v8::Persistent 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(&(debug_break_return_entry_))); v->VisitPointer(bit_cast(&(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 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(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(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 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 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(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(); } -- cgit v1.2.1