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(); |