#include "src/debug/wasm/gdb-server/wasm-module-debug.h"
#include "src/api/api-inl.h"
#include "src/api/api.h"
#include "src/debug/debug.h"
#include "src/execution/frames-inl.h"
#include "src/execution/frames.h"
#include "src/objects/script.h"
#include "src/wasm/constant-expression.h"
#include "src/wasm/module-instantiate.h"
#include "src/wasm/wasm-debug.h"
#include "src/wasm/wasm-opcodes.h"
#include "src/wasm/wasm-value.h"
#include "src/zone/zone.h"
namespace v8 {
namespace internal {
namespace wasm {
namespace gdb_server {
WasmModuleDebug::WasmModuleDebug(v8::Isolate* isolate,
Local<debug::WasmScript> wasm_script) {
DCHECK_EQ(Script::Type::kWasm, Utils::OpenHandle(*wasm_script)->type());
isolate_ = isolate;
wasm_script_ = Global<debug::WasmScript>(isolate, wasm_script);
}
std::string WasmModuleDebug::GetModuleName() const {
v8::Local<debug::WasmScript> wasm_script = wasm_script_.Get(isolate_);
v8::Local<v8::String> name;
std::string module_name;
if (wasm_script->Name().ToLocal(&name)) {
module_name = *(v8::String::Utf8Value(isolate_, name));
}
return module_name;
}
Handle<WasmInstanceObject> WasmModuleDebug::GetFirstWasmInstance() {
v8::Local<debug::WasmScript> wasm_script = wasm_script_.Get(isolate_);
Handle<Script> script = Utils::OpenHandle(*wasm_script);
Handle<WeakArrayList> weak_instance_list(script->wasm_weak_instance_list(),
GetIsolate());
if (weak_instance_list->length() > 0) {
Tagged<MaybeObject> maybe_instance = weak_instance_list->Get(0);
if (maybe_instance.IsWeak()) {
Handle<WasmInstanceObject> instance(
Cast<WasmInstanceObject>(maybe_instance.GetHeapObjectAssumeWeak()),
GetIsolate());
return instance;
}
}
return Handle<WasmInstanceObject>::null();
}
int GetLEB128Size(base::Vector<const uint8_t> module_bytes, int offset) {
int index = offset;
while (module_bytes[index] & 0x80) index++;
return index + 1 - offset;
}
int ReturnPc(const NativeModule* native_module, int pc) {
base::Vector<const uint8_t> wire_bytes = native_module->wire_bytes();
uint8_t opcode = wire_bytes[pc];
switch (opcode) {
case kExprCallFunction: {
pc++;
return pc + GetLEB128Size(wire_bytes, pc);
}
case kExprCallIndirect: {
pc++;
pc += GetLEB128Size(wire_bytes, pc);
return pc + GetLEB128Size(wire_bytes, pc);
}
default:
UNREACHABLE();
}
}
std::vector<wasm_addr_t> WasmModuleDebug::GetCallStack(
uint32_t debug_context_id, Isolate* isolate) {
std::vector<wasm_addr_t> call_stack;
for (StackFrameIterator frame_it(isolate); !frame_it.done();
frame_it.Advance()) {
StackFrame* const frame = frame_it.frame();
switch (frame->type()) {
case StackFrame::JAVASCRIPT_BUILTIN_CONTINUATION:
case StackFrame::JAVASCRIPT_BUILTIN_CONTINUATION_WITH_CATCH:
case StackFrame::INTERPRETED:
case StackFrame::BASELINE:
case StackFrame::MAGLEV:
case StackFrame::TURBOFAN_JS:
case StackFrame::BUILTIN:
case StackFrame::WASM: {
FrameSummaries summaries = CommonFrame::cast(frame)->Summarize();
for (size_t i = summaries.size(); i-- != 0;) {
int offset = 0;
Handle<Script> script;
auto& summary = summaries.frames[i];
if (summary.IsJavaScript()) {
FrameSummary::JavaScriptFrameSummary const& javascript =
summary.AsJavaScript();
offset = javascript.code_offset();
script = Cast<Script>(javascript.script());
} else if (summary.IsWasm()) {
FrameSummary::WasmFrameSummary const& wasm = summary.AsWasm();
offset = GetWasmFunctionOffset(wasm.wasm_instance()->module(),
wasm.function_index()) +
wasm.code_offset();
script = wasm.script();
bool zeroth_frame = call_stack.empty();
if (!zeroth_frame) {
const NativeModule* native_module =
wasm.wasm_instance()->module_object()->native_module();
offset = ReturnPc(native_module, offset);
}
}
if (offset > 0) {
call_stack.push_back(
{debug_context_id << 16 | script->id(), uint32_t(offset)});
}
}
break;
}
case StackFrame::BUILTIN_EXIT:
default:
break;
}
}
if (call_stack.empty()) call_stack.push_back({1, 0});
return call_stack;
}
std::vector<FrameSummary> WasmModuleDebug::FindWasmFrame(
DebuggableStackFrameIterator* frame_it, uint32_t* frame_index) {
while (!frame_it->done()) {
StackFrame* const frame = frame_it->frame();
switch (frame->type()) {
case StackFrame::JAVASCRIPT_BUILTIN_CONTINUATION:
case StackFrame::JAVASCRIPT_BUILTIN_CONTINUATION_WITH_CATCH:
case StackFrame::INTERPRETED:
case StackFrame::BASELINE:
case StackFrame::MAGLEV:
case StackFrame::TURBOFAN_JS:
case StackFrame::BUILTIN:
case StackFrame::WASM: {
FrameSummaries summaries = CommonFrame::cast(frame)->Summarize();
const size_t frame_count = summaries.size();
DCHECK_GT(frame_count, 0);
if (frame_count > *frame_index) {
#if V8_ENABLE_DRUMBRAKE
if (frame_it->is_wasm() && !frame_it->is_wasm_interpreter_entry())
#else
if (frame_it->is_wasm())
#endif
return summaries.frames;
else
return {};
} else {
*frame_index -= frame_count;
frame_it->Advance();
}
break;
}
case StackFrame::BUILTIN_EXIT:
default:
break;
}
}
return {};
}
DirectHandle<WasmInstanceObject> WasmModuleDebug::GetWasmInstance(
Isolate* isolate, uint32_t frame_index) {
DebuggableStackFrameIterator frame_it(isolate);
std::vector<FrameSummary> frames = FindWasmFrame(&frame_it, &frame_index);
if (frames.empty()) {
return DirectHandle<WasmInstanceObject>::null();
}
int reversed_index = static_cast<int>(frames.size() - 1 - frame_index);
const FrameSummary::WasmFrameSummary& summary =
frames[reversed_index].AsWasm();
return summary.wasm_instance();
}
bool WasmModuleDebug::GetWasmGlobal(Isolate* isolate, uint32_t frame_index,
uint32_t index, uint8_t* buffer,
uint32_t buffer_size, uint32_t* size) {
HandleScope handles(isolate);
DirectHandle<WasmInstanceObject> instance =
GetWasmInstance(isolate, frame_index);
if (!instance.is_null()) {
Handle<WasmModuleObject> module_object(instance->module_object(), isolate);
const wasm::WasmModule* module = module_object->native_module()->module();
if (index < module->globals.size()) {
DirectHandle<WasmTrustedInstanceData> trusted_data(
instance->trusted_data(isolate), isolate);
wasm::WasmValue wasm_value =
trusted_data->GetGlobalValue(isolate, module->globals[index]);
return GetWasmValue(wasm_value, buffer, buffer_size, size);
}
}
return false;
}
bool WasmModuleDebug::GetWasmLocal(Isolate* isolate, uint32_t frame_index,
uint32_t index, uint8_t* buffer,
uint32_t buffer_size, uint32_t* size) {
HandleScope handles(isolate);
DebuggableStackFrameIterator frame_it(isolate);
std::vector<FrameSummary> frames = FindWasmFrame(&frame_it, &frame_index);
if (frames.empty()) {
return false;
}
int reversed_index = static_cast<int>(frames.size() - 1 - frame_index);
const FrameSummary& summary = frames[reversed_index];
if (summary.IsWasm()) {
DirectHandle<WasmInstanceObject> instance =
summary.AsWasm().wasm_instance();
if (!instance.is_null()) {
Handle<WasmModuleObject> module_object(instance->module_object(),
isolate);
wasm::NativeModule* native_module = module_object->native_module();
DebugInfo* debug_info = native_module->GetDebugInfo();
if (static_cast<uint32_t>(debug_info->GetNumLocals(frame_it.frame()->pc(),
isolate)) > index) {
wasm::WasmValue wasm_value = debug_info->GetLocalValue(
index, frame_it.frame()->pc(), frame_it.frame()->fp(),
frame_it.frame()->callee_fp(), isolate);
return GetWasmValue(wasm_value, buffer, buffer_size, size);
}
}
}
return false;
}
bool WasmModuleDebug::GetWasmStackValue(Isolate* isolate, uint32_t frame_index,
uint32_t index, uint8_t* buffer,
uint32_t buffer_size, uint32_t* size) {
HandleScope handles(isolate);
DebuggableStackFrameIterator frame_it(isolate);
std::vector<FrameSummary> frames = FindWasmFrame(&frame_it, &frame_index);
if (frames.empty()) {
return false;
}
int reversed_index = static_cast<int>(frames.size() - 1 - frame_index);
const FrameSummary& summary = frames[reversed_index];
if (summary.IsWasm()) {
DirectHandle<WasmInstanceObject> instance =
summary.AsWasm().wasm_instance();
if (!instance.is_null()) {
Handle<WasmModuleObject> module_object(instance->module_object(),
isolate);
wasm::NativeModule* native_module = module_object->native_module();
DebugInfo* debug_info = native_module->GetDebugInfo();
if (static_cast<uint32_t>(debug_info->GetStackDepth(
frame_it.frame()->pc(), isolate)) > index) {
WasmValue wasm_value = debug_info->GetStackValue(
index, frame_it.frame()->pc(), frame_it.frame()->fp(),
frame_it.frame()->callee_fp(), isolate);
return GetWasmValue(wasm_value, buffer, buffer_size, size);
}
}
}
return false;
}
uint32_t WasmModuleDebug::GetWasmMemory(Isolate* isolate, uint32_t offset,
uint8_t* buffer, uint32_t size) {
HandleScope handles(isolate);
uint32_t bytes_read = 0;
Handle<WasmInstanceObject> instance = GetFirstWasmInstance();
if (!instance.is_null()) {
uint8_t* mem_start = instance->trusted_data(isolate)->memory0_start();
size_t mem_size = instance->trusted_data(isolate)->memory0_size();
if (static_cast<uint64_t>(offset) + size <= mem_size) {
memcpy(buffer, mem_start + offset, size);
bytes_read = size;
} else if (offset < mem_size) {
bytes_read = static_cast<uint32_t>(mem_size) - offset;
memcpy(buffer, mem_start + offset, bytes_read);
}
}
return bytes_read;
}
uint32_t WasmModuleDebug::GetWasmData(Zone* zone, Isolate* isolate,
uint32_t offset, uint8_t* buffer,
uint32_t size) {
HandleScope handles(isolate);
uint32_t bytes_read = 0;
Handle<WasmInstanceObject> instance = GetFirstWasmInstance();
if (!instance.is_null()) {
DirectHandle<WasmTrustedInstanceData> trusted_data(
instance->trusted_data(isolate), isolate);
DirectHandle<WasmTrustedInstanceData> shared_data(
trusted_data->shared_part(), isolate);
Handle<WasmModuleObject> module_object(instance->module_object(), isolate);
const wasm::WasmModule* module = module_object->native_module()->module();
if (!module->data_segments.empty()) {
const WasmDataSegment& segment = module->data_segments[0];
wasm::ValueOrError result = wasm::EvaluateConstantExpression(
zone, segment.dest_addr, wasm::kWasmI32, module, isolate,
trusted_data, shared_data);
if (!wasm::is_error(result)) {
uint32_t data_offset = wasm::to_value(result).to_u32();
offset += data_offset;
uint8_t* mem_start = trusted_data->memory0_start();
size_t mem_size = trusted_data->memory0_size();
if (static_cast<uint64_t>(offset) + size <= mem_size) {
memcpy(buffer, mem_start + offset, size);
bytes_read = size;
} else if (offset < mem_size) {
bytes_read = static_cast<uint32_t>(mem_size) - offset;
memcpy(buffer, mem_start + offset, bytes_read);
}
}
}
}
return bytes_read;
}
uint32_t WasmModuleDebug::GetWasmModuleBytes(wasm_addr_t wasm_addr,
uint8_t* buffer, uint32_t size) {
uint32_t bytes_read = 0;
Handle<WasmInstanceObject> instance = GetFirstWasmInstance();
if (!instance.is_null()) {
Handle<WasmModuleObject> module_object(instance->module_object(),
GetIsolate());
wasm::NativeModule* native_module = module_object->native_module();
const wasm::ModuleWireBytes wire_bytes(native_module->wire_bytes());
uint32_t offset = wasm_addr.Offset();
if (offset < wire_bytes.length()) {
uint32_t module_size = static_cast<uint32_t>(wire_bytes.length());
bytes_read = module_size - offset >= size ? size : module_size - offset;
memcpy(buffer, wire_bytes.start() + offset, bytes_read);
}
}
return bytes_read;
}
bool WasmModuleDebug::AddBreakpoint(uint32_t offset, int* breakpoint_id) {
v8::Local<debug::WasmScript> wasm_script = wasm_script_.Get(isolate_);
Handle<Script> script = Utils::OpenHandle(*wasm_script);
Handle<String> condition = GetIsolate()->factory()->empty_string();
int breakpoint_address = static_cast<int>(offset);
return GetIsolate()->debug()->SetBreakPointForScript(
script, condition, &breakpoint_address, breakpoint_id);
}
void WasmModuleDebug::RemoveBreakpoint(uint32_t offset, int breakpoint_id) {
v8::Local<debug::WasmScript> wasm_script = wasm_script_.Get(isolate_);
Handle<Script> script = Utils::OpenHandle(*wasm_script);
GetIsolate()->debug()->RemoveBreakpointForWasmScript(script, breakpoint_id);
}
void WasmModuleDebug::PrepareStep() {
i::Isolate* isolate = GetIsolate();
DebugScope debug_scope(isolate->debug());
debug::PrepareStep(reinterpret_cast<v8::Isolate*>(isolate),
debug::StepAction::StepInto);
}
template <typename T>
bool StoreValue(const T& value, uint8_t* buffer, uint32_t buffer_size,
uint32_t* size) {
*size = sizeof(value);
if (*size > buffer_size) return false;
memcpy(buffer, &value, *size);
return true;
}
bool WasmModuleDebug::GetWasmValue(const wasm::WasmValue& wasm_value,
uint8_t* buffer, uint32_t buffer_size,
uint32_t* size) {
switch (wasm_value.type().kind()) {
case wasm::kI32:
return StoreValue(wasm_value.to_i32(), buffer, buffer_size, size);
case wasm::kI64:
return StoreValue(wasm_value.to_i64(), buffer, buffer_size, size);
case wasm::kF32:
return StoreValue(wasm_value.to_f32(), buffer, buffer_size, size);
case wasm::kF64:
return StoreValue(wasm_value.to_f64(), buffer, buffer_size, size);
case wasm::kS128:
return StoreValue(wasm_value.to_s128(), buffer, buffer_size, size);
case wasm::kRef:
case wasm::kRefNull:
case wasm::kVoid:
case wasm::kBottom:
return false;
default:
return false;
}
}
}
}
}
}