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