blob: f0b77bc0965a8abad4136a5bc69ab558b63fb698 [file] [log] [blame]
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/debug/wasm/gdb-server/wasm-module-debug.h"
#include "src/api/api-inl.h"
#include "src/api/api.h"
#include "src/execution/frames-inl.h"
#include "src/execution/frames.h"
#include "src/objects/script.h"
#include "src/wasm/wasm-debug.h"
#include "src/wasm/wasm-value.h"
namespace v8 {
namespace internal {
namespace wasm {
namespace gdb_server {
WasmModuleDebug::WasmModuleDebug(v8::Isolate* isolate,
Local<debug::WasmScript> wasm_script) {
DCHECK_EQ(Script::TYPE_WASM, 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) {
MaybeObject maybe_instance = weak_instance_list->Get(0);
if (maybe_instance->IsWeak()) {
Handle<WasmInstanceObject> instance(
WasmInstanceObject::cast(maybe_instance->GetHeapObjectAssumeWeak()),
GetIsolate());
return instance;
}
}
return Handle<WasmInstanceObject>::null();
}
int GetLEB128Size(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) {
Vector<const uint8_t> wire_bytes = native_module->wire_bytes();
uint8_t opcode = wire_bytes[pc];
switch (opcode) {
case kExprCallFunction: {
// skip opcode
pc++;
// skip function index
return pc + GetLEB128Size(wire_bytes, pc);
}
case kExprCallIndirect: {
// skip opcode
pc++;
// skip signature index
pc += GetLEB128Size(wire_bytes, pc);
// skip table index
return pc + GetLEB128Size(wire_bytes, pc);
}
default:
UNREACHABLE();
}
}
// static
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::JAVA_SCRIPT_BUILTIN_CONTINUATION:
case StackFrame::JAVA_SCRIPT_BUILTIN_CONTINUATION_WITH_CATCH:
case StackFrame::OPTIMIZED:
case StackFrame::INTERPRETED:
case StackFrame::BUILTIN:
case StackFrame::WASM: {
// A standard frame may include many summarized frames, due to inlining.
std::vector<FrameSummary> frames;
CommonFrame::cast(frame)->Summarize(&frames);
for (size_t i = frames.size(); i-- != 0;) {
int offset = 0;
Handle<Script> script;
auto& summary = frames[i];
if (summary.IsJavaScript()) {
FrameSummary::JavaScriptFrameSummary const& java_script =
summary.AsJavaScript();
offset = java_script.code_offset();
script = Handle<Script>::cast(java_script.script());
} else if (summary.IsWasm()) {
FrameSummary::WasmFrameSummary const& wasm = summary.AsWasm();
offset = GetWasmFunctionOffset(wasm.wasm_instance()->module(),
wasm.function_index()) +
wasm.byte_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:
// ignore the frame.
break;
}
}
if (call_stack.empty()) call_stack.push_back({1, 0});
return call_stack;
}
// static
std::vector<FrameSummary> WasmModuleDebug::FindWasmFrame(
StackTraceFrameIterator* frame_it, uint32_t* frame_index) {
while (!frame_it->done()) {
StackFrame* const frame = frame_it->frame();
switch (frame->type()) {
case StackFrame::JAVA_SCRIPT_BUILTIN_CONTINUATION:
case StackFrame::JAVA_SCRIPT_BUILTIN_CONTINUATION_WITH_CATCH:
case StackFrame::OPTIMIZED:
case StackFrame::INTERPRETED:
case StackFrame::BUILTIN:
case StackFrame::WASM: {
// A standard frame may include many summarized frames, due to inlining.
std::vector<FrameSummary> frames;
CommonFrame::cast(frame)->Summarize(&frames);
const size_t frame_count = frames.size();
DCHECK_GT(frame_count, 0);
if (frame_count > *frame_index) {
if (frame_it->is_wasm())
return frames;
else
return {};
} else {
*frame_index -= frame_count;
frame_it->Advance();
}
break;
}
case StackFrame::BUILTIN_EXIT:
default:
// ignore the frame.
break;
}
}
return {};
}
// static
Handle<WasmInstanceObject> WasmModuleDebug::GetWasmInstance(
Isolate* isolate, uint32_t frame_index) {
StackTraceFrameIterator frame_it(isolate);
std::vector<FrameSummary> frames = FindWasmFrame(&frame_it, &frame_index);
if (frames.empty()) {
return Handle<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();
}
// static
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);
Handle<WasmInstanceObject> instance = GetWasmInstance(isolate, frame_index);
if (!instance.is_null()) {
Handle<WasmModuleObject> module_object(instance->module_object(), isolate);
const wasm::WasmModule* module = module_object->module();
if (index < module->globals.size()) {
wasm::WasmValue wasm_value =
WasmInstanceObject::GetGlobalValue(instance, module->globals[index]);
return GetWasmValue(wasm_value, buffer, buffer_size, size);
}
}
return false;
}
// static
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);
StackTraceFrameIterator 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()) {
Handle<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(
isolate, frame_it.frame()->pc())) > index) {
wasm::WasmValue wasm_value = debug_info->GetLocalValue(
index, isolate, frame_it.frame()->pc(), frame_it.frame()->fp(),
frame_it.frame()->callee_fp());
return GetWasmValue(wasm_value, buffer, buffer_size, size);
}
}
}
return false;
}
// static
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);
StackTraceFrameIterator 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()) {
Handle<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(
isolate, frame_it.frame()->pc())) > index) {
WasmValue wasm_value = debug_info->GetStackValue(
index, isolate, frame_it.frame()->pc(), frame_it.frame()->fp(),
frame_it.frame()->callee_fp());
return GetWasmValue(wasm_value, buffer, buffer_size, size);
}
}
}
return false;
}
// static
uint32_t WasmModuleDebug::GetWasmMemory(Isolate* isolate, uint32_t frame_index,
uint32_t offset, uint8_t* buffer,
uint32_t size) {
HandleScope handles(isolate);
uint32_t bytes_read = 0;
Handle<WasmInstanceObject> instance = GetWasmInstance(isolate, frame_index);
if (!instance.is_null()) {
uint8_t* mem_start = instance->memory_start();
size_t mem_size = instance->memory_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;
// Any instance will work.
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::StepIn);
}
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;
}
// static
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::kWasmI32.kind():
return StoreValue(wasm_value.to_i32(), buffer, buffer_size, size);
case wasm::kWasmI64.kind():
return StoreValue(wasm_value.to_i64(), buffer, buffer_size, size);
case wasm::kWasmF32.kind():
return StoreValue(wasm_value.to_f32(), buffer, buffer_size, size);
case wasm::kWasmF64.kind():
return StoreValue(wasm_value.to_f64(), buffer, buffer_size, size);
case wasm::kWasmS128.kind():
return StoreValue(wasm_value.to_s128(), buffer, buffer_size, size);
case wasm::kWasmStmt.kind():
case wasm::kWasmExternRef.kind():
case wasm::kWasmFuncRef.kind():
case wasm::kWasmExnRef.kind():
case wasm::kWasmBottom.kind():
default:
// Not supported
return false;
}
}
} // namespace gdb_server
} // namespace wasm
} // namespace internal
} // namespace v8