Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nodejs/node.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'deps/v8/src/wasm/wasm-debug.cc')
-rw-r--r--deps/v8/src/wasm/wasm-debug.cc935
1 files changed, 277 insertions, 658 deletions
diff --git a/deps/v8/src/wasm/wasm-debug.cc b/deps/v8/src/wasm/wasm-debug.cc
index 10a2e194a7e..a8fd6505f0e 100644
--- a/deps/v8/src/wasm/wasm-debug.cc
+++ b/deps/v8/src/wasm/wasm-debug.cc
@@ -24,6 +24,7 @@
#include "src/wasm/wasm-limits.h"
#include "src/wasm/wasm-module.h"
#include "src/wasm/wasm-objects-inl.h"
+#include "src/wasm/wasm-value.h"
#include "src/zone/accounting-allocator.h"
namespace v8 {
@@ -90,8 +91,6 @@ class InterpreterHandle {
Isolate* isolate_;
const WasmModule* module_;
WasmInterpreter interpreter_;
- StepAction next_step_action_ = StepNone;
- int last_step_stack_depth_ = 0;
std::unordered_map<Address, uint32_t> activations_;
uint32_t StartActivation(Address frame_pointer) {
@@ -145,18 +144,6 @@ class InterpreterHandle {
WasmInterpreter* interpreter() { return &interpreter_; }
const WasmModule* module() const { return module_; }
- void PrepareStep(StepAction step_action) {
- next_step_action_ = step_action;
- last_step_stack_depth_ = CurrentStackDepth();
- }
-
- void ClearStepping() { next_step_action_ = StepNone; }
-
- int CurrentStackDepth() {
- DCHECK_EQ(1, interpreter()->GetThreadCount());
- return interpreter()->GetThread(0)->GetFrameCount();
- }
-
// Returns true if exited regularly, false if a trap/exception occurred and
// was not handled inside this activation. In the latter case, a pending
// exception will have been set on the isolate.
@@ -178,11 +165,10 @@ class InterpreterHandle {
bool finished = false;
while (!finished) {
// TODO(clemensb): Add occasional StackChecks.
- WasmInterpreter::State state = ContinueExecution(thread);
+ WasmInterpreter::State state = thread->Run();
switch (state) {
case WasmInterpreter::State::PAUSED:
- NotifyDebugEventListeners(thread);
- break;
+ UNREACHABLE();
case WasmInterpreter::State::FINISHED:
// Perfect, just break the switch and exit the loop.
finished = true;
@@ -230,109 +216,9 @@ class InterpreterHandle {
FinishActivation(frame_pointer, activation_id);
- // If we do stepping and it exits wasm interpreter then debugger need to
- // prepare for it.
- if (next_step_action_ != StepNone) {
- // Enter the debugger.
- DebugScope debug_scope(isolate_->debug());
-
- isolate_->debug()->PrepareStep(StepOut);
- }
- ClearStepping();
-
return true;
}
- WasmInterpreter::State ContinueExecution(WasmInterpreter::Thread* thread) {
- switch (next_step_action_) {
- case StepNone:
- return thread->Run();
- case StepIn:
- return thread->Step();
- case StepOut:
- thread->AddBreakFlags(WasmInterpreter::BreakFlag::AfterReturn);
- return thread->Run();
- case StepNext: {
- int stack_depth = thread->GetFrameCount();
- if (stack_depth == last_step_stack_depth_) return thread->Step();
- thread->AddBreakFlags(stack_depth > last_step_stack_depth_
- ? WasmInterpreter::BreakFlag::AfterReturn
- : WasmInterpreter::BreakFlag::AfterCall);
- return thread->Run();
- }
- default:
- UNREACHABLE();
- }
- }
-
- Handle<WasmInstanceObject> GetInstanceObject() {
- StackTraceFrameIterator it(isolate_);
- WasmInterpreterEntryFrame* frame =
- WasmInterpreterEntryFrame::cast(it.frame());
- Handle<WasmInstanceObject> instance_obj(frame->wasm_instance(), isolate_);
- // Check that this is indeed the instance which is connected to this
- // interpreter.
- DCHECK_EQ(this, Managed<InterpreterHandle>::cast(
- instance_obj->debug_info().interpreter_handle())
- .raw());
- return instance_obj;
- }
-
- void NotifyDebugEventListeners(WasmInterpreter::Thread* thread) {
- // Enter the debugger.
- DebugScope debug_scope(isolate_->debug());
-
- // Check whether we hit a breakpoint.
- if (isolate_->debug()->break_points_active()) {
- Handle<WasmModuleObject> module_object(
- GetInstanceObject()->module_object(), isolate_);
- Handle<Script> script(module_object->script(), isolate_);
- int position = GetTopPosition(module_object);
- Handle<FixedArray> breakpoints;
- if (WasmScript::CheckBreakPoints(isolate_, script, position)
- .ToHandle(&breakpoints)) {
- // We hit one or several breakpoints. Clear stepping, notify the
- // listeners and return.
- ClearStepping();
- isolate_->debug()->OnDebugBreak(breakpoints);
- return;
- }
- }
-
- // We did not hit a breakpoint, so maybe this pause is related to stepping.
- bool hit_step = false;
- switch (next_step_action_) {
- case StepNone:
- break;
- case StepIn:
- hit_step = true;
- break;
- case StepOut:
- hit_step = thread->GetFrameCount() < last_step_stack_depth_;
- break;
- case StepNext: {
- hit_step = thread->GetFrameCount() == last_step_stack_depth_;
- break;
- }
- default:
- UNREACHABLE();
- }
- if (!hit_step) return;
- ClearStepping();
- isolate_->debug()->OnDebugBreak(isolate_->factory()->empty_fixed_array());
- }
-
- int GetTopPosition(Handle<WasmModuleObject> module_object) {
- DCHECK_EQ(1, interpreter()->GetThreadCount());
- WasmInterpreter::Thread* thread = interpreter()->GetThread(0);
- DCHECK_LT(0, thread->GetFrameCount());
-
- auto frame = thread->GetFrame(thread->GetFrameCount() - 1);
- return GetWasmFunctionOffset(module_object->module(),
- frame->function()->func_index) +
- frame->pc();
- }
-
std::vector<std::pair<uint32_t, int>> GetInterpretedStack(
Address frame_pointer) {
DCHECK_EQ(1, interpreter()->GetThreadCount());
@@ -362,116 +248,10 @@ class InterpreterHandle {
return frame_range.second - frame_range.first;
}
- WasmInterpreter::FramePtr GetInterpretedFrame(Address frame_pointer,
- int idx) {
- DCHECK_EQ(1, interpreter()->GetThreadCount());
- WasmInterpreter::Thread* thread = interpreter()->GetThread(0);
-
- std::pair<uint32_t, uint32_t> frame_range =
- GetActivationFrameRange(thread, frame_pointer);
- DCHECK_LE(0, idx);
- DCHECK_GT(frame_range.second - frame_range.first, idx);
-
- return thread->GetFrame(frame_range.first + idx);
- }
-
- uint64_t NumInterpretedCalls() {
- DCHECK_EQ(1, interpreter()->GetThreadCount());
- return interpreter()->GetThread(0)->NumInterpretedCalls();
- }
-
- Handle<JSObject> GetLocalScopeObject(InterpretedFrame* frame,
- Handle<WasmDebugInfo> debug_info) {
- Isolate* isolate = isolate_;
-
- Handle<JSObject> local_scope_object =
- isolate_->factory()->NewJSObjectWithNullProto();
- // Fill parameters and locals.
- int num_locals = frame->GetLocalCount();
- DCHECK_LE(frame->GetParameterCount(), num_locals);
- if (num_locals > 0) {
- Handle<JSObject> locals_obj =
- isolate_->factory()->NewJSObjectWithNullProto();
- Handle<String> locals_name =
- isolate_->factory()->InternalizeString(StaticCharVector("locals"));
- JSObject::AddProperty(isolate, local_scope_object, locals_name,
- locals_obj, NONE);
- NativeModule* native_module =
- debug_info->wasm_instance().module_object().native_module();
- for (int i = 0; i < num_locals; ++i) {
- Handle<Name> name;
- if (!GetLocalNameString(isolate, native_module,
- frame->function()->func_index, i)
- .ToHandle(&name)) {
- name = PrintFToOneByteString<true>(isolate_, "var%d", i);
- }
- WasmValue value = frame->GetLocalValue(i);
- Handle<Object> value_obj = WasmValueToValueObject(isolate_, value);
- // {name} can be a string representation of an element index.
- LookupIterator::Key lookup_key{isolate, name};
- LookupIterator it(isolate, locals_obj, lookup_key, locals_obj,
- LookupIterator::OWN_SKIP_INTERCEPTOR);
- if (it.IsFound()) continue;
- Object::AddDataProperty(&it, value_obj, NONE,
- Just(ShouldThrow::kThrowOnError),
- StoreOrigin::kNamed)
- .Check();
- }
- }
-
- // Fill stack values.
- int stack_count = frame->GetStackHeight();
- // Use an object without prototype instead of an Array, for nicer displaying
- // in DevTools. For Arrays, the length field and prototype is displayed,
- // which does not make too much sense here.
- Handle<JSObject> stack_obj =
- isolate_->factory()->NewJSObjectWithNullProto();
- Handle<String> stack_name =
- isolate_->factory()->InternalizeString(StaticCharVector("stack"));
- JSObject::AddProperty(isolate, local_scope_object, stack_name, stack_obj,
- NONE);
- for (int i = 0; i < stack_count; ++i) {
- WasmValue value = frame->GetStackValue(i);
- Handle<Object> value_obj = WasmValueToValueObject(isolate_, value);
- JSObject::AddDataElement(stack_obj, static_cast<uint32_t>(i), value_obj,
- NONE);
- }
- return local_scope_object;
- }
-
- Handle<JSObject> GetStackScopeObject(InterpretedFrame* frame,
- Handle<WasmDebugInfo> debug_info) {
- // Fill stack values.
- int stack_count = frame->GetStackHeight();
- // Use an object without prototype instead of an Array, for nicer displaying
- // in DevTools. For Arrays, the length field and prototype is displayed,
- // which does not make too much sense here.
- Handle<JSObject> stack_scope_obj =
- isolate_->factory()->NewJSObjectWithNullProto();
- for (int i = 0; i < stack_count; ++i) {
- WasmValue value = frame->GetStackValue(i);
- Handle<Object> value_obj = WasmValueToValueObject(isolate_, value);
- JSObject::AddDataElement(stack_scope_obj, static_cast<uint32_t>(i),
- value_obj, NONE);
- }
- return stack_scope_obj;
- }
-
private:
DISALLOW_COPY_AND_ASSIGN(InterpreterHandle);
};
-int FindByteOffset(int pc_offset, WasmCode* wasm_code) {
- int position = 0;
- SourcePositionTableIterator iterator(wasm_code->source_positions());
- for (SourcePositionTableIterator iterator(wasm_code->source_positions());
- !iterator.done() && iterator.code_offset() < pc_offset;
- iterator.Advance()) {
- position = iterator.source_position().ScriptOffset();
- }
- return position;
-}
-
// Generate a sorted and deduplicated list of byte offsets for this function's
// current positions on the stack.
std::vector<int> StackFramePositions(int func_index, Isolate* isolate) {
@@ -479,14 +259,11 @@ std::vector<int> StackFramePositions(int func_index, Isolate* isolate) {
WasmCodeRefScope code_ref_scope;
for (StackTraceFrameIterator it(isolate); !it.done(); it.Advance()) {
if (!it.is_wasm()) continue;
- WasmCompiledFrame* frame = WasmCompiledFrame::cast(it.frame());
+ WasmFrame* frame = WasmFrame::cast(it.frame());
if (static_cast<int>(frame->function_index()) != func_index) continue;
WasmCode* wasm_code = frame->wasm_code();
if (!wasm_code->is_liftoff()) continue;
- int pc_offset =
- static_cast<int>(frame->pc() - wasm_code->instruction_start());
- int byte_offset = FindByteOffset(pc_offset, wasm_code);
- byte_offsets.push_back(byte_offset);
+ byte_offsets.push_back(frame->byte_offset());
}
std::sort(byte_offsets.begin(), byte_offsets.end());
auto last = std::unique(byte_offsets.begin(), byte_offsets.end());
@@ -527,19 +304,26 @@ Address FindNewPC(WasmCode* wasm_code, int byte_offset,
} // namespace
-Handle<JSObject> GetGlobalScopeObject(Handle<WasmInstanceObject> instance) {
+Handle<JSObject> GetModuleScopeObject(Handle<WasmInstanceObject> instance) {
Isolate* isolate = instance->GetIsolate();
- Handle<JSObject> global_scope_object =
+ Handle<JSObject> module_scope_object =
isolate->factory()->NewJSObjectWithNullProto();
if (instance->has_memory_object()) {
- Handle<String> name =
- isolate->factory()->InternalizeString(StaticCharVector("memory"));
+ Handle<String> name;
+ // TODO(duongn): extend the logic when multiple memories are supported.
+ const uint32_t memory_index = 0;
+ if (!WasmInstanceObject::GetMemoryNameOrNull(isolate, instance,
+ memory_index)
+ .ToHandle(&name)) {
+ const char* label = "memory%d";
+ name = PrintFToOneByteString<true>(isolate, label, memory_index);
+ }
Handle<JSArrayBuffer> memory_buffer(
instance->memory_object().array_buffer(), isolate);
Handle<JSTypedArray> uint8_array = isolate->factory()->NewJSTypedArray(
kExternalUint8Array, memory_buffer, 0, memory_buffer->byte_length());
- JSObject::AddProperty(isolate, global_scope_object, name, uint8_array,
+ JSObject::AddProperty(isolate, module_scope_object, name, uint8_array,
NONE);
}
@@ -549,7 +333,7 @@ Handle<JSObject> GetGlobalScopeObject(Handle<WasmInstanceObject> instance) {
isolate->factory()->NewJSObjectWithNullProto();
Handle<String> globals_name =
isolate->factory()->InternalizeString(StaticCharVector("globals"));
- JSObject::AddProperty(isolate, global_scope_object, globals_name,
+ JSObject::AddProperty(isolate, module_scope_object, globals_name,
globals_obj, NONE);
for (uint32_t i = 0; i < globals.size(); ++i) {
@@ -565,8 +349,7 @@ Handle<JSObject> GetGlobalScopeObject(Handle<WasmInstanceObject> instance) {
JSObject::AddProperty(isolate, globals_obj, name, value_obj, NONE);
}
}
-
- return global_scope_object;
+ return module_scope_object;
}
class DebugInfoImpl {
@@ -574,26 +357,49 @@ class DebugInfoImpl {
explicit DebugInfoImpl(NativeModule* native_module)
: native_module_(native_module) {}
+ int GetNumLocals(Isolate* isolate, Address pc) {
+ FrameInspectionScope scope(this, isolate, pc);
+ if (!scope.is_inspectable()) return 0;
+ return scope.debug_side_table->num_locals();
+ }
+
+ WasmValue GetLocalValue(int local, Isolate* isolate, Address pc, Address fp,
+ Address debug_break_fp) {
+ FrameInspectionScope scope(this, isolate, pc);
+ return GetValue(scope.debug_side_table_entry, local, fp, debug_break_fp);
+ }
+
+ int GetStackDepth(Isolate* isolate, Address pc) {
+ FrameInspectionScope scope(this, isolate, pc);
+ if (!scope.is_inspectable()) return 0;
+ int num_locals = static_cast<int>(scope.debug_side_table->num_locals());
+ int value_count = scope.debug_side_table_entry->num_values();
+ return value_count - num_locals;
+ }
+
+ WasmValue GetStackValue(int index, Isolate* isolate, Address pc, Address fp,
+ Address debug_break_fp) {
+ FrameInspectionScope scope(this, isolate, pc);
+ int num_locals = static_cast<int>(scope.debug_side_table->num_locals());
+ int value_count = scope.debug_side_table_entry->num_values();
+ if (num_locals + index >= value_count) return {};
+ return GetValue(scope.debug_side_table_entry, num_locals + index, fp,
+ debug_break_fp);
+ }
+
Handle<JSObject> GetLocalScopeObject(Isolate* isolate, Address pc, Address fp,
Address debug_break_fp) {
+ FrameInspectionScope scope(this, isolate, pc);
Handle<JSObject> local_scope_object =
isolate->factory()->NewJSObjectWithNullProto();
- wasm::WasmCodeRefScope wasm_code_ref_scope;
- wasm::WasmCode* code =
- isolate->wasm_engine()->code_manager()->LookupCode(pc);
- // Only Liftoff code can be inspected.
- if (!code->is_liftoff()) return local_scope_object;
+ if (!scope.is_inspectable()) return local_scope_object;
auto* module = native_module_->module();
- auto* function = &module->functions[code->index()];
- auto* debug_side_table = GetDebugSideTable(code, isolate->allocator());
- int pc_offset = static_cast<int>(pc - code->instruction_start());
- auto* debug_side_table_entry = debug_side_table->GetEntry(pc_offset);
- DCHECK_NOT_NULL(debug_side_table_entry);
+ auto* function = &module->functions[scope.code->index()];
// Fill parameters and locals.
- int num_locals = static_cast<int>(debug_side_table->num_locals());
+ int num_locals = static_cast<int>(scope.debug_side_table->num_locals());
DCHECK_LE(static_cast<int>(function->sig->parameter_count()), num_locals);
if (num_locals > 0) {
Handle<JSObject> locals_obj =
@@ -610,7 +416,7 @@ class DebugInfoImpl {
name = PrintFToOneByteString<true>(isolate, "var%d", i);
}
WasmValue value =
- GetValue(debug_side_table_entry, i, fp, debug_break_fp);
+ GetValue(scope.debug_side_table_entry, i, fp, debug_break_fp);
Handle<Object> value_obj = WasmValueToValueObject(isolate, value);
// {name} can be a string representation of an element index.
LookupIterator::Key lookup_key{isolate, name};
@@ -623,50 +429,26 @@ class DebugInfoImpl {
.Check();
}
}
-
- // Fill stack values.
- // Use an object without prototype instead of an Array, for nicer displaying
- // in DevTools. For Arrays, the length field and prototype is displayed,
- // which does not make too much sense here.
- Handle<JSObject> stack_obj = isolate->factory()->NewJSObjectWithNullProto();
- Handle<String> stack_name =
- isolate->factory()->InternalizeString(StaticCharVector("stack"));
- JSObject::AddProperty(isolate, local_scope_object, stack_name, stack_obj,
- NONE);
- int value_count = debug_side_table_entry->num_values();
- for (int i = num_locals; i < value_count; ++i) {
- WasmValue value = GetValue(debug_side_table_entry, i, fp, debug_break_fp);
- Handle<Object> value_obj = WasmValueToValueObject(isolate, value);
- JSObject::AddDataElement(stack_obj, static_cast<uint32_t>(i - num_locals),
- value_obj, NONE);
- }
return local_scope_object;
}
Handle<JSObject> GetStackScopeObject(Isolate* isolate, Address pc, Address fp,
Address debug_break_fp) {
+ FrameInspectionScope scope(this, isolate, pc);
Handle<JSObject> stack_scope_obj =
isolate->factory()->NewJSObjectWithNullProto();
- wasm::WasmCodeRefScope wasm_code_ref_scope;
-
- wasm::WasmCode* code =
- isolate->wasm_engine()->code_manager()->LookupCode(pc);
- // Only Liftoff code can be inspected.
- if (!code->is_liftoff()) return stack_scope_obj;
- auto* debug_side_table = GetDebugSideTable(code, isolate->allocator());
- int pc_offset = static_cast<int>(pc - code->instruction_start());
- auto* debug_side_table_entry = debug_side_table->GetEntry(pc_offset);
- DCHECK_NOT_NULL(debug_side_table_entry);
+ if (!scope.is_inspectable()) return stack_scope_obj;
// Fill stack values.
// Use an object without prototype instead of an Array, for nicer displaying
// in DevTools. For Arrays, the length field and prototype is displayed,
// which does not make too much sense here.
- int num_locals = static_cast<int>(debug_side_table->num_locals());
- int value_count = debug_side_table_entry->num_values();
+ int num_locals = static_cast<int>(scope.debug_side_table->num_locals());
+ int value_count = scope.debug_side_table_entry->num_values();
for (int i = num_locals; i < value_count; ++i) {
- WasmValue value = GetValue(debug_side_table_entry, i, fp, debug_break_fp);
+ WasmValue value =
+ GetValue(scope.debug_side_table_entry, i, fp, debug_break_fp);
Handle<Object> value_obj = WasmValueToValueObject(isolate, value);
JSObject::AddDataElement(stack_scope_obj,
static_cast<uint32_t>(i - num_locals), value_obj,
@@ -684,13 +466,12 @@ class DebugInfoImpl {
return local_names_->GetName(func_index, local_index);
}
- void RecompileLiftoffWithBreakpoints(int func_index, Vector<int> offsets,
- Isolate* current_isolate) {
- if (func_index == flooded_function_index_) {
- // We should not be flooding a function that is already flooded.
- DCHECK(!(offsets.size() == 1 && offsets[0] == 0));
- flooded_function_index_ = -1;
- }
+ WasmCode* RecompileLiftoffWithBreakpoints(
+ int func_index, Vector<int> offsets, Vector<int> extra_source_positions) {
+ // During compilation, we cannot hold the lock, since compilation takes the
+ // {NativeModule} lock, which could lead to deadlocks.
+ mutex_.AssertUnheld();
+
// Recompile the function with Liftoff, setting the new breakpoints.
// Not thread-safe. The caller is responsible for locking {mutex_}.
CompilationEnv env = native_module_->CreateCompilationEnv();
@@ -701,94 +482,110 @@ class DebugInfoImpl {
wire_bytes.begin() + function->code.end_offset()};
std::unique_ptr<DebugSideTable> debug_sidetable;
- // Generate additional source positions for current stack frame positions.
- // These source positions are used to find return addresses in the new code.
- std::vector<int> stack_frame_positions =
- StackFramePositions(func_index, current_isolate);
-
+ ForDebugging for_debugging =
+ offsets.size() == 1 && offsets[0] == 0 ? kForStepping : kForDebugging;
WasmCompilationResult result = ExecuteLiftoffCompilation(
- native_module_->engine()->allocator(), &env, body, func_index, nullptr,
- nullptr, offsets, &debug_sidetable, VectorOf(stack_frame_positions));
+ native_module_->engine()->allocator(), &env, body, func_index,
+ for_debugging, nullptr, nullptr, offsets, &debug_sidetable,
+ extra_source_positions);
// Liftoff compilation failure is a FATAL error. We rely on complete Liftoff
// support for debugging.
if (!result.succeeded()) FATAL("Liftoff compilation failed");
DCHECK_NOT_NULL(debug_sidetable);
- WasmCodeRefScope wasm_code_ref_scope;
- WasmCode* new_code = native_module_->AddCompiledCode(std::move(result));
+ WasmCode* new_code = native_module_->PublishCode(
+ native_module_->AddCompiledCode(std::move(result)));
+
+ DCHECK(new_code->is_inspectable());
bool added =
debug_side_tables_.emplace(new_code, std::move(debug_sidetable)).second;
DCHECK(added);
USE(added);
- UpdateReturnAddresses(current_isolate, new_code);
+ return new_code;
}
void SetBreakpoint(int func_index, int offset, Isolate* current_isolate) {
- // Hold the mutex while setting the breakpoint. This guards against multiple
- // isolates setting breakpoints at the same time. We don't really support
- // that scenario yet, but concurrently compiling and installing different
- // Liftoff variants of a function would be problematic.
- base::MutexGuard guard(&mutex_);
-
- // offset == 0 indicates flooding and should not happen here.
- DCHECK_NE(0, offset);
-
- std::vector<int>& breakpoints = breakpoints_per_function_[func_index];
- auto insertion_point =
- std::lower_bound(breakpoints.begin(), breakpoints.end(), offset);
- if (insertion_point != breakpoints.end() && *insertion_point == offset) {
- // The breakpoint is already set.
- return;
+ std::vector<int> breakpoints_copy;
+ {
+ // Hold the mutex while modifying the set of breakpoints, but release it
+ // before compiling the new code (see comment in
+ // {RecompileLiftoffWithBreakpoints}). This needs to be revisited once we
+ // support setting different breakpoints in different isolates
+ // (https://crbug.com/v8/10351).
+ base::MutexGuard guard(&mutex_);
+
+ // offset == 0 indicates flooding and should not happen here.
+ DCHECK_NE(0, offset);
+
+ std::vector<int>& breakpoints = breakpoints_per_function_[func_index];
+ auto insertion_point =
+ std::lower_bound(breakpoints.begin(), breakpoints.end(), offset);
+ if (insertion_point != breakpoints.end() && *insertion_point == offset) {
+ // The breakpoint is already set.
+ return;
+ }
+ breakpoints.insert(insertion_point, offset);
+ breakpoints_copy = breakpoints;
}
- breakpoints.insert(insertion_point, offset);
- // No need to recompile if the function is already flooded.
- if (func_index == flooded_function_index_) return;
+ UpdateBreakpoints(func_index, VectorOf(breakpoints_copy), current_isolate);
+ }
+
+ void UpdateBreakpoints(int func_index, Vector<int> breakpoints,
+ Isolate* current_isolate) {
+ // Generate additional source positions for current stack frame positions.
+ // These source positions are used to find return addresses in the new code.
+ std::vector<int> stack_frame_positions =
+ StackFramePositions(func_index, current_isolate);
- RecompileLiftoffWithBreakpoints(func_index, VectorOf(breakpoints),
- current_isolate);
+ WasmCodeRefScope wasm_code_ref_scope;
+ WasmCode* new_code = RecompileLiftoffWithBreakpoints(
+ func_index, breakpoints, VectorOf(stack_frame_positions));
+ UpdateReturnAddresses(current_isolate, new_code);
}
- void FloodWithBreakpoints(int func_index, Isolate* current_isolate) {
- base::MutexGuard guard(&mutex_);
+ void FloodWithBreakpoints(WasmFrame* frame, Isolate* current_isolate,
+ ReturnLocation return_location) {
// 0 is an invalid offset used to indicate flooding.
int offset = 0;
- RecompileLiftoffWithBreakpoints(func_index, Vector<int>(&offset, 1),
- current_isolate);
+ WasmCodeRefScope wasm_code_ref_scope;
+ DCHECK(frame->wasm_code()->is_liftoff());
+ // Generate an additional source position for the current byte offset.
+ int byte_offset = frame->byte_offset();
+ WasmCode* new_code = RecompileLiftoffWithBreakpoints(
+ frame->function_index(), VectorOf(&offset, 1),
+ VectorOf(&byte_offset, 1));
+ UpdateReturnAddress(frame, new_code, return_location);
}
void PrepareStep(Isolate* isolate, StackFrameId break_frame_id) {
StackTraceFrameIterator it(isolate, break_frame_id);
DCHECK(!it.done());
- DCHECK(it.frame()->is_wasm_compiled());
- WasmCompiledFrame* frame = WasmCompiledFrame::cast(it.frame());
+ DCHECK(it.frame()->is_wasm());
+ WasmFrame* frame = WasmFrame::cast(it.frame());
StepAction step_action = isolate->debug()->last_step_action();
+ // If we are flooding the top frame, the return location is after a
+ // breakpoints. Otherwise, it's after a call.
+ ReturnLocation return_location = kAfterBreakpoint;
+
// If we are at a return instruction, then any stepping action is equivalent
// to StepOut, and we need to flood the parent function.
if (IsAtReturn(frame) || step_action == StepOut) {
it.Advance();
- if (it.done() || !it.frame()->is_wasm_compiled()) return;
- frame = WasmCompiledFrame::cast(it.frame());
+ if (it.done() || !it.frame()->is_wasm()) return;
+ frame = WasmFrame::cast(it.frame());
+ return_location = kAfterWasmCall;
}
- if (static_cast<int>(frame->function_index()) != flooded_function_index_) {
- if (flooded_function_index_ != -1) {
- std::vector<int>& breakpoints =
- breakpoints_per_function_[flooded_function_index_];
- RecompileLiftoffWithBreakpoints(flooded_function_index_,
- VectorOf(breakpoints), isolate);
- }
- FloodWithBreakpoints(frame->function_index(), isolate);
- flooded_function_index_ = frame->function_index();
- }
+ FloodWithBreakpoints(frame, isolate, return_location);
stepping_frame_ = frame->id();
}
void ClearStepping() { stepping_frame_ = NO_ID; }
- bool IsStepping(WasmCompiledFrame* frame) {
+ bool IsStepping(WasmFrame* frame) {
Isolate* isolate = frame->wasm_instance().GetIsolate();
StepAction last_step_action = isolate->debug()->last_step_action();
return stepping_frame_ == frame->id() || last_step_action == StepIn;
@@ -796,19 +593,23 @@ class DebugInfoImpl {
void RemoveBreakpoint(int func_index, int position,
Isolate* current_isolate) {
- base::MutexGuard guard(&mutex_);
- const auto& function = native_module_->module()->functions[func_index];
- int offset = position - function.code.offset();
-
- std::vector<int>& breakpoints = breakpoints_per_function_[func_index];
- DCHECK_LT(0, offset);
- auto insertion_point =
- std::lower_bound(breakpoints.begin(), breakpoints.end(), offset);
- if (insertion_point != breakpoints.end() && *insertion_point == offset) {
+ std::vector<int> breakpoints_copy;
+ {
+ base::MutexGuard guard(&mutex_);
+ const auto& function = native_module_->module()->functions[func_index];
+ int offset = position - function.code.offset();
+
+ std::vector<int>& breakpoints = breakpoints_per_function_[func_index];
+ DCHECK_LT(0, offset);
+ auto insertion_point =
+ std::lower_bound(breakpoints.begin(), breakpoints.end(), offset);
+ if (insertion_point == breakpoints.end()) return;
+ if (*insertion_point != offset) return;
breakpoints.erase(insertion_point);
+ breakpoints_copy = breakpoints;
}
- RecompileLiftoffWithBreakpoints(func_index, VectorOf(breakpoints),
- current_isolate);
+
+ UpdateBreakpoints(func_index, VectorOf(breakpoints_copy), current_isolate);
}
void RemoveDebugSideTables(Vector<WasmCode* const> codes) {
@@ -819,11 +620,39 @@ class DebugInfoImpl {
}
private:
+ struct FrameInspectionScope {
+ FrameInspectionScope(DebugInfoImpl* debug_info, Isolate* isolate,
+ Address pc)
+ : code(isolate->wasm_engine()->code_manager()->LookupCode(pc)),
+ pc_offset(static_cast<int>(pc - code->instruction_start())),
+ debug_side_table(
+ code->is_inspectable()
+ ? debug_info->GetDebugSideTable(code, isolate->allocator())
+ : nullptr),
+ debug_side_table_entry(debug_side_table
+ ? debug_side_table->GetEntry(pc_offset)
+ : nullptr) {
+ DCHECK_IMPLIES(code->is_inspectable(), debug_side_table_entry != nullptr);
+ }
+
+ bool is_inspectable() const { return debug_side_table_entry; }
+
+ wasm::WasmCodeRefScope wasm_code_ref_scope;
+ wasm::WasmCode* code;
+ int pc_offset;
+ const DebugSideTable* debug_side_table;
+ const DebugSideTable::Entry* debug_side_table_entry;
+ };
+
const DebugSideTable* GetDebugSideTable(WasmCode* code,
AccountingAllocator* allocator) {
- base::MutexGuard guard(&mutex_);
- if (auto& existing_table = debug_side_tables_[code]) {
- return existing_table.get();
+ DCHECK(code->is_inspectable());
+ {
+ // Only hold the mutex temporarily. We can't hold it while generating the
+ // debug side table, because compilation takes the {NativeModule} lock.
+ base::MutexGuard guard(&mutex_);
+ auto it = debug_side_tables_.find(code);
+ if (it != debug_side_tables_.end()) return it->second.get();
}
// Otherwise create the debug side table now.
@@ -839,7 +668,10 @@ class DebugInfoImpl {
DebugSideTable* ret = debug_side_table.get();
// Install into cache and return.
- debug_side_tables_[code] = std::move(debug_side_table);
+ {
+ base::MutexGuard guard(&mutex_);
+ debug_side_tables_[code] = std::move(debug_side_table);
+ }
return ret;
}
@@ -911,7 +743,6 @@ class DebugInfoImpl {
// code. The frame layout itself should be independent of breakpoints.
// TODO(thibaudm): update other threads as well.
void UpdateReturnAddresses(Isolate* isolate, WasmCode* new_code) {
- DCHECK(new_code->is_liftoff());
// The first return location is after the breakpoint, others are after wasm
// calls.
ReturnLocation return_location = kAfterBreakpoint;
@@ -920,25 +751,31 @@ class DebugInfoImpl {
// We still need the flooded function for stepping.
if (it.frame()->id() == stepping_frame_) continue;
if (!it.is_wasm()) continue;
- WasmCompiledFrame* frame = WasmCompiledFrame::cast(it.frame());
+ WasmFrame* frame = WasmFrame::cast(it.frame());
if (frame->native_module() != new_code->native_module()) continue;
if (frame->function_index() != new_code->index()) continue;
- WasmCode* old_code = frame->wasm_code();
- if (!old_code->is_liftoff()) continue;
- int pc_offset =
- static_cast<int>(frame->pc() - old_code->instruction_start());
- int position = frame->position();
- int byte_offset = FindByteOffset(pc_offset, old_code);
- Address new_pc = FindNewPC(new_code, byte_offset, return_location);
- PointerAuthentication::ReplacePC(frame->pc_address(), new_pc,
- kSystemPointerSize);
- USE(position);
- // The frame position should still be the same after OSR.
- DCHECK_EQ(position, frame->position());
+ if (!frame->wasm_code()->is_liftoff()) continue;
+ UpdateReturnAddress(frame, new_code, return_location);
}
}
- bool IsAtReturn(WasmCompiledFrame* frame) {
+ void UpdateReturnAddress(WasmFrame* frame, WasmCode* new_code,
+ ReturnLocation return_location) {
+ DCHECK(new_code->is_liftoff());
+ DCHECK_EQ(frame->function_index(), new_code->index());
+ DCHECK_EQ(frame->native_module(), new_code->native_module());
+ DCHECK(frame->wasm_code()->is_liftoff());
+#ifdef DEBUG
+ int old_position = frame->position();
+#endif
+ Address new_pc = FindNewPC(new_code, frame->byte_offset(), return_location);
+ PointerAuthentication::ReplacePC(frame->pc_address(), new_pc,
+ kSystemPointerSize);
+ // The frame position should still be the same after OSR.
+ DCHECK_EQ(old_position, frame->position());
+ }
+
+ bool IsAtReturn(WasmFrame* frame) {
DisallowHeapAllocation no_gc;
int position = frame->position();
NativeModule* native_module =
@@ -967,10 +804,9 @@ class DebugInfoImpl {
// function).
std::unordered_map<int, std::vector<int>> breakpoints_per_function_;
- // Store the frame ID when stepping, to avoid breaking in recursive calls of
- // the same function.
+ // Store the frame ID when stepping, to avoid overwriting that frame when
+ // setting or removing a breakpoint.
StackFrameId stepping_frame_ = NO_ID;
- int flooded_function_index_ = -1;
DISALLOW_COPY_AND_ASSIGN(DebugInfoImpl);
};
@@ -980,6 +816,24 @@ DebugInfo::DebugInfo(NativeModule* native_module)
DebugInfo::~DebugInfo() = default;
+int DebugInfo::GetNumLocals(Isolate* isolate, Address pc) {
+ return impl_->GetNumLocals(isolate, pc);
+}
+
+WasmValue DebugInfo::GetLocalValue(int local, Isolate* isolate, Address pc,
+ Address fp, Address debug_break_fp) {
+ return impl_->GetLocalValue(local, isolate, pc, fp, debug_break_fp);
+}
+
+int DebugInfo::GetStackDepth(Isolate* isolate, Address pc) {
+ return impl_->GetStackDepth(isolate, pc);
+}
+
+WasmValue DebugInfo::GetStackValue(int index, Isolate* isolate, Address pc,
+ Address fp, Address debug_break_fp) {
+ return impl_->GetStackValue(index, isolate, pc, fp, debug_break_fp);
+}
+
Handle<JSObject> DebugInfo::GetLocalScopeObject(Isolate* isolate, Address pc,
Address fp,
Address debug_break_fp) {
@@ -1007,7 +861,7 @@ void DebugInfo::PrepareStep(Isolate* isolate, StackFrameId break_frame_id) {
void DebugInfo::ClearStepping() { impl_->ClearStepping(); }
-bool DebugInfo::IsStepping(WasmCompiledFrame* frame) {
+bool DebugInfo::IsStepping(WasmFrame* frame) {
return impl_->IsStepping(frame);
}
@@ -1022,40 +876,6 @@ void DebugInfo::RemoveDebugSideTables(Vector<WasmCode* const> code) {
} // namespace wasm
-namespace {
-
-wasm::InterpreterHandle* GetOrCreateInterpreterHandle(
- Isolate* isolate, Handle<WasmDebugInfo> debug_info) {
- Handle<Object> handle(debug_info->interpreter_handle(), isolate);
- if (handle->IsUndefined(isolate)) {
- // Use the maximum stack size to estimate the maximum size of the
- // interpreter. The interpreter keeps its own stack internally, and the size
- // of the stack should dominate the overall size of the interpreter. We
- // multiply by '2' to account for the growing strategy for the backing store
- // of the stack.
- size_t interpreter_size = FLAG_stack_size * KB * 2;
- handle = Managed<wasm::InterpreterHandle>::Allocate(
- isolate, interpreter_size, isolate, debug_info);
- debug_info->set_interpreter_handle(*handle);
- }
-
- return Handle<Managed<wasm::InterpreterHandle>>::cast(handle)->raw();
-}
-
-wasm::InterpreterHandle* GetInterpreterHandle(WasmDebugInfo debug_info) {
- Object handle_obj = debug_info.interpreter_handle();
- DCHECK(!handle_obj.IsUndefined());
- return Managed<wasm::InterpreterHandle>::cast(handle_obj).raw();
-}
-
-wasm::InterpreterHandle* GetInterpreterHandleOrNull(WasmDebugInfo debug_info) {
- Object handle_obj = debug_info.interpreter_handle();
- if (handle_obj.IsUndefined()) return nullptr;
- return Managed<wasm::InterpreterHandle>::cast(handle_obj).raw();
-}
-
-} // namespace
-
Handle<WasmDebugInfo> WasmDebugInfo::New(Handle<WasmInstanceObject> instance) {
DCHECK(!instance->has_debug_info());
Factory* factory = instance->GetIsolate()->factory();
@@ -1084,128 +904,6 @@ wasm::WasmInterpreter* WasmDebugInfo::SetupForTesting(
}
// static
-void WasmDebugInfo::PrepareStepIn(Handle<WasmDebugInfo> debug_info,
- int func_index) {
- Isolate* isolate = debug_info->GetIsolate();
- auto* handle = GetOrCreateInterpreterHandle(isolate, debug_info);
- RedirectToInterpreter(debug_info, Vector<int>(&func_index, 1));
- const wasm::WasmFunction* func = &handle->module()->functions[func_index];
- handle->interpreter()->PrepareStepIn(func);
- // Debug break would be considered as a step-in inside wasm.
- handle->PrepareStep(StepAction::StepIn);
-}
-
-// static
-void WasmDebugInfo::SetBreakpoint(Handle<WasmDebugInfo> debug_info,
- int func_index, int offset) {
- Isolate* isolate = debug_info->GetIsolate();
- auto* handle = GetOrCreateInterpreterHandle(isolate, debug_info);
- RedirectToInterpreter(debug_info, Vector<int>(&func_index, 1));
- const wasm::WasmFunction* func = &handle->module()->functions[func_index];
- handle->interpreter()->SetBreakpoint(func, offset, true);
-}
-
-// static
-void WasmDebugInfo::ClearBreakpoint(Handle<WasmDebugInfo> debug_info,
- int func_index, int offset) {
- Isolate* isolate = debug_info->GetIsolate();
- auto* handle = GetOrCreateInterpreterHandle(isolate, debug_info);
- // TODO(leese): If there are no more breakpoints left it would be good to
- // undo redirecting to the interpreter.
- const wasm::WasmFunction* func = &handle->module()->functions[func_index];
- handle->interpreter()->SetBreakpoint(func, offset, false);
-}
-
-// static
-void WasmDebugInfo::RedirectToInterpreter(Handle<WasmDebugInfo> debug_info,
- Vector<int> func_indexes) {
- Isolate* isolate = debug_info->GetIsolate();
- // Ensure that the interpreter is instantiated.
- GetOrCreateInterpreterHandle(isolate, debug_info);
- Handle<WasmInstanceObject> instance(debug_info->wasm_instance(), isolate);
- wasm::NativeModule* native_module = instance->module_object().native_module();
- const wasm::WasmModule* module = instance->module();
-
- // We may modify the wasm jump table.
- wasm::NativeModuleModificationScope native_module_modification_scope(
- native_module);
-
- for (int func_index : func_indexes) {
- DCHECK_LE(0, func_index);
- DCHECK_GT(module->functions.size(), func_index);
- // Note that this is just a best effort check. Multiple threads can still
- // race at redirecting the same function to the interpreter, which is OK.
- if (native_module->IsRedirectedToInterpreter(func_index)) continue;
-
- wasm::WasmCodeRefScope code_ref_scope;
- wasm::WasmCompilationResult result = compiler::CompileWasmInterpreterEntry(
- isolate->wasm_engine(), native_module->enabled_features(), func_index,
- module->functions[func_index].sig);
- std::unique_ptr<wasm::WasmCode> wasm_code = native_module->AddCode(
- func_index, result.code_desc, result.frame_slot_count,
- result.tagged_parameter_slots,
- result.protected_instructions_data.as_vector(),
- result.source_positions.as_vector(), wasm::WasmCode::kInterpreterEntry,
- wasm::ExecutionTier::kInterpreter);
- native_module->PublishCode(std::move(wasm_code));
- DCHECK(native_module->IsRedirectedToInterpreter(func_index));
- }
-}
-
-void WasmDebugInfo::PrepareStep(StepAction step_action) {
- GetInterpreterHandle(*this)->PrepareStep(step_action);
-}
-
-// static
-bool WasmDebugInfo::RunInterpreter(Isolate* isolate,
- Handle<WasmDebugInfo> debug_info,
- Address frame_pointer, int func_index,
- Vector<wasm::WasmValue> argument_values,
- Vector<wasm::WasmValue> return_values) {
- DCHECK_LE(0, func_index);
- auto* handle = GetOrCreateInterpreterHandle(isolate, debug_info);
- Handle<WasmInstanceObject> instance(debug_info->wasm_instance(), isolate);
- return handle->Execute(instance, frame_pointer,
- static_cast<uint32_t>(func_index), argument_values,
- return_values);
-}
-
-std::vector<std::pair<uint32_t, int>> WasmDebugInfo::GetInterpretedStack(
- Address frame_pointer) {
- return GetInterpreterHandle(*this)->GetInterpretedStack(frame_pointer);
-}
-
-int WasmDebugInfo::NumberOfActiveFrames(Address frame_pointer) {
- return GetInterpreterHandle(*this)->NumberOfActiveFrames(frame_pointer);
-}
-
-wasm::WasmInterpreter::FramePtr WasmDebugInfo::GetInterpretedFrame(
- Address frame_pointer, int idx) {
- return GetInterpreterHandle(*this)->GetInterpretedFrame(frame_pointer, idx);
-}
-
-uint64_t WasmDebugInfo::NumInterpretedCalls() {
- auto* handle = GetInterpreterHandleOrNull(*this);
- return handle ? handle->NumInterpretedCalls() : 0;
-}
-
-// static
-Handle<JSObject> WasmDebugInfo::GetLocalScopeObject(
- Handle<WasmDebugInfo> debug_info, Address frame_pointer, int frame_index) {
- auto* interp_handle = GetInterpreterHandle(*debug_info);
- auto frame = interp_handle->GetInterpretedFrame(frame_pointer, frame_index);
- return interp_handle->GetLocalScopeObject(frame.get(), debug_info);
-}
-
-// static
-Handle<JSObject> WasmDebugInfo::GetStackScopeObject(
- Handle<WasmDebugInfo> debug_info, Address frame_pointer, int frame_index) {
- auto* interp_handle = GetInterpreterHandle(*debug_info);
- auto frame = interp_handle->GetInterpretedFrame(frame_pointer, frame_index);
- return interp_handle->GetStackScopeObject(frame.get(), debug_info);
-}
-
-// static
Handle<Code> WasmDebugInfo::GetCWasmEntry(Handle<WasmDebugInfo> debug_info,
const wasm::FunctionSig* sig) {
Isolate* isolate = debug_info->GetIsolate();
@@ -1315,61 +1013,71 @@ bool WasmScript::SetBreakPointForFunction(Handle<Script> script, int func_index,
WasmScript::AddBreakpointToInfo(script, func.code.offset() + offset,
break_point);
- if (FLAG_debug_in_liftoff) {
- native_module->GetDebugInfo()->SetBreakpoint(func_index, offset, isolate);
- } else {
- // Iterate over all instances and tell them to set this new breakpoint.
- // We do this using the weak list of all instances from the script.
- Handle<WeakArrayList> weak_instance_list(script->wasm_weak_instance_list(),
- isolate);
- for (int i = 0; i < weak_instance_list->length(); ++i) {
- MaybeObject maybe_instance = weak_instance_list->Get(i);
- if (maybe_instance->IsWeak()) {
- Handle<WasmInstanceObject> instance(
- WasmInstanceObject::cast(maybe_instance->GetHeapObjectAssumeWeak()),
- isolate);
- Handle<WasmDebugInfo> debug_info =
- WasmInstanceObject::GetOrCreateDebugInfo(instance);
- WasmDebugInfo::SetBreakpoint(debug_info, func_index, offset);
- }
+ native_module->GetDebugInfo()->SetBreakpoint(func_index, offset, isolate);
+
+ return true;
+}
+
+namespace {
+
+int GetBreakpointPos(Isolate* isolate, Object break_point_info_or_undef) {
+ if (break_point_info_or_undef.IsUndefined(isolate)) return kMaxInt;
+ return BreakPointInfo::cast(break_point_info_or_undef).source_position();
+}
+
+int FindBreakpointInfoInsertPos(Isolate* isolate,
+ Handle<FixedArray> breakpoint_infos,
+ int position) {
+ // Find insert location via binary search, taking care of undefined values on
+ // the right. Position is always greater than zero.
+ DCHECK_LT(0, position);
+
+ int left = 0; // inclusive
+ int right = breakpoint_infos->length(); // exclusive
+ while (right - left > 1) {
+ int mid = left + (right - left) / 2;
+ Object mid_obj = breakpoint_infos->get(mid);
+ if (GetBreakpointPos(isolate, mid_obj) <= position) {
+ left = mid;
+ } else {
+ right = mid;
}
}
- return true;
+ int left_pos = GetBreakpointPos(isolate, breakpoint_infos->get(left));
+ return left_pos < position ? left + 1 : left;
}
+} // namespace
+
// static
bool WasmScript::ClearBreakPoint(Handle<Script> script, int position,
Handle<BreakPoint> break_point) {
+ if (!script->has_wasm_breakpoint_infos()) return false;
+
Isolate* isolate = script->GetIsolate();
+ Handle<FixedArray> breakpoint_infos(script->wasm_breakpoint_infos(), isolate);
- // Find the function for this breakpoint.
- const wasm::WasmModule* module = script->wasm_native_module()->module();
- int func_index = GetContainingWasmFunction(module, position);
- if (func_index < 0) return false;
- const wasm::WasmFunction& func = module->functions[func_index];
- int offset_in_func = position - func.code.offset();
+ int pos = FindBreakpointInfoInsertPos(isolate, breakpoint_infos, position);
- if (!WasmScript::RemoveBreakpointFromInfo(script, position, break_point)) {
- return false;
- }
+ // Does a BreakPointInfo object already exist for this position?
+ if (pos == breakpoint_infos->length()) return false;
- // Iterate over all instances and tell them to remove this breakpoint.
- // We do this using the weak list of all instances from the script.
- Handle<WeakArrayList> weak_instance_list(script->wasm_weak_instance_list(),
- isolate);
- for (int i = 0; i < weak_instance_list->length(); ++i) {
- MaybeObject maybe_instance = weak_instance_list->Get(i);
- if (maybe_instance->IsWeak()) {
- Handle<WasmInstanceObject> instance(
- WasmInstanceObject::cast(maybe_instance->GetHeapObjectAssumeWeak()),
- isolate);
- Handle<WasmDebugInfo> debug_info =
- WasmInstanceObject::GetOrCreateDebugInfo(instance);
- WasmDebugInfo::ClearBreakpoint(debug_info, func_index, offset_in_func);
+ Handle<BreakPointInfo> info(BreakPointInfo::cast(breakpoint_infos->get(pos)),
+ isolate);
+ BreakPointInfo::ClearBreakPoint(isolate, info, break_point);
+
+ // Check if there are no more breakpoints at this location.
+ if (info->GetBreakPointCount(isolate) == 0) {
+ // Update array by moving breakpoints up one position.
+ for (int i = pos; i < breakpoint_infos->length() - 1; i++) {
+ Object entry = breakpoint_infos->get(i + 1);
+ breakpoint_infos->set(i, entry);
+ if (entry.IsUndefined(isolate)) break;
}
+ // Make sure last array element is empty as a result.
+ breakpoint_infos->set_undefined(breakpoint_infos->length() - 1);
}
-
return true;
}
@@ -1401,38 +1109,12 @@ bool WasmScript::ClearBreakPointById(Handle<Script> script, int breakpoint_id) {
return false;
}
-namespace {
-
-int GetBreakpointPos(Isolate* isolate, Object break_point_info_or_undef) {
- if (break_point_info_or_undef.IsUndefined(isolate)) return kMaxInt;
- return BreakPointInfo::cast(break_point_info_or_undef).source_position();
-}
-
-int FindBreakpointInfoInsertPos(Isolate* isolate,
- Handle<FixedArray> breakpoint_infos,
- int position) {
- // Find insert location via binary search, taking care of undefined values on
- // the right. Position is always greater than zero.
- DCHECK_LT(0, position);
-
- int left = 0; // inclusive
- int right = breakpoint_infos->length(); // exclusive
- while (right - left > 1) {
- int mid = left + (right - left) / 2;
- Object mid_obj = breakpoint_infos->get(mid);
- if (GetBreakpointPos(isolate, mid_obj) <= position) {
- left = mid;
- } else {
- right = mid;
- }
- }
-
- int left_pos = GetBreakpointPos(isolate, breakpoint_infos->get(left));
- return left_pos < position ? left + 1 : left;
+// static
+void WasmScript::ClearAllBreakpoints(Script script) {
+ script.set_wasm_breakpoint_infos(
+ ReadOnlyRoots(script.GetIsolate()).empty_fixed_array());
}
-} // namespace
-
// static
void WasmScript::AddBreakpointToInfo(Handle<Script> script, int position,
Handle<BreakPoint> break_point) {
@@ -1490,69 +1172,6 @@ void WasmScript::AddBreakpointToInfo(Handle<Script> script, int position,
}
// static
-bool WasmScript::RemoveBreakpointFromInfo(Handle<Script> script, int position,
- Handle<BreakPoint> break_point) {
- if (!script->has_wasm_breakpoint_infos()) return false;
-
- Isolate* isolate = script->GetIsolate();
- Handle<FixedArray> breakpoint_infos(script->wasm_breakpoint_infos(), isolate);
-
- int pos = FindBreakpointInfoInsertPos(isolate, breakpoint_infos, position);
-
- // Does a BreakPointInfo object already exist for this position?
- if (pos == breakpoint_infos->length()) return false;
-
- Handle<BreakPointInfo> info(BreakPointInfo::cast(breakpoint_infos->get(pos)),
- isolate);
- BreakPointInfo::ClearBreakPoint(isolate, info, break_point);
-
- // Check if there are no more breakpoints at this location.
- if (info->GetBreakPointCount(isolate) == 0) {
- // Update array by moving breakpoints up one position.
- for (int i = pos; i < breakpoint_infos->length() - 1; i++) {
- Object entry = breakpoint_infos->get(i + 1);
- breakpoint_infos->set(i, entry);
- if (entry.IsUndefined(isolate)) break;
- }
- // Make sure last array element is empty as a result.
- breakpoint_infos->set_undefined(breakpoint_infos->length() - 1);
- }
- return true;
-}
-
-void WasmScript::SetBreakpointsOnNewInstance(
- Handle<Script> script, Handle<WasmInstanceObject> instance) {
- if (!script->has_wasm_breakpoint_infos()) return;
- Isolate* isolate = script->GetIsolate();
- Handle<WasmDebugInfo> debug_info =
- WasmInstanceObject::GetOrCreateDebugInfo(instance);
-
- Handle<FixedArray> breakpoint_infos(script->wasm_breakpoint_infos(), isolate);
- // If the array exists, it should not be empty.
- DCHECK_LT(0, breakpoint_infos->length());
-
- for (int i = 0, e = breakpoint_infos->length(); i < e; ++i) {
- Handle<Object> obj(breakpoint_infos->get(i), isolate);
- if (obj->IsUndefined(isolate)) {
- for (; i < e; ++i) {
- DCHECK(breakpoint_infos->get(i).IsUndefined(isolate));
- }
- break;
- }
- Handle<BreakPointInfo> breakpoint_info = Handle<BreakPointInfo>::cast(obj);
- int position = breakpoint_info->source_position();
-
- // Find the function for this breakpoint, and set the breakpoint.
- const wasm::WasmModule* module = script->wasm_native_module()->module();
- int func_index = GetContainingWasmFunction(module, position);
- DCHECK_LE(0, func_index);
- const wasm::WasmFunction& func = module->functions[func_index];
- int offset_in_func = position - func.code.offset();
- WasmDebugInfo::SetBreakpoint(debug_info, func_index, offset_in_func);
- }
-}
-
-// static
bool WasmScript::GetPossibleBreakpoints(
wasm::NativeModule* native_module, const v8::debug::Location& start,
const v8::debug::Location& end,