| Index: src/debug/debug.cc
|
| diff --git a/src/debug/debug.cc b/src/debug/debug.cc
|
| index 631a6fdbc58e29faac969b2d6a105fad01c0fb86..28906dbca1f9cdd8a5343ba3b260e9431f044101 100644
|
| --- a/src/debug/debug.cc
|
| +++ b/src/debug/debug.cc
|
| @@ -32,11 +32,29 @@ namespace internal {
|
|
|
| namespace {
|
|
|
| -inline int CallOffsetFromCodeOffset(int code_offset, bool is_interpreted) {
|
| +inline int GetCodeOffset(StandardFrame* frame) {
|
| + // Frame summary is top-down, i.e. caller before callee.
|
| + // For whatever reason, javascript takes the lowest caller as the code offset
|
| + // for the frame, where for wasm we need the highest one.
|
| + return frame->is_java_script() ? FrameSummary::GetFirst(frame).code_offset()
|
| + : FrameSummary::GetLast(frame).code_offset();
|
| +}
|
| +
|
| +inline int CallOffsetFromCodeOffset(int code_offset, StandardFrame* frame) {
|
| // Code offset points to the instruction after the call. Subtract 1 to
|
| - // exclude that instruction from the search. For bytecode, the code offset
|
| - // still points to the call.
|
| - return is_interpreted ? code_offset : code_offset - 1;
|
| + // exclude that instruction from the search. For interpreted frames, the code
|
| + // offset still points to the call.
|
| + bool interpreted = frame->is_interpreted() || frame->is_wasm_interpreted();
|
| + return interpreted ? code_offset : code_offset - 1;
|
| +}
|
| +
|
| +std::pair<Handle<JSObject>, int> GetWasmObjectAndFunctionIndexFromCode(
|
| + Code* code) {
|
| + FixedArray* deopt_data = code->deoptimization_data();
|
| + DCHECK_EQ(2, deopt_data->length());
|
| + Handle<JSObject> wasm(JSObject::cast(deopt_data->get(0)));
|
| + int func_index = Smi::cast(deopt_data->get(1))->value();
|
| + return std::make_pair(wasm, func_index);
|
| }
|
|
|
| } // namespace
|
| @@ -85,6 +103,9 @@ BreakLocation::Iterator* BreakLocation::GetIterator(
|
| Handle<DebugInfo> debug_info, BreakLocatorType type) {
|
| if (debug_info->abstract_code()->IsBytecodeArray()) {
|
| return new BytecodeArrayIterator(debug_info, type);
|
| + } else if (debug_info->abstract_code()->GetCode()->kind() ==
|
| + Code::WASM_FUNCTION) {
|
| + return new WasmBytecodeIterator(debug_info, type);
|
| } else {
|
| return new CodeIterator(debug_info, type);
|
| }
|
| @@ -264,6 +285,64 @@ BreakLocation BreakLocation::BytecodeArrayIterator::GetBreakLocation() {
|
| position(), statement_position());
|
| }
|
|
|
| +namespace {
|
| +
|
| +wasm::WasmInstructionIterator GetWasmInsnIterator(
|
| + Handle<DebugInfo> debug_info) {
|
| + auto wasm_and_index = GetWasmObjectAndFunctionIndexFromCode(
|
| + debug_info->abstract_code()->GetCode());
|
| + Handle<wasm::WasmDebugInfo> wasm_debug_info(
|
| + wasm::GetDebugInfo(wasm_and_index.first));
|
| + return wasm::WasmDebugInfo::GetInstructionIterator(wasm_debug_info,
|
| + wasm_and_index.second);
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +BreakLocation::WasmBytecodeIterator::WasmBytecodeIterator(
|
| + Handle<DebugInfo> debug_info, BreakLocatorType type)
|
| + : Iterator(debug_info),
|
| + wasm_insn_iterator(GetWasmInsnIterator(debug_info)),
|
| + break_locator_type_(type) {
|
| + // Get wasm object and function index from deopt data.
|
| + Code* code = debug_info->abstract_code()->GetCode();
|
| + Handle<JSObject> wasm;
|
| + std::tie(wasm, func_index) = GetWasmObjectAndFunctionIndexFromCode(code);
|
| + wasm_debug_info = handle(wasm::GetDebugInfo(wasm));
|
| + Next();
|
| +}
|
| +
|
| +BreakLocation BreakLocation::WasmBytecodeIterator::GetBreakLocation() {
|
| + DebugBreakType type;
|
| + switch (wasm_debug_info->GetInstructionType(func_index,
|
| + wasm_insn_iterator.pc_offset())) {
|
| + case wasm::WasmDebugInfo::InstructionType::RETURN:
|
| + type = DEBUG_BREAK_SLOT_AT_RETURN;
|
| + break;
|
| + case wasm::WasmDebugInfo::InstructionType::CALL:
|
| + type = DEBUG_BREAK_SLOT_AT_CALL;
|
| + break;
|
| + default:
|
| + type = DEBUG_BREAK_SLOT;
|
| + break;
|
| + }
|
| + return BreakLocation(debug_info_, type, code_offset(), position(),
|
| + statement_position());
|
| +}
|
| +
|
| +void BreakLocation::WasmBytecodeIterator::Next() {
|
| + ++break_index_;
|
| + for (bool first = break_index_ == 0; !Done(); first = false) {
|
| + if (!first) wasm_insn_iterator.Next();
|
| + position_ = statement_position_ = wasm_insn_iterator.pc_offset();
|
| + if (break_locator_type_ == ALL_BREAK_LOCATIONS) break;
|
| + DCHECK_EQ(BreakLocatorType::CALLS_AND_RETURNS, break_locator_type_);
|
| + using InsnType = wasm::WasmDebugInfo::InstructionType;
|
| + InsnType type = wasm_debug_info->GetInstructionType(func_index, position_);
|
| + if (type == InsnType::RETURN || type == InsnType::CALL) break;
|
| + }
|
| +}
|
| +
|
| // Find the break point at the supplied address, or the closest one before
|
| // the address.
|
| BreakLocation BreakLocation::FromCodeOffset(Handle<DebugInfo> debug_info,
|
| @@ -273,11 +352,12 @@ BreakLocation BreakLocation::FromCodeOffset(Handle<DebugInfo> debug_info,
|
| return it->GetBreakLocation();
|
| }
|
|
|
| -BreakLocation BreakLocation::FromFrame(Handle<DebugInfo> debug_info,
|
| - JavaScriptFrame* frame) {
|
| - int code_offset = FrameSummary::GetFirst(frame).code_offset();
|
| - int call_offset =
|
| - CallOffsetFromCodeOffset(code_offset, frame->is_interpreted());
|
| +BreakLocation BreakLocation::FromFrame(StandardFrame* frame) {
|
| + // Get the debug info (create it if it does not exist).
|
| + Handle<DebugInfo> debug_info(DebugInfo::cast(frame->debug_info(true)));
|
| +
|
| + int code_offset = GetCodeOffset(frame);
|
| + int call_offset = CallOffsetFromCodeOffset(code_offset, frame);
|
| return FromCodeOffset(debug_info, call_offset);
|
| }
|
|
|
| @@ -297,7 +377,7 @@ int BreakLocation::BreakIndexFromCodeOffset(Handle<DebugInfo> debug_info,
|
| // Run through all break points to locate the one closest to the address.
|
| int closest_break = 0;
|
| int distance = kMaxInt;
|
| - DCHECK(0 <= offset && offset < debug_info->abstract_code()->Size());
|
| + DCHECK_LE(0, offset);
|
| for (base::SmartPointer<Iterator> it(GetIterator(debug_info)); !it->Done();
|
| it->Next()) {
|
| // Check if this break point is closer that what was previously found.
|
| @@ -406,12 +486,21 @@ void BreakLocation::SetDebugBreak() {
|
| DCHECK(IsDebugBreakSlot());
|
| if (abstract_code()->IsCode()) {
|
| Code* code = abstract_code()->GetCode();
|
| - DCHECK(code->kind() == Code::FUNCTION);
|
| - Builtins* builtins = isolate()->builtins();
|
| - Handle<Code> target = IsReturn() ? builtins->Return_DebugBreak()
|
| - : builtins->Slot_DebugBreak();
|
| - Address pc = code->instruction_start() + code_offset();
|
| - DebugCodegen::PatchDebugBreakSlot(isolate(), pc, target);
|
| + if (code->kind() == Code::FUNCTION) {
|
| + Builtins* builtins = isolate()->builtins();
|
| + Handle<Code> target = IsReturn() ? builtins->Return_DebugBreak()
|
| + : builtins->Slot_DebugBreak();
|
| + Address pc = code->instruction_start() + code_offset();
|
| + DebugCodegen::PatchDebugBreakSlot(isolate(), pc, target);
|
| + } else {
|
| + DCHECK(code->kind() == Code::WASM_FUNCTION);
|
| + // Get wasm object and function index from deopt data.
|
| + Handle<JSObject> wasm;
|
| + int func_index;
|
| + std::tie(wasm, func_index) = GetWasmObjectAndFunctionIndexFromCode(code);
|
| + Handle<wasm::WasmDebugInfo> debug_info(wasm::GetDebugInfo(wasm));
|
| + wasm::WasmDebugInfo::SetBreakpoint(debug_info, func_index, position());
|
| + }
|
| } else {
|
| BytecodeArray* bytecode_array = abstract_code()->GetBytecodeArray();
|
| interpreter::Bytecode bytecode =
|
| @@ -449,9 +538,16 @@ bool BreakLocation::IsDebugBreak() const {
|
| DCHECK(IsDebugBreakSlot());
|
| if (abstract_code()->IsCode()) {
|
| Code* code = abstract_code()->GetCode();
|
| - DCHECK(code->kind() == Code::FUNCTION);
|
| - Address pc = code->instruction_start() + code_offset();
|
| - return DebugCodegen::DebugBreakSlotIsPatched(pc);
|
| + if (code->kind() == Code::FUNCTION) {
|
| + Address pc = code->instruction_start() + code_offset();
|
| + return DebugCodegen::DebugBreakSlotIsPatched(pc);
|
| + }
|
| + DCHECK(code->kind() == Code::WASM_FUNCTION);
|
| + Handle<JSObject> wasm;
|
| + int func_index;
|
| + std::tie(wasm, func_index) = GetWasmObjectAndFunctionIndexFromCode(code);
|
| + Handle<wasm::WasmDebugInfo> wasm_debug_info(wasm::GetDebugInfo(wasm));
|
| + return wasm_debug_info->HasBreakpoint(func_index, position());
|
| } else {
|
| BytecodeArray* bytecode_array = abstract_code()->GetBytecodeArray();
|
| interpreter::Bytecode bytecode =
|
| @@ -574,7 +670,7 @@ void Debug::Unload() {
|
| debug_context_ = Handle<Context>();
|
| }
|
|
|
| -void Debug::Break(JavaScriptFrame* frame) {
|
| +void Debug::Break(StandardFrame* frame) {
|
| HandleScope scope(isolate_);
|
|
|
| // Initialize LiveEdit.
|
| @@ -590,17 +686,8 @@ void Debug::Break(JavaScriptFrame* frame) {
|
| // Postpone interrupt during breakpoint processing.
|
| PostponeInterruptsScope postpone(isolate_);
|
|
|
| - // Get the debug info (create it if it does not exist).
|
| - Handle<JSFunction> function(frame->function());
|
| - Handle<SharedFunctionInfo> shared(function->shared());
|
| - if (!EnsureDebugInfo(shared, function)) {
|
| - // Return if we failed to retrieve the debug info.
|
| - return;
|
| - }
|
| - Handle<DebugInfo> debug_info(shared->GetDebugInfo(), isolate_);
|
| -
|
| // Find the break location where execution has stopped.
|
| - BreakLocation location = BreakLocation::FromFrame(debug_info, frame);
|
| + BreakLocation location = BreakLocation::FromFrame(frame);
|
|
|
| // Find actual break points, if any, and trigger debug break event.
|
| Handle<Object> break_points_hit = CheckBreakPoints(&location);
|
| @@ -634,8 +721,7 @@ void Debug::Break(JavaScriptFrame* frame) {
|
| step_break = location.IsTailCall();
|
| // Fall through.
|
| case StepIn: {
|
| - FrameSummary summary = FrameSummary::GetFirst(frame);
|
| - int offset = summary.code_offset();
|
| + int offset = GetCodeOffset(frame);
|
| step_break = step_break || location.IsReturn() ||
|
| (current_fp != last_fp) ||
|
| (thread_local_.last_statement_position_ !=
|
| @@ -713,7 +799,7 @@ bool Debug::IsMutedAtCurrentLocation(JavaScriptFrame* frame) {
|
| // Enter the debugger.
|
| DebugScope debug_scope(this);
|
| if (debug_scope.failed()) return false;
|
| - BreakLocation current_position = BreakLocation::FromFrame(debug_info, frame);
|
| + BreakLocation current_position = BreakLocation::FromFrame(frame);
|
| List<BreakLocation> break_locations;
|
| BreakLocation::AllForStatementPosition(
|
| debug_info, current_position.statement_position(), &break_locations);
|
| @@ -799,36 +885,40 @@ bool Debug::SetBreakPointForScript(Handle<Script> script,
|
| Handle<Object> break_point_object,
|
| int* source_position,
|
| BreakPositionAlignment alignment) {
|
| - if (script->type() == Script::TYPE_WASM) {
|
| - // TODO(clemensh): set breakpoint for wasm.
|
| - return false;
|
| - }
|
| HandleScope scope(isolate_);
|
|
|
| - // Obtain shared function info for the function.
|
| - Handle<Object> result =
|
| - FindSharedFunctionInfoInScript(script, *source_position);
|
| - if (result->IsUndefined(isolate_)) return false;
|
| -
|
| - // Make sure the function has set up the debug info.
|
| - Handle<SharedFunctionInfo> shared = Handle<SharedFunctionInfo>::cast(result);
|
| - if (!EnsureDebugInfo(shared, Handle<JSFunction>::null())) {
|
| - // Return if retrieving debug info failed.
|
| - return false;
|
| + Handle<DebugInfo> debug_info;
|
| + int start_position = 0;
|
| + if (script->type() == Script::TYPE_WASM) {
|
| + Handle<wasm::WasmDebugInfo> wasm_debug(
|
| + wasm::GetDebugInfo(handle(script->wasm_object(), isolate_)), isolate_);
|
| + uint32_t func_index = script->wasm_function_index();
|
| + debug_info = handle(
|
| + wasm::WasmDebugInfo::GetDebugInfo(wasm_debug, func_index), isolate_);
|
| + } else {
|
| + // Obtain shared function info for the function.
|
| + Handle<Object> result =
|
| + FindSharedFunctionInfoInScript(script, *source_position);
|
| + if (result->IsUndefined(isolate_)) return false;
|
| +
|
| + // Get debug info, create it if necessary.
|
| + Handle<SharedFunctionInfo> shared =
|
| + Handle<SharedFunctionInfo>::cast(result);
|
| + if (!EnsureDebugInfo(shared, Handle<JSFunction>::null())) {
|
| + // Return if retrieving debug info failed.
|
| + return false;
|
| + }
|
| + debug_info = handle(shared->GetDebugInfo(), isolate_);
|
| + start_position = shared->start_position();
|
| }
|
|
|
| // Find position within function. The script position might be before the
|
| // source position of the first function.
|
| - int position;
|
| - if (shared->start_position() > *source_position) {
|
| - position = 0;
|
| - } else {
|
| - position = *source_position - shared->start_position();
|
| - }
|
| + int position =
|
| + start_position > *source_position ? 0 : *source_position - start_position;
|
|
|
| - Handle<DebugInfo> debug_info(shared->GetDebugInfo());
|
| // Source positions starts with zero.
|
| - DCHECK(position >= 0);
|
| + DCHECK_LE(0, position);
|
|
|
| // Find the break point and change it.
|
| BreakLocation location =
|
| @@ -840,7 +930,7 @@ bool Debug::SetBreakPointForScript(Handle<Script> script,
|
| position = (alignment == STATEMENT_ALIGNED) ? location.statement_position()
|
| : location.position();
|
|
|
| - *source_position = position + shared->start_position();
|
| + *source_position = position + start_position;
|
|
|
| // At least one active break point now.
|
| DCHECK(debug_info->GetBreakPointCount() > 0);
|
| @@ -1045,8 +1135,7 @@ void Debug::PrepareStep(StepAction step_action) {
|
| summary = FrameSummary::GetFirst(frame);
|
| }
|
|
|
| - int call_offset =
|
| - CallOffsetFromCodeOffset(summary.code_offset(), frame->is_interpreted());
|
| + int call_offset = CallOffsetFromCodeOffset(summary.code_offset(), frame);
|
| BreakLocation location =
|
| BreakLocation::FromCodeOffset(debug_info, call_offset);
|
|
|
| @@ -1603,7 +1692,7 @@ void Debug::RemoveDebugInfoAndClearFromShared(Handle<DebugInfo> debug_info) {
|
| UNREACHABLE();
|
| }
|
|
|
| -void Debug::SetAfterBreakTarget(JavaScriptFrame* frame) {
|
| +void Debug::SetAfterBreakTarget(StandardFrame* frame) {
|
| after_break_target_ = NULL;
|
| if (!LiveEdit::SetAfterBreakTarget(this)) {
|
| // Continue just after the slot.
|
| @@ -2315,7 +2404,6 @@ DebugScope::DebugScope(Debug* debug)
|
| StackTraceFrameIterator it(isolate());
|
| bool has_frames = !it.done();
|
| // We don't currently support breaking inside wasm framess.
|
| - DCHECK(!has_frames || !it.is_wasm());
|
| debug_->thread_local_.break_frame_id_ =
|
| has_frames ? it.frame()->id() : StackFrame::NO_ID;
|
| debug_->SetNextBreakId();
|
|
|