• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2016 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/assembler-inl.h"
6 #include "src/assert-scope.h"
7 #include "src/compiler/wasm-compiler.h"
8 #include "src/debug/debug.h"
9 #include "src/factory.h"
10 #include "src/frames-inl.h"
11 #include "src/isolate.h"
12 #include "src/wasm/module-decoder.h"
13 #include "src/wasm/wasm-interpreter.h"
14 #include "src/wasm/wasm-limits.h"
15 #include "src/wasm/wasm-module.h"
16 #include "src/wasm/wasm-objects.h"
17 #include "src/zone/accounting-allocator.h"
18 
19 using namespace v8::internal;
20 using namespace v8::internal::wasm;
21 
22 namespace {
23 
24 // Forward declaration.
25 class InterpreterHandle;
26 InterpreterHandle* GetInterpreterHandle(WasmDebugInfo* debug_info);
27 
28 class InterpreterHandle {
29   AccountingAllocator allocator_;
30   WasmInstance instance_;
31   WasmInterpreter interpreter_;
32   Isolate* isolate_;
33   StepAction next_step_action_ = StepNone;
34   int last_step_stack_depth_ = 0;
35 
36  public:
37   // Initialize in the right order, using helper methods to make this possible.
38   // WasmInterpreter has to be allocated in place, since it is not movable.
InterpreterHandle(Isolate * isolate,WasmDebugInfo * debug_info)39   InterpreterHandle(Isolate* isolate, WasmDebugInfo* debug_info)
40       : instance_(debug_info->wasm_instance()->compiled_module()->module()),
41         interpreter_(GetBytesEnv(&instance_, debug_info), &allocator_),
42         isolate_(isolate) {
43     if (debug_info->wasm_instance()->has_memory_buffer()) {
44       JSArrayBuffer* mem_buffer = debug_info->wasm_instance()->memory_buffer();
45       instance_.mem_start =
46           reinterpret_cast<byte*>(mem_buffer->backing_store());
47       CHECK(mem_buffer->byte_length()->ToUint32(&instance_.mem_size));
48     } else {
49       DCHECK_EQ(0, instance_.module->min_mem_pages);
50       instance_.mem_start = nullptr;
51       instance_.mem_size = 0;
52     }
53   }
54 
GetBytesEnv(WasmInstance * instance,WasmDebugInfo * debug_info)55   static ModuleBytesEnv GetBytesEnv(WasmInstance* instance,
56                                     WasmDebugInfo* debug_info) {
57     // Return raw pointer into heap. The WasmInterpreter will make its own copy
58     // of this data anyway, and there is no heap allocation in-between.
59     SeqOneByteString* bytes_str =
60         debug_info->wasm_instance()->compiled_module()->module_bytes();
61     Vector<const byte> bytes(bytes_str->GetChars(), bytes_str->length());
62     return ModuleBytesEnv(instance->module, instance, bytes);
63   }
64 
interpreter()65   WasmInterpreter* interpreter() { return &interpreter_; }
module()66   const WasmModule* module() { return instance_.module; }
67 
PrepareStep(StepAction step_action)68   void PrepareStep(StepAction step_action) {
69     next_step_action_ = step_action;
70     last_step_stack_depth_ = CurrentStackDepth();
71   }
72 
ClearStepping()73   void ClearStepping() { next_step_action_ = StepNone; }
74 
CurrentStackDepth()75   int CurrentStackDepth() {
76     DCHECK_EQ(1, interpreter()->GetThreadCount());
77     return interpreter()->GetThread(0)->GetFrameCount();
78   }
79 
Execute(uint32_t func_index,uint8_t * arg_buffer)80   void Execute(uint32_t func_index, uint8_t* arg_buffer) {
81     DCHECK_GE(module()->functions.size(), func_index);
82     FunctionSig* sig = module()->functions[func_index].sig;
83     DCHECK_GE(kMaxInt, sig->parameter_count());
84     int num_params = static_cast<int>(sig->parameter_count());
85     ScopedVector<WasmVal> wasm_args(num_params);
86     uint8_t* arg_buf_ptr = arg_buffer;
87     for (int i = 0; i < num_params; ++i) {
88       int param_size = 1 << ElementSizeLog2Of(sig->GetParam(i));
89 #define CASE_ARG_TYPE(type, ctype)                                  \
90   case type:                                                        \
91     DCHECK_EQ(param_size, sizeof(ctype));                           \
92     wasm_args[i] = WasmVal(*reinterpret_cast<ctype*>(arg_buf_ptr)); \
93     break;
94       switch (sig->GetParam(i)) {
95         CASE_ARG_TYPE(kWasmI32, uint32_t)
96         CASE_ARG_TYPE(kWasmI64, uint64_t)
97         CASE_ARG_TYPE(kWasmF32, float)
98         CASE_ARG_TYPE(kWasmF64, double)
99 #undef CASE_ARG_TYPE
100         default:
101           UNREACHABLE();
102       }
103       arg_buf_ptr += RoundUpToMultipleOfPowOf2(param_size, 8);
104     }
105 
106     WasmInterpreter::Thread* thread = interpreter_.GetThread(0);
107     // We do not support reentering an already running interpreter at the moment
108     // (like INTERPRETER -> JS -> WASM -> INTERPRETER).
109     DCHECK(thread->state() == WasmInterpreter::STOPPED ||
110            thread->state() == WasmInterpreter::FINISHED);
111     thread->Reset();
112     thread->PushFrame(&module()->functions[func_index], wasm_args.start());
113     bool finished = false;
114     while (!finished) {
115       // TODO(clemensh): Add occasional StackChecks.
116       WasmInterpreter::State state = ContinueExecution(thread);
117       switch (state) {
118         case WasmInterpreter::State::PAUSED:
119           NotifyDebugEventListeners(thread);
120           break;
121         case WasmInterpreter::State::FINISHED:
122           // Perfect, just break the switch and exit the loop.
123           finished = true;
124           break;
125         case WasmInterpreter::State::TRAPPED:
126           // TODO(clemensh): Generate appropriate JS exception.
127           UNIMPLEMENTED();
128           break;
129         // STOPPED and RUNNING should never occur here.
130         case WasmInterpreter::State::STOPPED:
131         case WasmInterpreter::State::RUNNING:
132         default:
133           UNREACHABLE();
134       }
135     }
136 
137     // Copy back the return value
138     DCHECK_GE(kV8MaxWasmFunctionReturns, sig->return_count());
139     // TODO(wasm): Handle multi-value returns.
140     DCHECK_EQ(1, kV8MaxWasmFunctionReturns);
141     if (sig->return_count()) {
142       WasmVal ret_val = thread->GetReturnValue(0);
143 #define CASE_RET_TYPE(type, ctype)                                       \
144   case type:                                                             \
145     DCHECK_EQ(1 << ElementSizeLog2Of(sig->GetReturn(0)), sizeof(ctype)); \
146     *reinterpret_cast<ctype*>(arg_buffer) = ret_val.to<ctype>();         \
147     break;
148       switch (sig->GetReturn(0)) {
149         CASE_RET_TYPE(kWasmI32, uint32_t)
150         CASE_RET_TYPE(kWasmI64, uint64_t)
151         CASE_RET_TYPE(kWasmF32, float)
152         CASE_RET_TYPE(kWasmF64, double)
153 #undef CASE_RET_TYPE
154         default:
155           UNREACHABLE();
156       }
157     }
158   }
159 
ContinueExecution(WasmInterpreter::Thread * thread)160   WasmInterpreter::State ContinueExecution(WasmInterpreter::Thread* thread) {
161     switch (next_step_action_) {
162       case StepNone:
163         return thread->Run();
164       case StepIn:
165         return thread->Step();
166       case StepOut:
167         thread->AddBreakFlags(WasmInterpreter::BreakFlag::AfterReturn);
168         return thread->Run();
169       case StepNext: {
170         int stack_depth = thread->GetFrameCount();
171         if (stack_depth == last_step_stack_depth_) return thread->Step();
172         thread->AddBreakFlags(stack_depth > last_step_stack_depth_
173                                   ? WasmInterpreter::BreakFlag::AfterReturn
174                                   : WasmInterpreter::BreakFlag::AfterCall);
175         return thread->Run();
176       }
177       default:
178         UNREACHABLE();
179         return WasmInterpreter::STOPPED;
180     }
181   }
182 
GetInstanceObject()183   Handle<WasmInstanceObject> GetInstanceObject() {
184     StackTraceFrameIterator it(isolate_);
185     WasmInterpreterEntryFrame* frame =
186         WasmInterpreterEntryFrame::cast(it.frame());
187     Handle<WasmInstanceObject> instance_obj(frame->wasm_instance(), isolate_);
188     DCHECK_EQ(this, GetInterpreterHandle(instance_obj->debug_info()));
189     return instance_obj;
190   }
191 
NotifyDebugEventListeners(WasmInterpreter::Thread * thread)192   void NotifyDebugEventListeners(WasmInterpreter::Thread* thread) {
193     // Enter the debugger.
194     DebugScope debug_scope(isolate_->debug());
195     if (debug_scope.failed()) return;
196 
197     // Postpone interrupt during breakpoint processing.
198     PostponeInterruptsScope postpone(isolate_);
199 
200     // Check whether we hit a breakpoint.
201     if (isolate_->debug()->break_points_active()) {
202       Handle<WasmCompiledModule> compiled_module(
203           GetInstanceObject()->compiled_module(), isolate_);
204       int position = GetTopPosition(compiled_module);
205       Handle<FixedArray> breakpoints;
206       if (compiled_module->CheckBreakPoints(position).ToHandle(&breakpoints)) {
207         // We hit one or several breakpoints. Clear stepping, notify the
208         // listeners and return.
209         ClearStepping();
210         Handle<Object> hit_breakpoints_js =
211             isolate_->factory()->NewJSArrayWithElements(breakpoints);
212         isolate_->debug()->OnDebugBreak(hit_breakpoints_js);
213         return;
214       }
215     }
216 
217     // We did not hit a breakpoint, so maybe this pause is related to stepping.
218     bool hit_step = false;
219     switch (next_step_action_) {
220       case StepNone:
221         break;
222       case StepIn:
223         hit_step = true;
224         break;
225       case StepOut:
226         hit_step = thread->GetFrameCount() < last_step_stack_depth_;
227         break;
228       case StepNext: {
229         hit_step = thread->GetFrameCount() == last_step_stack_depth_;
230         break;
231       }
232       default:
233         UNREACHABLE();
234     }
235     if (!hit_step) return;
236     ClearStepping();
237     isolate_->debug()->OnDebugBreak(isolate_->factory()->undefined_value());
238   }
239 
GetTopPosition(Handle<WasmCompiledModule> compiled_module)240   int GetTopPosition(Handle<WasmCompiledModule> compiled_module) {
241     DCHECK_EQ(1, interpreter()->GetThreadCount());
242     WasmInterpreter::Thread* thread = interpreter()->GetThread(0);
243     DCHECK_LT(0, thread->GetFrameCount());
244 
245     wasm::InterpretedFrame frame =
246         thread->GetFrame(thread->GetFrameCount() - 1);
247     return compiled_module->GetFunctionOffset(frame.function()->func_index) +
248            frame.pc();
249   }
250 
GetInterpretedStack(Address frame_pointer)251   std::vector<std::pair<uint32_t, int>> GetInterpretedStack(
252       Address frame_pointer) {
253     // TODO(clemensh): Use frame_pointer.
254     USE(frame_pointer);
255 
256     DCHECK_EQ(1, interpreter()->GetThreadCount());
257     WasmInterpreter::Thread* thread = interpreter()->GetThread(0);
258     std::vector<std::pair<uint32_t, int>> stack(thread->GetFrameCount());
259     for (int i = 0, e = thread->GetFrameCount(); i < e; ++i) {
260       wasm::InterpretedFrame frame = thread->GetFrame(i);
261       stack[i] = {frame.function()->func_index, frame.pc()};
262     }
263     return stack;
264   }
265 
GetInterpretedFrame(Address frame_pointer,int idx)266   std::unique_ptr<wasm::InterpretedFrame> GetInterpretedFrame(
267       Address frame_pointer, int idx) {
268     // TODO(clemensh): Use frame_pointer.
269     USE(frame_pointer);
270 
271     DCHECK_EQ(1, interpreter()->GetThreadCount());
272     WasmInterpreter::Thread* thread = interpreter()->GetThread(0);
273     return std::unique_ptr<wasm::InterpretedFrame>(
274         new wasm::InterpretedFrame(thread->GetMutableFrame(idx)));
275   }
276 
NumInterpretedCalls()277   uint64_t NumInterpretedCalls() {
278     DCHECK_EQ(1, interpreter()->GetThreadCount());
279     return interpreter()->GetThread(0)->NumInterpretedCalls();
280   }
281 };
282 
GetOrCreateInterpreterHandle(Isolate * isolate,Handle<WasmDebugInfo> debug_info)283 InterpreterHandle* GetOrCreateInterpreterHandle(
284     Isolate* isolate, Handle<WasmDebugInfo> debug_info) {
285   Handle<Object> handle(debug_info->get(WasmDebugInfo::kInterpreterHandle),
286                         isolate);
287   if (handle->IsUndefined(isolate)) {
288     InterpreterHandle* cpp_handle = new InterpreterHandle(isolate, *debug_info);
289     handle = Managed<InterpreterHandle>::New(isolate, cpp_handle);
290     debug_info->set(WasmDebugInfo::kInterpreterHandle, *handle);
291   }
292 
293   return Handle<Managed<InterpreterHandle>>::cast(handle)->get();
294 }
295 
GetInterpreterHandle(WasmDebugInfo * debug_info)296 InterpreterHandle* GetInterpreterHandle(WasmDebugInfo* debug_info) {
297   Object* handle_obj = debug_info->get(WasmDebugInfo::kInterpreterHandle);
298   DCHECK(!handle_obj->IsUndefined(debug_info->GetIsolate()));
299   return Managed<InterpreterHandle>::cast(handle_obj)->get();
300 }
301 
GetInterpreterHandleOrNull(WasmDebugInfo * debug_info)302 InterpreterHandle* GetInterpreterHandleOrNull(WasmDebugInfo* debug_info) {
303   Object* handle_obj = debug_info->get(WasmDebugInfo::kInterpreterHandle);
304   if (handle_obj->IsUndefined(debug_info->GetIsolate())) return nullptr;
305   return Managed<InterpreterHandle>::cast(handle_obj)->get();
306 }
307 
GetNumFunctions(WasmInstanceObject * instance)308 int GetNumFunctions(WasmInstanceObject* instance) {
309   size_t num_functions =
310       instance->compiled_module()->module()->functions.size();
311   DCHECK_GE(kMaxInt, num_functions);
312   return static_cast<int>(num_functions);
313 }
314 
GetOrCreateInterpretedFunctions(Isolate * isolate,Handle<WasmDebugInfo> debug_info)315 Handle<FixedArray> GetOrCreateInterpretedFunctions(
316     Isolate* isolate, Handle<WasmDebugInfo> debug_info) {
317   Handle<Object> obj(debug_info->get(WasmDebugInfo::kInterpretedFunctions),
318                      isolate);
319   if (!obj->IsUndefined(isolate)) return Handle<FixedArray>::cast(obj);
320 
321   Handle<FixedArray> new_arr = isolate->factory()->NewFixedArray(
322       GetNumFunctions(debug_info->wasm_instance()));
323   debug_info->set(WasmDebugInfo::kInterpretedFunctions, *new_arr);
324   return new_arr;
325 }
326 
RedirectCallsitesInCode(Code * code,Code * old_target,Code * new_target)327 void RedirectCallsitesInCode(Code* code, Code* old_target, Code* new_target) {
328   DisallowHeapAllocation no_gc;
329   for (RelocIterator it(code, RelocInfo::kCodeTargetMask); !it.done();
330        it.next()) {
331     DCHECK(RelocInfo::IsCodeTarget(it.rinfo()->rmode()));
332     Code* target = Code::GetCodeFromTargetAddress(it.rinfo()->target_address());
333     if (target != old_target) continue;
334     it.rinfo()->set_target_address(new_target->instruction_start());
335   }
336 }
337 
RedirectCallsitesInInstance(Isolate * isolate,WasmInstanceObject * instance,Code * old_target,Code * new_target)338 void RedirectCallsitesInInstance(Isolate* isolate, WasmInstanceObject* instance,
339                                  Code* old_target, Code* new_target) {
340   DisallowHeapAllocation no_gc;
341   // Redirect all calls in wasm functions.
342   FixedArray* code_table = instance->compiled_module()->ptr_to_code_table();
343   for (int i = 0, e = GetNumFunctions(instance); i < e; ++i) {
344     RedirectCallsitesInCode(Code::cast(code_table->get(i)), old_target,
345                             new_target);
346   }
347 
348   // Redirect all calls in exported functions.
349   FixedArray* weak_exported_functions =
350       instance->compiled_module()->ptr_to_weak_exported_functions();
351   for (int i = 0, e = weak_exported_functions->length(); i != e; ++i) {
352     WeakCell* weak_function = WeakCell::cast(weak_exported_functions->get(i));
353     if (weak_function->cleared()) continue;
354     Code* code = JSFunction::cast(weak_function->value())->code();
355     RedirectCallsitesInCode(code, old_target, new_target);
356   }
357 }
358 
359 }  // namespace
360 
New(Handle<WasmInstanceObject> instance)361 Handle<WasmDebugInfo> WasmDebugInfo::New(Handle<WasmInstanceObject> instance) {
362   Isolate* isolate = instance->GetIsolate();
363   Factory* factory = isolate->factory();
364   Handle<FixedArray> arr = factory->NewFixedArray(kFieldCount, TENURED);
365   arr->set(kInstance, *instance);
366   return Handle<WasmDebugInfo>::cast(arr);
367 }
368 
IsDebugInfo(Object * object)369 bool WasmDebugInfo::IsDebugInfo(Object* object) {
370   if (!object->IsFixedArray()) return false;
371   FixedArray* arr = FixedArray::cast(object);
372   if (arr->length() != kFieldCount) return false;
373   if (!IsWasmInstance(arr->get(kInstance))) return false;
374   Isolate* isolate = arr->GetIsolate();
375   if (!arr->get(kInterpreterHandle)->IsUndefined(isolate) &&
376       !arr->get(kInterpreterHandle)->IsForeign())
377     return false;
378   return true;
379 }
380 
cast(Object * object)381 WasmDebugInfo* WasmDebugInfo::cast(Object* object) {
382   DCHECK(IsDebugInfo(object));
383   return reinterpret_cast<WasmDebugInfo*>(object);
384 }
385 
wasm_instance()386 WasmInstanceObject* WasmDebugInfo::wasm_instance() {
387   return WasmInstanceObject::cast(get(kInstance));
388 }
389 
SetBreakpoint(Handle<WasmDebugInfo> debug_info,int func_index,int offset)390 void WasmDebugInfo::SetBreakpoint(Handle<WasmDebugInfo> debug_info,
391                                   int func_index, int offset) {
392   Isolate* isolate = debug_info->GetIsolate();
393   InterpreterHandle* handle = GetOrCreateInterpreterHandle(isolate, debug_info);
394   RedirectToInterpreter(debug_info, func_index);
395   const WasmFunction* func = &handle->module()->functions[func_index];
396   handle->interpreter()->SetBreakpoint(func, offset, true);
397 }
398 
RedirectToInterpreter(Handle<WasmDebugInfo> debug_info,int func_index)399 void WasmDebugInfo::RedirectToInterpreter(Handle<WasmDebugInfo> debug_info,
400                                           int func_index) {
401   Isolate* isolate = debug_info->GetIsolate();
402   DCHECK_LE(0, func_index);
403   DCHECK_GT(debug_info->wasm_instance()->module()->functions.size(),
404             func_index);
405   Handle<FixedArray> interpreted_functions =
406       GetOrCreateInterpretedFunctions(isolate, debug_info);
407   if (!interpreted_functions->get(func_index)->IsUndefined(isolate)) return;
408 
409   // Ensure that the interpreter is instantiated.
410   GetOrCreateInterpreterHandle(isolate, debug_info);
411   Handle<WasmInstanceObject> instance(debug_info->wasm_instance(), isolate);
412   Handle<Code> new_code = compiler::CompileWasmInterpreterEntry(
413       isolate, func_index,
414       instance->compiled_module()->module()->functions[func_index].sig,
415       instance);
416 
417   Handle<FixedArray> code_table = instance->compiled_module()->code_table();
418   Handle<Code> old_code(Code::cast(code_table->get(func_index)), isolate);
419   interpreted_functions->set(func_index, *new_code);
420 
421   RedirectCallsitesInInstance(isolate, *instance, *old_code, *new_code);
422 }
423 
PrepareStep(StepAction step_action)424 void WasmDebugInfo::PrepareStep(StepAction step_action) {
425   GetInterpreterHandle(this)->PrepareStep(step_action);
426 }
427 
RunInterpreter(int func_index,uint8_t * arg_buffer)428 void WasmDebugInfo::RunInterpreter(int func_index, uint8_t* arg_buffer) {
429   DCHECK_LE(0, func_index);
430   GetInterpreterHandle(this)->Execute(static_cast<uint32_t>(func_index),
431                                       arg_buffer);
432 }
433 
GetInterpretedStack(Address frame_pointer)434 std::vector<std::pair<uint32_t, int>> WasmDebugInfo::GetInterpretedStack(
435     Address frame_pointer) {
436   return GetInterpreterHandle(this)->GetInterpretedStack(frame_pointer);
437 }
438 
GetInterpretedFrame(Address frame_pointer,int idx)439 std::unique_ptr<wasm::InterpretedFrame> WasmDebugInfo::GetInterpretedFrame(
440     Address frame_pointer, int idx) {
441   return GetInterpreterHandle(this)->GetInterpretedFrame(frame_pointer, idx);
442 }
443 
NumInterpretedCalls()444 uint64_t WasmDebugInfo::NumInterpretedCalls() {
445   auto handle = GetInterpreterHandleOrNull(this);
446   return handle ? handle->NumInterpretedCalls() : 0;
447 }
448