summaryrefslogtreecommitdiff
path: root/deps/v8/src/wasm/wasm-debug.cc
diff options
context:
space:
mode:
authorMatheus Marchini <mmarchini@netflix.com>2020-03-05 10:49:19 -0800
committerMatheus Marchini <mmarchini@netflix.com>2020-03-18 16:23:22 -0700
commit2883c855e0105b51e5c8020d21458af109ffe3d4 (patch)
tree26777aad0a398e9f7755c8b65ac76827fe352a81 /deps/v8/src/wasm/wasm-debug.cc
parent5f0af2af2a67216e00fe07ccda11e889d14abfcd (diff)
downloadnode-new-2883c855e0105b51e5c8020d21458af109ffe3d4.tar.gz
deps: update V8 to 8.1.307.20
PR-URL: https://github.com/nodejs/node/pull/32116 Reviewed-By: Michaƫl Zasso <targos@protonmail.com> Reviewed-By: Jiawen Geng <technicalcute@gmail.com> Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
Diffstat (limited to 'deps/v8/src/wasm/wasm-debug.cc')
-rw-r--r--deps/v8/src/wasm/wasm-debug.cc852
1 files changed, 752 insertions, 100 deletions
diff --git a/deps/v8/src/wasm/wasm-debug.cc b/deps/v8/src/wasm/wasm-debug.cc
index f3580e4427..230427bed0 100644
--- a/deps/v8/src/wasm/wasm-debug.cc
+++ b/deps/v8/src/wasm/wasm-debug.cc
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "src/wasm/wasm-debug.h"
+
#include <unordered_map>
#include "src/base/optional.h"
@@ -14,6 +16,7 @@
#include "src/execution/isolate.h"
#include "src/heap/factory.h"
#include "src/utils/identity-map.h"
+#include "src/wasm/baseline/liftoff-compiler.h"
#include "src/wasm/module-decoder.h"
#include "src/wasm/wasm-code-manager.h"
#include "src/wasm/wasm-interpreter.h"
@@ -69,31 +72,17 @@ Handle<Object> WasmValueToValueObject(Isolate* isolate, WasmValue value) {
}
}
-MaybeHandle<String> GetLocalName(Isolate* isolate,
- Handle<WasmDebugInfo> debug_info,
- int func_index, int local_index) {
- DCHECK_LE(0, func_index);
- DCHECK_LE(0, local_index);
- if (!debug_info->has_locals_names()) {
- Handle<WasmModuleObject> module_object(
- debug_info->wasm_instance().module_object(), isolate);
- Handle<FixedArray> locals_names = DecodeLocalNames(isolate, module_object);
- debug_info->set_locals_names(*locals_names);
- }
-
- Handle<FixedArray> locals_names(debug_info->locals_names(), isolate);
- if (func_index >= locals_names->length() ||
- locals_names->get(func_index).IsUndefined(isolate)) {
- return {};
- }
-
- Handle<FixedArray> func_locals_names(
- FixedArray::cast(locals_names->get(func_index)), isolate);
- if (local_index >= func_locals_names->length() ||
- func_locals_names->get(local_index).IsUndefined(isolate)) {
- return {};
- }
- return handle(String::cast(func_locals_names->get(local_index)), isolate);
+MaybeHandle<String> GetLocalNameString(Isolate* isolate,
+ NativeModule* native_module,
+ int func_index, int local_index) {
+ WireBytesRef name_ref =
+ native_module->GetDebugInfo()->GetLocalName(func_index, local_index);
+ ModuleWireBytes wire_bytes{native_module->wire_bytes()};
+ // Bounds were checked during decoding.
+ DCHECK(wire_bytes.BoundsCheck(name_ref));
+ Vector<const char> name = wire_bytes.GetNameOrNull(name_ref);
+ if (name.begin() == nullptr) return {};
+ return isolate->factory()->NewStringFromUtf8(name);
}
class InterpreterHandle {
@@ -119,6 +108,10 @@ class InterpreterHandle {
activations_.erase(frame_pointer);
}
+ bool HasActivation(Address frame_pointer) {
+ return activations_.count(frame_pointer);
+ }
+
std::pair<uint32_t, uint32_t> GetActivationFrameRange(
WasmInterpreter::Thread* thread, Address frame_pointer) {
DCHECK_EQ(1, activations_.count(frame_pointer));
@@ -221,15 +214,28 @@ class InterpreterHandle {
}
// Copy back the return value.
- DCHECK_GE(kV8MaxWasmFunctionReturns, sig->return_count());
- // TODO(wasm): Handle multi-value returns.
- DCHECK_EQ(1, kV8MaxWasmFunctionReturns);
- if (sig->return_count()) {
- return_values[0] = thread->GetReturnValue(0);
+#ifdef DEBUG
+ const int max_count = WasmFeatures::FromIsolate(isolate_).has_mv()
+ ? kV8MaxWasmFunctionMultiReturns
+ : kV8MaxWasmFunctionReturns;
+#endif
+ DCHECK_GE(max_count, sig->return_count());
+ for (unsigned i = 0; i < sig->return_count(); ++i) {
+ return_values[i] = thread->GetReturnValue(i);
}
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;
}
@@ -279,7 +285,7 @@ class InterpreterHandle {
Handle<Script> script(module_object->script(), isolate_);
int position = GetTopPosition(module_object);
Handle<FixedArray> breakpoints;
- if (WasmModuleObject::CheckBreakPoints(isolate_, script, position)
+ if (WasmScript::CheckBreakPoints(isolate_, script, position)
.ToHandle(&breakpoints)) {
// We hit one or several breakpoints. Clear stepping, notify the
// listeners and return.
@@ -340,6 +346,18 @@ class InterpreterHandle {
return stack;
}
+ int NumberOfActiveFrames(Address frame_pointer) {
+ if (!HasActivation(frame_pointer)) return 0;
+
+ DCHECK_EQ(1, interpreter()->GetThreadCount());
+ WasmInterpreter::Thread* thread = interpreter()->GetThread(0);
+
+ std::pair<uint32_t, uint32_t> frame_range =
+ GetActivationFrameRange(thread, frame_pointer);
+
+ return frame_range.second - frame_range.first;
+ }
+
WasmInterpreter::FramePtr GetInterpretedFrame(Address frame_pointer,
int idx) {
DCHECK_EQ(1, interpreter()->GetThreadCount());
@@ -358,52 +376,6 @@ class InterpreterHandle {
return interpreter()->GetThread(0)->NumInterpretedCalls();
}
- Handle<JSObject> GetGlobalScopeObject(InterpretedFrame* frame,
- Handle<WasmDebugInfo> debug_info) {
- Isolate* isolate = isolate_;
- Handle<WasmInstanceObject> instance(debug_info->wasm_instance(), isolate);
-
- Handle<JSObject> global_scope_object =
- isolate_->factory()->NewJSObjectWithNullProto();
- if (instance->has_memory_object()) {
- Handle<String> name =
- isolate_->factory()->InternalizeString(StaticCharVector("memory"));
- 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::SetOwnPropertyIgnoreAttributes(global_scope_object, name,
- uint8_array, NONE)
- .Assert();
- }
-
- DCHECK_EQ(1, interpreter()->GetThreadCount());
- WasmInterpreter::Thread* thread = interpreter()->GetThread(0);
-
- uint32_t global_count = thread->GetGlobalCount();
- if (global_count > 0) {
- Handle<JSObject> globals_obj =
- isolate_->factory()->NewJSObjectWithNullProto();
- Handle<String> globals_name =
- isolate_->factory()->InternalizeString(StaticCharVector("globals"));
- JSObject::SetOwnPropertyIgnoreAttributes(global_scope_object,
- globals_name, globals_obj, NONE)
- .Assert();
-
- for (uint32_t i = 0; i < global_count; ++i) {
- const char* label = "global#%d";
- Handle<String> name = PrintFToOneByteString<true>(isolate_, label, i);
- WasmValue value = thread->GetGlobalValue(i);
- Handle<Object> value_obj = WasmValueToValueObject(isolate_, value);
- JSObject::SetOwnPropertyIgnoreAttributes(globals_obj, name, value_obj,
- NONE)
- .Assert();
- }
- }
-
- return global_scope_object;
- }
-
Handle<JSObject> GetLocalScopeObject(InterpretedFrame* frame,
Handle<WasmDebugInfo> debug_info) {
Isolate* isolate = isolate_;
@@ -419,13 +391,15 @@ class InterpreterHandle {
isolate_->factory()->NewJSObjectWithNullProto();
Handle<String> locals_name =
isolate_->factory()->InternalizeString(StaticCharVector("locals"));
- JSObject::SetOwnPropertyIgnoreAttributes(local_scope_object, locals_name,
- locals_obj, NONE)
- .Assert();
+ 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) {
- MaybeHandle<String> name =
- GetLocalName(isolate, debug_info, frame->function()->func_index, i);
- if (name.is_null()) {
+ Handle<Name> name;
+ if (!GetLocalNameString(isolate, native_module,
+ frame->function()->func_index, i)
+ .ToHandle(&name)) {
// Parameters should come before locals in alphabetical ordering, so
// we name them "args" here.
const char* label = i < num_params ? "arg#%d" : "local#%d";
@@ -433,9 +407,15 @@ class InterpreterHandle {
}
WasmValue value = frame->GetLocalValue(i);
Handle<Object> value_obj = WasmValueToValueObject(isolate_, value);
- JSObject::SetOwnPropertyIgnoreAttributes(
- locals_obj, name.ToHandleChecked(), value_obj, NONE)
- .Assert();
+ // {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();
}
}
@@ -448,15 +428,13 @@ class InterpreterHandle {
isolate_->factory()->NewJSObjectWithNullProto();
Handle<String> stack_name =
isolate_->factory()->InternalizeString(StaticCharVector("stack"));
- JSObject::SetOwnPropertyIgnoreAttributes(local_scope_object, stack_name,
- stack_obj, NONE)
- .Assert();
+ 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::SetOwnElementIgnoreAttributes(
- stack_obj, static_cast<uint32_t>(i), value_obj, NONE)
- .Assert();
+ JSObject::AddDataElement(stack_obj, static_cast<uint32_t>(i), value_obj,
+ NONE);
}
return local_scope_object;
}
@@ -467,6 +445,261 @@ class InterpreterHandle {
} // namespace
+Handle<JSObject> GetGlobalScopeObject(Handle<WasmInstanceObject> instance) {
+ Isolate* isolate = instance->GetIsolate();
+
+ Handle<JSObject> global_scope_object =
+ isolate->factory()->NewJSObjectWithNullProto();
+ if (instance->has_memory_object()) {
+ Handle<String> name =
+ isolate->factory()->InternalizeString(StaticCharVector("memory"));
+ 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,
+ NONE);
+ }
+
+ auto& globals = instance->module()->globals;
+ if (globals.size() > 0) {
+ Handle<JSObject> globals_obj =
+ isolate->factory()->NewJSObjectWithNullProto();
+ Handle<String> globals_name =
+ isolate->factory()->InternalizeString(StaticCharVector("globals"));
+ JSObject::AddProperty(isolate, global_scope_object, globals_name,
+ globals_obj, NONE);
+
+ for (size_t i = 0; i < globals.size(); ++i) {
+ const char* label = "global#%d";
+ Handle<String> name = PrintFToOneByteString<true>(isolate, label, i);
+ WasmValue value =
+ WasmInstanceObject::GetGlobalValue(instance, globals[i]);
+ Handle<Object> value_obj = WasmValueToValueObject(isolate, value);
+ JSObject::AddProperty(isolate, globals_obj, name, value_obj, NONE);
+ }
+ }
+
+ return global_scope_object;
+}
+
+class DebugInfoImpl {
+ public:
+ explicit DebugInfoImpl(NativeModule* native_module)
+ : native_module_(native_module) {}
+
+ Handle<JSObject> GetLocalScopeObject(Isolate* isolate, Address pc,
+ Address fp) {
+ 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;
+
+ const WasmModule* module = native_module_->module();
+ const WasmFunction* function = &module->functions[code->index()];
+ DebugSideTable* debug_side_table =
+ GetDebugSideTable(isolate->allocator(), function->func_index);
+ 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);
+
+ // Fill parameters and locals.
+ int num_params = static_cast<int>(function->sig->parameter_count());
+ int num_locals = static_cast<int>(debug_side_table->num_locals());
+ DCHECK_LE(num_params, 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);
+ for (int i = 0; i < num_locals; ++i) {
+ Handle<Name> name;
+ if (!GetLocalNameString(isolate, native_module_, function->func_index,
+ i)
+ .ToHandle(&name)) {
+ // Parameters should come before locals in alphabetical ordering, so
+ // we name them "args" here.
+ const char* label = i < num_params ? "arg#%d" : "local#%d";
+ name = PrintFToOneByteString<true>(isolate, label, i);
+ }
+ WasmValue value =
+ GetValue(debug_side_table_entry, debug_side_table->local_type(i), i,
+ fp - debug_side_table->local_stack_offset(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 = debug_side_table_entry->stack_height();
+ // 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) {
+ ValueType type = debug_side_table_entry->stack_type(i);
+ WasmValue value = GetValue(debug_side_table_entry, type, num_locals + i,
+ fp - debug_side_table_entry->stack_offset(i));
+ Handle<Object> value_obj = WasmValueToValueObject(isolate, value);
+ JSObject::AddDataElement(stack_obj, static_cast<uint32_t>(i), value_obj,
+ NONE);
+ }
+ return local_scope_object;
+ }
+
+ WireBytesRef GetLocalName(int func_index, int local_index) {
+ base::MutexGuard guard(&mutex_);
+ if (!local_names_) {
+ local_names_ = std::make_unique<LocalNames>(
+ DecodeLocalNames(native_module_->wire_bytes()));
+ }
+ return local_names_->GetName(func_index, local_index);
+ }
+
+ void SetBreakpoint(int func_index, int offset) {
+ // 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_);
+
+ 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);
+
+ // Recompile the function with Liftoff, setting the new breakpoints.
+ CompilationEnv env = native_module_->CreateCompilationEnv();
+ auto* function = &native_module_->module()->functions[func_index];
+ Vector<const uint8_t> wire_bytes = native_module_->wire_bytes();
+ FunctionBody body{function->sig, function->code.offset(),
+ wire_bytes.begin() + function->code.offset(),
+ wire_bytes.begin() + function->code.end_offset()};
+ WasmCompilationResult result = ExecuteLiftoffCompilation(
+ native_module_->engine()->allocator(), &env, body, func_index, nullptr,
+ nullptr, VectorOf(breakpoints));
+ DCHECK(result.succeeded());
+
+ WasmCodeRefScope wasm_code_ref_scope;
+ WasmCode* new_code = native_module_->AddCompiledCode(std::move(result));
+
+ // TODO(clemensb): OSR active frames on the stack (on all threads).
+ USE(new_code);
+ }
+
+ private:
+ DebugSideTable* GetDebugSideTable(AccountingAllocator* allocator,
+ int func_index) {
+ base::MutexGuard guard(&mutex_);
+ if (debug_side_tables_.empty()) {
+ debug_side_tables_.resize(native_module_->module()->functions.size());
+ }
+ if (auto& existing_table = debug_side_tables_[func_index]) {
+ return existing_table.get();
+ }
+
+ // Otherwise create the debug side table now.
+ const WasmModule* module = native_module_->module();
+ const WasmFunction* function = &module->functions[func_index];
+ ModuleWireBytes wire_bytes{native_module_->wire_bytes()};
+ Vector<const byte> function_bytes = wire_bytes.GetFunctionBytes(function);
+ CompilationEnv env = native_module_->CreateCompilationEnv();
+ FunctionBody func_body{function->sig, 0, function_bytes.begin(),
+ function_bytes.end()};
+ DebugSideTable debug_side_table =
+ GenerateLiftoffDebugSideTable(allocator, &env, func_body);
+
+ // Install into cache and return.
+ debug_side_tables_[func_index] =
+ std::make_unique<DebugSideTable>(std::move(debug_side_table));
+ return debug_side_tables_[func_index].get();
+ }
+
+ // Get the value of a local (including parameters) or stack value. Stack
+ // values follow the locals in the same index space.
+ WasmValue GetValue(const DebugSideTable::Entry* debug_side_table_entry,
+ ValueType type, int index, Address stack_address) {
+ if (debug_side_table_entry->IsConstant(index)) {
+ DCHECK(type == kWasmI32 || type == kWasmI64);
+ return type == kWasmI32
+ ? WasmValue(debug_side_table_entry->GetConstant(index))
+ : WasmValue(
+ int64_t{debug_side_table_entry->GetConstant(index)});
+ }
+
+ // Otherwise load the value from the stack.
+ switch (type) {
+ case kWasmI32:
+ return WasmValue(ReadUnalignedValue<int32_t>(stack_address));
+ case kWasmI64:
+ return WasmValue(ReadUnalignedValue<int64_t>(stack_address));
+ case kWasmF32:
+ return WasmValue(ReadUnalignedValue<float>(stack_address));
+ case kWasmF64:
+ return WasmValue(ReadUnalignedValue<double>(stack_address));
+ default:
+ UNIMPLEMENTED();
+ }
+ }
+
+ NativeModule* const native_module_;
+
+ // {mutex_} protects all fields below.
+ base::Mutex mutex_;
+
+ // DebugSideTable per function, lazily initialized.
+ std::vector<std::unique_ptr<DebugSideTable>> debug_side_tables_;
+
+ // Names of locals, lazily decoded from the wire bytes.
+ std::unique_ptr<LocalNames> local_names_;
+
+ // Keeps track of the currently set breakpoints (by offset within that
+ // function).
+ std::unordered_map<int, std::vector<int>> breakpoints_per_function_;
+
+ DISALLOW_COPY_AND_ASSIGN(DebugInfoImpl);
+};
+
+DebugInfo::DebugInfo(NativeModule* native_module)
+ : impl_(std::make_unique<DebugInfoImpl>(native_module)) {}
+
+DebugInfo::~DebugInfo() = default;
+
+Handle<JSObject> DebugInfo::GetLocalScopeObject(Isolate* isolate, Address pc,
+ Address fp) {
+ return impl_->GetLocalScopeObject(isolate, pc, fp);
+}
+
+WireBytesRef DebugInfo::GetLocalName(int func_index, int local_index) {
+ return impl_->GetLocalName(func_index, local_index);
+}
+
+void DebugInfo::SetBreakpoint(int func_index, int offset) {
+ impl_->SetBreakpoint(func_index, offset);
+}
+
} // namespace wasm
namespace {
@@ -531,6 +764,18 @@ 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();
@@ -609,6 +854,10 @@ std::vector<std::pair<uint32_t, int>> WasmDebugInfo::GetInterpretedStack(
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);
@@ -620,14 +869,6 @@ uint64_t WasmDebugInfo::NumInterpretedCalls() {
}
// static
-Handle<JSObject> WasmDebugInfo::GetGlobalScopeObject(
- 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->GetGlobalScopeObject(frame.get(), debug_info);
-}
-
-// static
Handle<JSObject> WasmDebugInfo::GetLocalScopeObject(
Handle<WasmDebugInfo> debug_info, Address frame_pointer, int frame_index) {
auto* interp_handle = GetInterpreterHandle(*debug_info);
@@ -666,5 +907,416 @@ Handle<Code> WasmDebugInfo::GetCWasmEntry(Handle<WasmDebugInfo> debug_info,
return handle(Code::cast(entries->get(index)), isolate);
}
+namespace {
+
+// Return the next breakable position after {offset_in_func} in function
+// {func_index}, or 0 if there is none.
+// Note that 0 is never a breakable position in wasm, since the first byte
+// contains the locals count for the function.
+int FindNextBreakablePosition(wasm::NativeModule* native_module, int func_index,
+ int offset_in_func) {
+ AccountingAllocator alloc;
+ Zone tmp(&alloc, ZONE_NAME);
+ wasm::BodyLocalDecls locals(&tmp);
+ const byte* module_start = native_module->wire_bytes().begin();
+ const wasm::WasmFunction& func =
+ native_module->module()->functions[func_index];
+ wasm::BytecodeIterator iterator(module_start + func.code.offset(),
+ module_start + func.code.end_offset(),
+ &locals);
+ DCHECK_LT(0, locals.encoded_size);
+ if (offset_in_func < 0) return 0;
+ for (uint32_t offset : iterator.offsets()) {
+ if (offset >= static_cast<uint32_t>(offset_in_func)) return offset;
+ }
+ return 0;
+}
+
+} // namespace
+
+// static
+bool WasmScript::SetBreakPoint(Handle<Script> script, int* position,
+ Handle<BreakPoint> break_point) {
+ // 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 breakable_offset = FindNextBreakablePosition(script->wasm_native_module(),
+ func_index, offset_in_func);
+ if (breakable_offset == 0) return false;
+ *position = func.code.offset() + breakable_offset;
+
+ return WasmScript::SetBreakPointForFunction(script, func_index,
+ breakable_offset, break_point);
+}
+
+// static
+bool WasmScript::SetBreakPointOnFirstBreakableForFunction(
+ Handle<Script> script, int func_index, Handle<BreakPoint> break_point) {
+ if (func_index < 0) return false;
+ int offset_in_func = 0;
+
+ int breakable_offset = FindNextBreakablePosition(script->wasm_native_module(),
+ func_index, offset_in_func);
+ if (breakable_offset == 0) return false;
+ return WasmScript::SetBreakPointForFunction(script, func_index,
+ breakable_offset, break_point);
+}
+
+// static
+bool WasmScript::SetBreakPointForFunction(Handle<Script> script, int func_index,
+ int offset,
+ Handle<BreakPoint> break_point) {
+ Isolate* isolate = script->GetIsolate();
+
+ DCHECK_LE(0, func_index);
+ DCHECK_NE(0, offset);
+
+ // Find the function for this breakpoint.
+ wasm::NativeModule* native_module = script->wasm_native_module();
+ const wasm::WasmModule* module = native_module->module();
+ const wasm::WasmFunction& func = module->functions[func_index];
+
+ // Insert new break point into {wasm_breakpoint_infos} of the script.
+ WasmScript::AddBreakpointToInfo(script, func.code.offset() + offset,
+ break_point);
+
+ if (FLAG_debug_in_liftoff) {
+ native_module->GetDebugInfo()->SetBreakpoint(func_index, offset);
+ } 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);
+ }
+ }
+ }
+
+ return true;
+}
+
+// static
+bool WasmScript::ClearBreakPoint(Handle<Script> script, int position,
+ Handle<BreakPoint> break_point) {
+ Isolate* isolate = script->GetIsolate();
+
+ // 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();
+
+ if (!WasmScript::RemoveBreakpointFromInfo(script, position, break_point)) {
+ 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);
+ }
+ }
+
+ return true;
+}
+
+// static
+bool WasmScript::ClearBreakPointById(Handle<Script> script, int breakpoint_id) {
+ if (!script->has_wasm_breakpoint_infos()) {
+ return false;
+ }
+ Isolate* isolate = script->GetIsolate();
+ 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)) {
+ continue;
+ }
+ Handle<BreakPointInfo> breakpoint_info = Handle<BreakPointInfo>::cast(obj);
+ Handle<BreakPoint> breakpoint;
+ if (BreakPointInfo::GetBreakPointById(isolate, breakpoint_info,
+ breakpoint_id)
+ .ToHandle(&breakpoint)) {
+ DCHECK(breakpoint->id() == breakpoint_id);
+ return WasmScript::ClearBreakPoint(
+ script, breakpoint_info->source_position(), breakpoint);
+ }
+ }
+ 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;
+}
+
+} // namespace
+
+// static
+void WasmScript::AddBreakpointToInfo(Handle<Script> script, int position,
+ Handle<BreakPoint> break_point) {
+ Isolate* isolate = script->GetIsolate();
+ Handle<FixedArray> breakpoint_infos;
+ if (script->has_wasm_breakpoint_infos()) {
+ breakpoint_infos = handle(script->wasm_breakpoint_infos(), isolate);
+ } else {
+ breakpoint_infos =
+ isolate->factory()->NewFixedArray(4, AllocationType::kOld);
+ script->set_wasm_breakpoint_infos(*breakpoint_infos);
+ }
+
+ int insert_pos =
+ FindBreakpointInfoInsertPos(isolate, breakpoint_infos, position);
+
+ // If a BreakPointInfo object already exists for this position, add the new
+ // breakpoint object and return.
+ if (insert_pos < breakpoint_infos->length() &&
+ GetBreakpointPos(isolate, breakpoint_infos->get(insert_pos)) ==
+ position) {
+ Handle<BreakPointInfo> old_info(
+ BreakPointInfo::cast(breakpoint_infos->get(insert_pos)), isolate);
+ BreakPointInfo::SetBreakPoint(isolate, old_info, break_point);
+ return;
+ }
+
+ // Enlarge break positions array if necessary.
+ bool need_realloc = !breakpoint_infos->get(breakpoint_infos->length() - 1)
+ .IsUndefined(isolate);
+ Handle<FixedArray> new_breakpoint_infos = breakpoint_infos;
+ if (need_realloc) {
+ new_breakpoint_infos = isolate->factory()->NewFixedArray(
+ 2 * breakpoint_infos->length(), AllocationType::kOld);
+ script->set_wasm_breakpoint_infos(*new_breakpoint_infos);
+ // Copy over the entries [0, insert_pos).
+ for (int i = 0; i < insert_pos; ++i)
+ new_breakpoint_infos->set(i, breakpoint_infos->get(i));
+ }
+
+ // Move elements [insert_pos, ...] up by one.
+ for (int i = breakpoint_infos->length() - 1; i >= insert_pos; --i) {
+ Object entry = breakpoint_infos->get(i);
+ if (entry.IsUndefined(isolate)) continue;
+ new_breakpoint_infos->set(i + 1, entry);
+ }
+
+ // Generate new BreakpointInfo.
+ Handle<BreakPointInfo> breakpoint_info =
+ isolate->factory()->NewBreakPointInfo(position);
+ BreakPointInfo::SetBreakPoint(isolate, breakpoint_info, break_point);
+
+ // Now insert new position at insert_pos.
+ new_breakpoint_infos->set(insert_pos, *breakpoint_info);
+}
+
+// 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,
+ std::vector<v8::debug::BreakLocation>* locations) {
+ DisallowHeapAllocation no_gc;
+
+ const wasm::WasmModule* module = native_module->module();
+ const std::vector<wasm::WasmFunction>& functions = module->functions;
+
+ if (start.GetLineNumber() != 0 || start.GetColumnNumber() < 0 ||
+ (!end.IsEmpty() &&
+ (end.GetLineNumber() != 0 || end.GetColumnNumber() < 0 ||
+ end.GetColumnNumber() < start.GetColumnNumber())))
+ return false;
+
+ // start_func_index, start_offset and end_func_index is inclusive.
+ // end_offset is exclusive.
+ // start_offset and end_offset are module-relative byte offsets.
+ // We set strict to false because offsets may be between functions.
+ int start_func_index =
+ GetNearestWasmFunction(module, start.GetColumnNumber());
+ if (start_func_index < 0) return false;
+ uint32_t start_offset = start.GetColumnNumber();
+ int end_func_index;
+ uint32_t end_offset;
+
+ if (end.IsEmpty()) {
+ // Default: everything till the end of the Script.
+ end_func_index = static_cast<uint32_t>(functions.size() - 1);
+ end_offset = functions[end_func_index].code.end_offset();
+ } else {
+ // If end is specified: Use it and check for valid input.
+ end_offset = end.GetColumnNumber();
+ end_func_index = GetNearestWasmFunction(module, end_offset);
+ DCHECK_GE(end_func_index, start_func_index);
+ }
+
+ if (start_func_index == end_func_index &&
+ start_offset > functions[end_func_index].code.end_offset())
+ return false;
+ AccountingAllocator alloc;
+ Zone tmp(&alloc, ZONE_NAME);
+ const byte* module_start = native_module->wire_bytes().begin();
+
+ for (int func_idx = start_func_index; func_idx <= end_func_index;
+ ++func_idx) {
+ const wasm::WasmFunction& func = functions[func_idx];
+ if (func.code.length() == 0) continue;
+
+ wasm::BodyLocalDecls locals(&tmp);
+ wasm::BytecodeIterator iterator(module_start + func.code.offset(),
+ module_start + func.code.end_offset(),
+ &locals);
+ DCHECK_LT(0u, locals.encoded_size);
+ for (uint32_t offset : iterator.offsets()) {
+ uint32_t total_offset = func.code.offset() + offset;
+ if (total_offset >= end_offset) {
+ DCHECK_EQ(end_func_index, func_idx);
+ break;
+ }
+ if (total_offset < start_offset) continue;
+ locations->emplace_back(0, total_offset, debug::kCommonBreakLocation);
+ }
+ }
+ return true;
+}
+
+// static
+MaybeHandle<FixedArray> WasmScript::CheckBreakPoints(Isolate* isolate,
+ Handle<Script> script,
+ int position) {
+ if (!script->has_wasm_breakpoint_infos()) return {};
+
+ Handle<FixedArray> breakpoint_infos(script->wasm_breakpoint_infos(), isolate);
+ int insert_pos =
+ FindBreakpointInfoInsertPos(isolate, breakpoint_infos, position);
+ if (insert_pos >= breakpoint_infos->length()) return {};
+
+ Handle<Object> maybe_breakpoint_info(breakpoint_infos->get(insert_pos),
+ isolate);
+ if (maybe_breakpoint_info->IsUndefined(isolate)) return {};
+ Handle<BreakPointInfo> breakpoint_info =
+ Handle<BreakPointInfo>::cast(maybe_breakpoint_info);
+ if (breakpoint_info->source_position() != position) return {};
+
+ // There is no support for conditional break points. Just assume that every
+ // break point always hits.
+ Handle<Object> break_points(breakpoint_info->break_points(), isolate);
+ if (break_points->IsFixedArray()) {
+ return Handle<FixedArray>::cast(break_points);
+ }
+ Handle<FixedArray> break_points_hit = isolate->factory()->NewFixedArray(1);
+ break_points_hit->set(0, *break_points);
+ return break_points_hit;
+}
+
} // namespace internal
} // namespace v8