1// Copyright 2020 the V8 project authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "src/debug/wasm/gdb-server/wasm-module-debug.h" 6 7#include "src/api/api-inl.h" 8#include "src/api/api.h" 9#include "src/base/platform/wrappers.h" 10#include "src/execution/frames-inl.h" 11#include "src/execution/frames.h" 12#include "src/objects/script.h" 13#include "src/wasm/module-instantiate.h" 14#include "src/wasm/wasm-debug.h" 15#include "src/wasm/wasm-value.h" 16 17namespace v8 { 18namespace internal { 19namespace wasm { 20namespace gdb_server { 21 22WasmModuleDebug::WasmModuleDebug(v8::Isolate* isolate, 23 Local<debug::WasmScript> wasm_script) { 24 DCHECK_EQ(Script::TYPE_WASM, Utils::OpenHandle(*wasm_script)->type()); 25 26 isolate_ = isolate; 27 wasm_script_ = Global<debug::WasmScript>(isolate, wasm_script); 28} 29 30std::string WasmModuleDebug::GetModuleName() const { 31 v8::Local<debug::WasmScript> wasm_script = wasm_script_.Get(isolate_); 32 v8::Local<v8::String> name; 33 std::string module_name; 34 if (wasm_script->Name().ToLocal(&name)) { 35 module_name = *(v8::String::Utf8Value(isolate_, name)); 36 } 37 return module_name; 38} 39 40Handle<WasmInstanceObject> WasmModuleDebug::GetFirstWasmInstance() { 41 v8::Local<debug::WasmScript> wasm_script = wasm_script_.Get(isolate_); 42 Handle<Script> script = Utils::OpenHandle(*wasm_script); 43 44 Handle<WeakArrayList> weak_instance_list(script->wasm_weak_instance_list(), 45 GetIsolate()); 46 if (weak_instance_list->length() > 0) { 47 MaybeObject maybe_instance = weak_instance_list->Get(0); 48 if (maybe_instance->IsWeak()) { 49 Handle<WasmInstanceObject> instance( 50 WasmInstanceObject::cast(maybe_instance->GetHeapObjectAssumeWeak()), 51 GetIsolate()); 52 return instance; 53 } 54 } 55 return Handle<WasmInstanceObject>::null(); 56} 57 58int GetLEB128Size(base::Vector<const uint8_t> module_bytes, int offset) { 59 int index = offset; 60 while (module_bytes[index] & 0x80) index++; 61 return index + 1 - offset; 62} 63 64int ReturnPc(const NativeModule* native_module, int pc) { 65 base::Vector<const uint8_t> wire_bytes = native_module->wire_bytes(); 66 uint8_t opcode = wire_bytes[pc]; 67 switch (opcode) { 68 case kExprCallFunction: { 69 // skip opcode 70 pc++; 71 // skip function index 72 return pc + GetLEB128Size(wire_bytes, pc); 73 } 74 case kExprCallIndirect: { 75 // skip opcode 76 pc++; 77 // skip signature index 78 pc += GetLEB128Size(wire_bytes, pc); 79 // skip table index 80 return pc + GetLEB128Size(wire_bytes, pc); 81 } 82 default: 83 UNREACHABLE(); 84 } 85} 86 87// static 88std::vector<wasm_addr_t> WasmModuleDebug::GetCallStack( 89 uint32_t debug_context_id, Isolate* isolate) { 90 std::vector<wasm_addr_t> call_stack; 91 for (StackFrameIterator frame_it(isolate); !frame_it.done(); 92 frame_it.Advance()) { 93 StackFrame* const frame = frame_it.frame(); 94 switch (frame->type()) { 95 case StackFrame::JAVA_SCRIPT_BUILTIN_CONTINUATION: 96 case StackFrame::JAVA_SCRIPT_BUILTIN_CONTINUATION_WITH_CATCH: 97 case StackFrame::OPTIMIZED: 98 case StackFrame::INTERPRETED: 99 case StackFrame::BASELINE: 100 case StackFrame::BUILTIN: 101 case StackFrame::WASM: { 102 // A standard frame may include many summarized frames, due to inlining. 103 std::vector<FrameSummary> frames; 104 CommonFrame::cast(frame)->Summarize(&frames); 105 for (size_t i = frames.size(); i-- != 0;) { 106 int offset = 0; 107 Handle<Script> script; 108 109 auto& summary = frames[i]; 110 if (summary.IsJavaScript()) { 111 FrameSummary::JavaScriptFrameSummary const& java_script = 112 summary.AsJavaScript(); 113 offset = java_script.code_offset(); 114 script = Handle<Script>::cast(java_script.script()); 115 } else if (summary.IsWasm()) { 116 FrameSummary::WasmFrameSummary const& wasm = summary.AsWasm(); 117 offset = GetWasmFunctionOffset(wasm.wasm_instance()->module(), 118 wasm.function_index()) + 119 wasm.byte_offset(); 120 script = wasm.script(); 121 122 bool zeroth_frame = call_stack.empty(); 123 if (!zeroth_frame) { 124 const NativeModule* native_module = 125 wasm.wasm_instance()->module_object().native_module(); 126 offset = ReturnPc(native_module, offset); 127 } 128 } 129 130 if (offset > 0) { 131 call_stack.push_back( 132 {debug_context_id << 16 | script->id(), uint32_t(offset)}); 133 } 134 } 135 break; 136 } 137 138 case StackFrame::BUILTIN_EXIT: 139 default: 140 // ignore the frame. 141 break; 142 } 143 } 144 if (call_stack.empty()) call_stack.push_back({1, 0}); 145 return call_stack; 146} 147 148// static 149std::vector<FrameSummary> WasmModuleDebug::FindWasmFrame( 150 StackTraceFrameIterator* frame_it, uint32_t* frame_index) { 151 while (!frame_it->done()) { 152 StackFrame* const frame = frame_it->frame(); 153 switch (frame->type()) { 154 case StackFrame::JAVA_SCRIPT_BUILTIN_CONTINUATION: 155 case StackFrame::JAVA_SCRIPT_BUILTIN_CONTINUATION_WITH_CATCH: 156 case StackFrame::OPTIMIZED: 157 case StackFrame::INTERPRETED: 158 case StackFrame::BASELINE: 159 case StackFrame::BUILTIN: 160 case StackFrame::WASM: { 161 // A standard frame may include many summarized frames, due to inlining. 162 std::vector<FrameSummary> frames; 163 CommonFrame::cast(frame)->Summarize(&frames); 164 const size_t frame_count = frames.size(); 165 DCHECK_GT(frame_count, 0); 166 167 if (frame_count > *frame_index) { 168 if (frame_it->is_wasm()) 169 return frames; 170 else 171 return {}; 172 } else { 173 *frame_index -= frame_count; 174 frame_it->Advance(); 175 } 176 break; 177 } 178 179 case StackFrame::BUILTIN_EXIT: 180 default: 181 // ignore the frame. 182 break; 183 } 184 } 185 return {}; 186} 187 188// static 189Handle<WasmInstanceObject> WasmModuleDebug::GetWasmInstance( 190 Isolate* isolate, uint32_t frame_index) { 191 StackTraceFrameIterator frame_it(isolate); 192 std::vector<FrameSummary> frames = FindWasmFrame(&frame_it, &frame_index); 193 if (frames.empty()) { 194 return Handle<WasmInstanceObject>::null(); 195 } 196 197 int reversed_index = static_cast<int>(frames.size() - 1 - frame_index); 198 const FrameSummary::WasmFrameSummary& summary = 199 frames[reversed_index].AsWasm(); 200 return summary.wasm_instance(); 201} 202 203// static 204bool WasmModuleDebug::GetWasmGlobal(Isolate* isolate, uint32_t frame_index, 205 uint32_t index, uint8_t* buffer, 206 uint32_t buffer_size, uint32_t* size) { 207 HandleScope handles(isolate); 208 209 Handle<WasmInstanceObject> instance = GetWasmInstance(isolate, frame_index); 210 if (!instance.is_null()) { 211 Handle<WasmModuleObject> module_object(instance->module_object(), isolate); 212 const wasm::WasmModule* module = module_object->module(); 213 if (index < module->globals.size()) { 214 wasm::WasmValue wasm_value = 215 WasmInstanceObject::GetGlobalValue(instance, module->globals[index]); 216 return GetWasmValue(wasm_value, buffer, buffer_size, size); 217 } 218 } 219 return false; 220} 221 222// static 223bool WasmModuleDebug::GetWasmLocal(Isolate* isolate, uint32_t frame_index, 224 uint32_t index, uint8_t* buffer, 225 uint32_t buffer_size, uint32_t* size) { 226 HandleScope handles(isolate); 227 228 StackTraceFrameIterator frame_it(isolate); 229 std::vector<FrameSummary> frames = FindWasmFrame(&frame_it, &frame_index); 230 if (frames.empty()) { 231 return false; 232 } 233 234 int reversed_index = static_cast<int>(frames.size() - 1 - frame_index); 235 const FrameSummary& summary = frames[reversed_index]; 236 if (summary.IsWasm()) { 237 Handle<WasmInstanceObject> instance = summary.AsWasm().wasm_instance(); 238 if (!instance.is_null()) { 239 Handle<WasmModuleObject> module_object(instance->module_object(), 240 isolate); 241 wasm::NativeModule* native_module = module_object->native_module(); 242 DebugInfo* debug_info = native_module->GetDebugInfo(); 243 if (static_cast<uint32_t>( 244 debug_info->GetNumLocals(frame_it.frame()->pc())) > index) { 245 wasm::WasmValue wasm_value = debug_info->GetLocalValue( 246 index, frame_it.frame()->pc(), frame_it.frame()->fp(), 247 frame_it.frame()->callee_fp()); 248 return GetWasmValue(wasm_value, buffer, buffer_size, size); 249 } 250 } 251 } 252 return false; 253} 254 255// static 256bool WasmModuleDebug::GetWasmStackValue(Isolate* isolate, uint32_t frame_index, 257 uint32_t index, uint8_t* buffer, 258 uint32_t buffer_size, uint32_t* size) { 259 HandleScope handles(isolate); 260 261 StackTraceFrameIterator frame_it(isolate); 262 std::vector<FrameSummary> frames = FindWasmFrame(&frame_it, &frame_index); 263 if (frames.empty()) { 264 return false; 265 } 266 267 int reversed_index = static_cast<int>(frames.size() - 1 - frame_index); 268 const FrameSummary& summary = frames[reversed_index]; 269 if (summary.IsWasm()) { 270 Handle<WasmInstanceObject> instance = summary.AsWasm().wasm_instance(); 271 if (!instance.is_null()) { 272 Handle<WasmModuleObject> module_object(instance->module_object(), 273 isolate); 274 wasm::NativeModule* native_module = module_object->native_module(); 275 DebugInfo* debug_info = native_module->GetDebugInfo(); 276 if (static_cast<uint32_t>( 277 debug_info->GetStackDepth(frame_it.frame()->pc())) > index) { 278 WasmValue wasm_value = debug_info->GetStackValue( 279 index, frame_it.frame()->pc(), frame_it.frame()->fp(), 280 frame_it.frame()->callee_fp()); 281 return GetWasmValue(wasm_value, buffer, buffer_size, size); 282 } 283 } 284 } 285 return false; 286} 287 288uint32_t WasmModuleDebug::GetWasmMemory(Isolate* isolate, uint32_t offset, 289 uint8_t* buffer, uint32_t size) { 290 HandleScope handles(isolate); 291 292 uint32_t bytes_read = 0; 293 Handle<WasmInstanceObject> instance = GetFirstWasmInstance(); 294 if (!instance.is_null()) { 295 uint8_t* mem_start = instance->memory_start(); 296 size_t mem_size = instance->memory_size(); 297 if (static_cast<uint64_t>(offset) + size <= mem_size) { 298 memcpy(buffer, mem_start + offset, size); 299 bytes_read = size; 300 } else if (offset < mem_size) { 301 bytes_read = static_cast<uint32_t>(mem_size) - offset; 302 memcpy(buffer, mem_start + offset, bytes_read); 303 } 304 } 305 return bytes_read; 306} 307 308uint32_t WasmModuleDebug::GetWasmData(Isolate* isolate, uint32_t offset, 309 uint8_t* buffer, uint32_t size) { 310 HandleScope handles(isolate); 311 312 uint32_t bytes_read = 0; 313 Handle<WasmInstanceObject> instance = GetFirstWasmInstance(); 314 if (!instance.is_null()) { 315 Handle<WasmModuleObject> module_object(instance->module_object(), isolate); 316 const wasm::WasmModule* module = module_object->module(); 317 if (!module->data_segments.empty()) { 318 const WasmDataSegment& segment = module->data_segments[0]; 319 uint32_t data_offset = EvalUint32InitExpr(instance, segment.dest_addr); 320 offset += data_offset; 321 322 uint8_t* mem_start = instance->memory_start(); 323 size_t mem_size = instance->memory_size(); 324 if (static_cast<uint64_t>(offset) + size <= mem_size) { 325 memcpy(buffer, mem_start + offset, size); 326 bytes_read = size; 327 } else if (offset < mem_size) { 328 bytes_read = static_cast<uint32_t>(mem_size) - offset; 329 memcpy(buffer, mem_start + offset, bytes_read); 330 } 331 } 332 } 333 return bytes_read; 334} 335 336uint32_t WasmModuleDebug::GetWasmModuleBytes(wasm_addr_t wasm_addr, 337 uint8_t* buffer, uint32_t size) { 338 uint32_t bytes_read = 0; 339 // Any instance will work. 340 Handle<WasmInstanceObject> instance = GetFirstWasmInstance(); 341 if (!instance.is_null()) { 342 Handle<WasmModuleObject> module_object(instance->module_object(), 343 GetIsolate()); 344 wasm::NativeModule* native_module = module_object->native_module(); 345 const wasm::ModuleWireBytes wire_bytes(native_module->wire_bytes()); 346 uint32_t offset = wasm_addr.Offset(); 347 if (offset < wire_bytes.length()) { 348 uint32_t module_size = static_cast<uint32_t>(wire_bytes.length()); 349 bytes_read = module_size - offset >= size ? size : module_size - offset; 350 memcpy(buffer, wire_bytes.start() + offset, bytes_read); 351 } 352 } 353 return bytes_read; 354} 355 356bool WasmModuleDebug::AddBreakpoint(uint32_t offset, int* breakpoint_id) { 357 v8::Local<debug::WasmScript> wasm_script = wasm_script_.Get(isolate_); 358 Handle<Script> script = Utils::OpenHandle(*wasm_script); 359 Handle<String> condition = GetIsolate()->factory()->empty_string(); 360 int breakpoint_address = static_cast<int>(offset); 361 return GetIsolate()->debug()->SetBreakPointForScript( 362 script, condition, &breakpoint_address, breakpoint_id); 363} 364 365void WasmModuleDebug::RemoveBreakpoint(uint32_t offset, int breakpoint_id) { 366 v8::Local<debug::WasmScript> wasm_script = wasm_script_.Get(isolate_); 367 Handle<Script> script = Utils::OpenHandle(*wasm_script); 368 GetIsolate()->debug()->RemoveBreakpointForWasmScript(script, breakpoint_id); 369} 370 371void WasmModuleDebug::PrepareStep() { 372 i::Isolate* isolate = GetIsolate(); 373 DebugScope debug_scope(isolate->debug()); 374 debug::PrepareStep(reinterpret_cast<v8::Isolate*>(isolate), 375 debug::StepAction::StepInto); 376} 377 378template <typename T> 379bool StoreValue(const T& value, uint8_t* buffer, uint32_t buffer_size, 380 uint32_t* size) { 381 *size = sizeof(value); 382 if (*size > buffer_size) return false; 383 memcpy(buffer, &value, *size); 384 return true; 385} 386 387// static 388bool WasmModuleDebug::GetWasmValue(const wasm::WasmValue& wasm_value, 389 uint8_t* buffer, uint32_t buffer_size, 390 uint32_t* size) { 391 switch (wasm_value.type().kind()) { 392 case wasm::kI32: 393 return StoreValue(wasm_value.to_i32(), buffer, buffer_size, size); 394 case wasm::kI64: 395 return StoreValue(wasm_value.to_i64(), buffer, buffer_size, size); 396 case wasm::kF32: 397 return StoreValue(wasm_value.to_f32(), buffer, buffer_size, size); 398 case wasm::kF64: 399 return StoreValue(wasm_value.to_f64(), buffer, buffer_size, size); 400 case wasm::kS128: 401 return StoreValue(wasm_value.to_s128(), buffer, buffer_size, size); 402 case wasm::kRef: 403 case wasm::kOptRef: 404 case wasm::kRtt: 405 case wasm::kVoid: 406 case wasm::kBottom: 407 // Not supported 408 return false; 409 } 410} 411 412} // namespace gdb_server 413} // namespace wasm 414} // namespace internal 415} // namespace v8 416