• 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/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 
17 namespace v8 {
18 namespace internal {
19 namespace wasm {
20 namespace gdb_server {
21 
WasmModuleDebug(v8::Isolate * isolate,Local<debug::WasmScript> wasm_script)22 WasmModuleDebug::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 
GetModuleName() const30 std::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 
GetFirstWasmInstance()40 Handle<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 
GetLEB128Size(base::Vector<const uint8_t> module_bytes,int offset)58 int 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 
ReturnPc(const NativeModule * native_module,int pc)64 int 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
GetCallStack(uint32_t debug_context_id,Isolate * isolate)88 std::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
FindWasmFrame(StackTraceFrameIterator * frame_it,uint32_t * frame_index)149 std::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
GetWasmInstance(Isolate * isolate,uint32_t frame_index)189 Handle<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
GetWasmGlobal(Isolate * isolate,uint32_t frame_index,uint32_t index,uint8_t * buffer,uint32_t buffer_size,uint32_t * size)204 bool 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
GetWasmLocal(Isolate * isolate,uint32_t frame_index,uint32_t index,uint8_t * buffer,uint32_t buffer_size,uint32_t * size)223 bool 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
GetWasmStackValue(Isolate * isolate,uint32_t frame_index,uint32_t index,uint8_t * buffer,uint32_t buffer_size,uint32_t * size)256 bool 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 
GetWasmMemory(Isolate * isolate,uint32_t offset,uint8_t * buffer,uint32_t size)288 uint32_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 
GetWasmData(Isolate * isolate,uint32_t offset,uint8_t * buffer,uint32_t size)308 uint32_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 
GetWasmModuleBytes(wasm_addr_t wasm_addr,uint8_t * buffer,uint32_t size)336 uint32_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 
AddBreakpoint(uint32_t offset,int * breakpoint_id)356 bool 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 
RemoveBreakpoint(uint32_t offset,int breakpoint_id)365 void 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 
PrepareStep()371 void 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 
378 template <typename T>
StoreValue(const T & value,uint8_t * buffer,uint32_t buffer_size,uint32_t * size)379 bool 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
GetWasmValue(const wasm::WasmValue & wasm_value,uint8_t * buffer,uint32_t buffer_size,uint32_t * size)388 bool 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