diff options
Diffstat (limited to 'deps/v8/src/debug/debug.cc')
-rw-r--r-- | deps/v8/src/debug/debug.cc | 251 |
1 files changed, 116 insertions, 135 deletions
diff --git a/deps/v8/src/debug/debug.cc b/deps/v8/src/debug/debug.cc index 93c914c3f8..6e94012579 100644 --- a/deps/v8/src/debug/debug.cc +++ b/deps/v8/src/debug/debug.cc @@ -16,7 +16,6 @@ #include "src/frames-inl.h" #include "src/full-codegen/full-codegen.h" #include "src/global-handles.h" -#include "src/interpreter/bytecodes.h" #include "src/interpreter/interpreter.h" #include "src/isolate-inl.h" #include "src/list.h" @@ -84,12 +83,23 @@ BreakLocation::Iterator::Iterator(Handle<DebugInfo> debug_info) position_(1), statement_position_(1) {} +int BreakLocation::Iterator::ReturnPosition() { + if (debug_info_->shared()->HasSourceCode()) { + return debug_info_->shared()->end_position() - + debug_info_->shared()->start_position() - 1; + } else { + return 0; + } +} + BreakLocation::CodeIterator::CodeIterator(Handle<DebugInfo> debug_info, BreakLocatorType type) : Iterator(debug_info), reloc_iterator_(debug_info->abstract_code()->GetCode(), GetModeMask(type)) { - if (!Done()) Next(); + // There is at least one break location. + DCHECK(!Done()); + Next(); } int BreakLocation::CodeIterator::GetModeMask(BreakLocatorType type) { @@ -98,6 +108,9 @@ int BreakLocation::CodeIterator::GetModeMask(BreakLocatorType type) { mask |= RelocInfo::ModeMask(RelocInfo::STATEMENT_POSITION); mask |= RelocInfo::ModeMask(RelocInfo::DEBUG_BREAK_SLOT_AT_RETURN); mask |= RelocInfo::ModeMask(RelocInfo::DEBUG_BREAK_SLOT_AT_CALL); + if (isolate()->is_tail_call_elimination_enabled()) { + mask |= RelocInfo::ModeMask(RelocInfo::DEBUG_BREAK_SLOT_AT_TAIL_CALL); + } if (type == ALL_BREAK_LOCATIONS) { mask |= RelocInfo::ModeMask(RelocInfo::DEBUG_BREAK_SLOT_AT_POSITION); mask |= RelocInfo::ModeMask(RelocInfo::DEBUGGER_STATEMENT); @@ -137,13 +150,7 @@ void BreakLocation::CodeIterator::Next() { if (RelocInfo::IsDebugBreakSlotAtReturn(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_; + statement_position_ = position_ = ReturnPosition(); } break; @@ -157,6 +164,10 @@ BreakLocation BreakLocation::CodeIterator::GetBreakLocation() { type = DEBUG_BREAK_SLOT_AT_RETURN; } else if (RelocInfo::IsDebugBreakSlotAtCall(rmode())) { type = DEBUG_BREAK_SLOT_AT_CALL; + } else if (RelocInfo::IsDebugBreakSlotAtTailCall(rmode())) { + type = isolate()->is_tail_call_elimination_enabled() + ? DEBUG_BREAK_SLOT_AT_TAIL_CALL + : DEBUG_BREAK_SLOT_AT_CALL; } else if (RelocInfo::IsDebuggerStatement(rmode())) { type = DEBUGGER_STATEMENT; } else if (RelocInfo::IsDebugBreakSlot(rmode())) { @@ -171,11 +182,14 @@ BreakLocation BreakLocation::CodeIterator::GetBreakLocation() { BreakLocation::BytecodeArrayIterator::BytecodeArrayIterator( Handle<DebugInfo> debug_info, BreakLocatorType type) : Iterator(debug_info), - source_position_iterator_( - debug_info->abstract_code()->GetBytecodeArray()), + source_position_iterator_(debug_info->abstract_code() + ->GetBytecodeArray() + ->source_position_table()), break_locator_type_(type), start_position_(debug_info->shared()->start_position()) { - if (!Done()) Next(); + // There is at least one break location. + DCHECK(!Done()); + Next(); } void BreakLocation::BytecodeArrayIterator::Next() { @@ -192,7 +206,6 @@ void BreakLocation::BytecodeArrayIterator::Next() { } DCHECK(position_ >= 0); DCHECK(statement_position_ >= 0); - break_index_++; enum DebugBreakType type = GetDebugBreakType(); if (type == NOT_DEBUG_BREAK) continue; @@ -200,11 +213,14 @@ void BreakLocation::BytecodeArrayIterator::Next() { if (break_locator_type_ == ALL_BREAK_LOCATIONS) break; DCHECK_EQ(CALLS_AND_RETURNS, break_locator_type_); - if (type == DEBUG_BREAK_SLOT_AT_CALL || - type == DEBUG_BREAK_SLOT_AT_RETURN) { + if (type == DEBUG_BREAK_SLOT_AT_CALL) break; + if (type == DEBUG_BREAK_SLOT_AT_RETURN) { + DCHECK_EQ(ReturnPosition(), position_); + DCHECK_EQ(ReturnPosition(), statement_position_); break; } } + break_index_++; } BreakLocation::DebugBreakType @@ -217,6 +233,10 @@ BreakLocation::BytecodeArrayIterator::GetDebugBreakType() { return DEBUGGER_STATEMENT; } else if (bytecode == interpreter::Bytecode::kReturn) { return DEBUG_BREAK_SLOT_AT_RETURN; + } else if (bytecode == interpreter::Bytecode::kTailCall) { + return isolate()->is_tail_call_elimination_enabled() + ? DEBUG_BREAK_SLOT_AT_TAIL_CALL + : DEBUG_BREAK_SLOT_AT_CALL; } else if (interpreter::Bytecodes::IsCallOrNew(bytecode)) { return DEBUG_BREAK_SLOT_AT_CALL; } else if (source_position_iterator_.is_statement()) { @@ -261,21 +281,6 @@ BreakLocation BreakLocation::FromFrame(Handle<DebugInfo> debug_info, return FromCodeOffset(debug_info, call_offset); } -// Find the break point at the supplied address, or the closest one before -// the address. -void BreakLocation::FromCodeOffsetSameStatement( - Handle<DebugInfo> debug_info, int offset, List<BreakLocation>* result_out) { - int break_index = BreakIndexFromCodeOffset(debug_info, offset); - base::SmartPointer<Iterator> it(GetIterator(debug_info)); - it->SkipTo(break_index); - int statement_position = it->statement_position(); - while (!it->Done() && it->statement_position() == statement_position) { - result_out->Add(it->GetBreakLocation()); - it->Next(); - } -} - - void BreakLocation::AllForStatementPosition(Handle<DebugInfo> debug_info, int statement_position, List<BreakLocation>* result_out) { @@ -479,6 +484,7 @@ void Debug::ThreadInit() { thread_local_.last_fp_ = 0; thread_local_.target_fp_ = 0; thread_local_.step_in_enabled_ = false; + thread_local_.return_value_ = Handle<Object>(); // TODO(isolates): frames_are_dropped_? base::NoBarrier_Store(&thread_local_.current_debug_scope_, static_cast<base::AtomicWord>(0)); @@ -565,10 +571,8 @@ void Debug::Unload() { debug_context_ = Handle<Context>(); } - -void Debug::Break(Arguments args, JavaScriptFrame* frame) { +void Debug::Break(JavaScriptFrame* frame) { HandleScope scope(isolate_); - DCHECK(args.length() == 0); // Initialize LiveEdit. LiveEdit::InitializeThreadLocal(this); @@ -611,22 +615,26 @@ void Debug::Break(Arguments args, JavaScriptFrame* frame) { Address target_fp = thread_local_.target_fp_; Address last_fp = thread_local_.last_fp_; - bool step_break = true; + bool step_break = false; switch (step_action) { case StepNone: return; case StepOut: // Step out has not reached the target frame yet. if (current_fp < target_fp) return; + step_break = true; break; case StepNext: // Step next should not break in a deeper frame. if (current_fp < target_fp) return; + // For step-next, a tail call is like a return and should break. + step_break = location.IsTailCall(); // Fall through. case StepIn: { FrameSummary summary = GetFirstFrameSummary(frame); int offset = summary.code_offset(); - step_break = location.IsReturn() || (current_fp != last_fp) || + step_break = step_break || location.IsReturn() || + (current_fp != last_fp) || (thread_local_.last_statement_position_ != location.abstract_code()->SourceStatementPosition(offset)); break; @@ -722,9 +730,10 @@ MaybeHandle<Object> Debug::CallFunction(const char* name, int argc, Handle<Object> args[]) { PostponeInterruptsScope no_interrupts(isolate_); AssertDebugContext(); - Handle<Object> holder = isolate_->natives_utils_object(); + Handle<JSReceiver> holder = + Handle<JSReceiver>::cast(isolate_->natives_utils_object()); Handle<JSFunction> fun = Handle<JSFunction>::cast( - Object::GetProperty(isolate_, holder, name).ToHandleChecked()); + JSReceiver::GetProperty(isolate_, holder, name).ToHandleChecked()); Handle<Object> undefined = isolate_->factory()->undefined_value(); return Execution::TryCall(isolate_, fun, undefined, argc, args); } @@ -1021,8 +1030,10 @@ void Debug::PrepareStep(StepAction step_action) { BreakLocation location = BreakLocation::FromCodeOffset(debug_info, call_offset); - // At a return statement we will step out either way. + // Any step at a return is a step-out. if (location.IsReturn()) step_action = StepOut; + // A step-next at a tail call is a step-out. + if (location.IsTailCall() && step_action == StepNext) step_action = StepOut; thread_local_.last_statement_position_ = debug_info->abstract_code()->SourceStatementPosition( @@ -1309,6 +1320,7 @@ bool Debug::PrepareFunctionForBreakPoints(Handle<SharedFunctionInfo> shared) { // Make sure we abort incremental marking. isolate_->heap()->CollectAllGarbage(Heap::kMakeHeapIterableMask, "prepare for break points"); + bool is_interpreted = shared->HasBytecodeArray(); { @@ -1514,7 +1526,7 @@ bool Debug::EnsureDebugInfo(Handle<SharedFunctionInfo> shared, if (function.is_null()) { DCHECK(shared->HasDebugCode()); - } else if (!Compiler::Compile(function, CLEAR_EXCEPTION)) { + } else if (!Compiler::Compile(function, Compiler::CLEAR_EXCEPTION)) { return false; } @@ -1572,24 +1584,11 @@ void Debug::RemoveDebugInfoAndClearFromShared(Handle<DebugInfo> debug_info) { UNREACHABLE(); } -Object* Debug::SetAfterBreakTarget(JavaScriptFrame* frame) { - if (frame->is_interpreted()) { - // Find the handler from the original bytecode array. - InterpretedFrame* interpreted_frame = - reinterpret_cast<InterpretedFrame*>(frame); - SharedFunctionInfo* shared = interpreted_frame->function()->shared(); - BytecodeArray* bytecode_array = shared->bytecode_array(); - int bytecode_offset = interpreted_frame->GetBytecodeOffset(); - interpreter::Bytecode bytecode = - interpreter::Bytecodes::FromByte(bytecode_array->get(bytecode_offset)); - return isolate_->interpreter()->GetBytecodeHandler(bytecode); - } else { - after_break_target_ = NULL; - if (!LiveEdit::SetAfterBreakTarget(this)) { - // Continue just after the slot. - after_break_target_ = frame->pc(); - } - return isolate_->heap()->undefined_value(); +void Debug::SetAfterBreakTarget(JavaScriptFrame* frame) { + after_break_target_ = NULL; + if (!LiveEdit::SetAfterBreakTarget(this)) { + // Continue just after the slot. + after_break_target_ = frame->pc(); } } @@ -1610,7 +1609,7 @@ bool Debug::IsBreakAtReturn(JavaScriptFrame* frame) { Handle<DebugInfo> debug_info(shared->GetDebugInfo()); BreakLocation location = BreakLocation::FromCodeOffset(debug_info, summary.code_offset()); - return location.IsReturn(); + return location.IsReturn() || location.IsTailCall(); } @@ -1657,45 +1656,6 @@ Handle<FixedArray> Debug::GetLoadedScripts() { } -void Debug::GetStepinPositions(JavaScriptFrame* frame, StackFrame::Id frame_id, - List<int>* results_out) { - FrameSummary summary = GetFirstFrameSummary(frame); - - Handle<JSFunction> fun = Handle<JSFunction>(summary.function()); - Handle<SharedFunctionInfo> shared = Handle<SharedFunctionInfo>(fun->shared()); - - if (!EnsureDebugInfo(shared, fun)) return; - - Handle<DebugInfo> debug_info(shared->GetDebugInfo()); - // Refresh frame summary if the code has been recompiled for debugging. - if (AbstractCode::cast(shared->code()) != *summary.abstract_code()) { - summary = GetFirstFrameSummary(frame); - } - - int call_offset = - CallOffsetFromCodeOffset(summary.code_offset(), frame->is_interpreted()); - List<BreakLocation> locations; - BreakLocation::FromCodeOffsetSameStatement(debug_info, call_offset, - &locations); - - for (BreakLocation location : locations) { - if (location.code_offset() <= summary.code_offset()) { - // The break point is near our pc. Could be a step-in possibility, - // that is currently taken by active debugger call. - if (break_frame_id() == StackFrame::NO_ID) { - continue; // We are not stepping. - } else { - JavaScriptFrameIterator frame_it(isolate_, break_frame_id()); - // If our frame is a top frame and we are stepping, we can do step-in - // at this place. - if (frame_it.frame()->id() != frame_id) continue; - } - } - if (location.IsCall()) results_out->Add(location.position()); - } -} - - void Debug::RecordEvalCaller(Handle<Script> script) { script->set_compilation_type(Script::COMPILATION_TYPE_EVAL); // For eval scripts add information on the function from which eval was @@ -1748,13 +1708,6 @@ MaybeHandle<Object> Debug::MakeCompileEvent(Handle<Script> script, } -MaybeHandle<Object> Debug::MakePromiseEvent(Handle<JSObject> event_data) { - // Create the promise event object. - Handle<Object> argv[] = { event_data }; - return CallFunction("MakePromiseEvent", arraysize(argv), argv); -} - - MaybeHandle<Object> Debug::MakeAsyncTaskEvent(Handle<JSObject> task_event) { // Create the async task event object. Handle<Object> argv[] = { task_event }; @@ -1852,6 +1805,10 @@ void Debug::OnDebugBreak(Handle<Object> break_points_hit, bool auto_continue) { // Bail out if there is no listener for this event if (ignore_events()) return; +#ifdef DEBUG + PrintBreakLocation(); +#endif // DEBUG + HandleScope scope(isolate_); // Create the event data object. Handle<Object> event_data; @@ -1881,25 +1838,6 @@ void Debug::OnAfterCompile(Handle<Script> script) { } -void Debug::OnPromiseEvent(Handle<JSObject> data) { - if (in_debug_scope() || ignore_events()) return; - - HandleScope scope(isolate_); - DebugScope debug_scope(this); - if (debug_scope.failed()) return; - - // Create the script collected state object. - Handle<Object> event_data; - // Bail out and don't call debugger if exception. - if (!MakePromiseEvent(data).ToHandle(&event_data)) return; - - // Process debug event. - ProcessDebugEvent(v8::PromiseEvent, - Handle<JSObject>::cast(event_data), - true); -} - - void Debug::OnAsyncTaskEvent(Handle<JSObject> data) { if (in_debug_scope() || ignore_events()) return; @@ -2049,7 +1987,6 @@ void Debug::NotifyMessageHandler(v8::DebugEvent event, case v8::NewFunction: case v8::BeforeCompile: case v8::CompileError: - case v8::PromiseEvent: case v8::AsyncTaskEvent: break; case v8::Exception: @@ -2084,16 +2021,19 @@ void Debug::NotifyMessageHandler(v8::DebugEvent event, // DebugCommandProcessor goes here. bool running = auto_continue; - Handle<Object> cmd_processor_ctor = Object::GetProperty( - isolate_, exec_state, "debugCommandProcessor").ToHandleChecked(); + Handle<Object> cmd_processor_ctor = + JSReceiver::GetProperty(isolate_, exec_state, "debugCommandProcessor") + .ToHandleChecked(); Handle<Object> ctor_args[] = { isolate_->factory()->ToBoolean(running) }; - Handle<Object> cmd_processor = Execution::Call( - isolate_, cmd_processor_ctor, exec_state, 1, ctor_args).ToHandleChecked(); + Handle<JSReceiver> cmd_processor = Handle<JSReceiver>::cast( + Execution::Call(isolate_, cmd_processor_ctor, exec_state, 1, ctor_args) + .ToHandleChecked()); Handle<JSFunction> process_debug_request = Handle<JSFunction>::cast( - Object::GetProperty( - isolate_, cmd_processor, "processDebugRequest").ToHandleChecked()); - Handle<Object> is_running = Object::GetProperty( - isolate_, cmd_processor, "isRunning").ToHandleChecked(); + JSReceiver::GetProperty(isolate_, cmd_processor, "processDebugRequest") + .ToHandleChecked()); + Handle<Object> is_running = + JSReceiver::GetProperty(isolate_, cmd_processor, "isRunning") + .ToHandleChecked(); // Process requests from the debugger. do { @@ -2313,6 +2253,44 @@ void Debug::ProcessDebugMessages(bool debug_command_only) { OnDebugBreak(isolate_->factory()->undefined_value(), debug_command_only); } +#ifdef DEBUG +void Debug::PrintBreakLocation() { + if (!FLAG_print_break_location) return; + HandleScope scope(isolate_); + JavaScriptFrameIterator iterator(isolate_); + if (iterator.done()) return; + JavaScriptFrame* frame = iterator.frame(); + FrameSummary summary = GetFirstFrameSummary(frame); + int source_position = + summary.abstract_code()->SourcePosition(summary.code_offset()); + Handle<Object> script_obj(summary.function()->shared()->script(), isolate_); + PrintF("[debug] break in function '"); + summary.function()->PrintName(); + PrintF("'.\n"); + if (script_obj->IsScript()) { + Handle<Script> script = Handle<Script>::cast(script_obj); + Handle<String> source(String::cast(script->source())); + Script::InitLineEnds(script); + int line = Script::GetLineNumber(script, source_position); + int column = Script::GetColumnNumber(script, source_position); + Handle<FixedArray> line_ends(FixedArray::cast(script->line_ends())); + int line_start = + line == 0 ? 0 : Smi::cast(line_ends->get(line - 1))->value() + 1; + int line_end = Smi::cast(line_ends->get(line))->value(); + DisallowHeapAllocation no_gc; + String::FlatContent content = source->GetFlatContent(); + if (content.IsOneByte()) { + PrintF("[debug] %.*s\n", line_end - line_start, + content.ToOneByteVector().start() + line_start); + PrintF("[debug] "); + for (int i = 0; i < column; i++) PrintF(" "); + PrintF("^\n"); + } else { + PrintF("[debug] at line %d column %d\n", line, column); + } + } +} +#endif // DEBUG DebugScope::DebugScope(Debug* debug) : debug_(debug), @@ -2324,9 +2302,10 @@ DebugScope::DebugScope(Debug* debug) base::NoBarrier_Store(&debug_->thread_local_.current_debug_scope_, reinterpret_cast<base::AtomicWord>(this)); - // Store the previous break id and frame id. + // Store the previous break id, frame id and return value. break_id_ = debug_->break_id(); break_frame_id_ = debug_->break_frame_id(); + return_value_ = debug_->return_value(); // Create the new break info. If there is no JavaScript frames there is no // break frame id. @@ -2364,6 +2343,7 @@ DebugScope::~DebugScope() { // Restore to the previous break state. debug_->thread_local_.break_frame_id_ = break_frame_id_; debug_->thread_local_.break_id_ = break_id_; + debug_->thread_local_.return_value_ = return_value_; debug_->UpdateState(); } @@ -2448,8 +2428,9 @@ v8::Local<v8::String> MessageImpl::GetJSON() const { if (IsEvent()) { // Call toJSONProtocol on the debug event object. - Handle<Object> fun = Object::GetProperty( - isolate, event_data_, "toJSONProtocol").ToHandleChecked(); + Handle<Object> fun = + JSReceiver::GetProperty(isolate, event_data_, "toJSONProtocol") + .ToHandleChecked(); if (!fun->IsJSFunction()) { return v8::Local<v8::String>(); } |