Home | History | Annotate | Download | only in wasm
      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.
     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 
     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 
     65   WasmInterpreter* interpreter() { return &interpreter_; }
     66   const WasmModule* module() { return instance_.module; }
     67 
     68   void PrepareStep(StepAction step_action) {
     69     next_step_action_ = step_action;
     70     last_step_stack_depth_ = CurrentStackDepth();
     71   }
     72 
     73   void ClearStepping() { next_step_action_ = StepNone; }
     74 
     75   int CurrentStackDepth() {
     76     DCHECK_EQ(1, interpreter()->GetThreadCount());
     77     return interpreter()->GetThread(0)->GetFrameCount();
     78   }
     79 
     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 
    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 
    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 
    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 
    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 
    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 
    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 
    277   uint64_t NumInterpretedCalls() {
    278     DCHECK_EQ(1, interpreter()->GetThreadCount());
    279     return interpreter()->GetThread(0)->NumInterpretedCalls();
    280   }
    281 };
    282 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    381 WasmDebugInfo* WasmDebugInfo::cast(Object* object) {
    382   DCHECK(IsDebugInfo(object));
    383   return reinterpret_cast<WasmDebugInfo*>(object);
    384 }
    385 
    386 WasmInstanceObject* WasmDebugInfo::wasm_instance() {
    387   return WasmInstanceObject::cast(get(kInstance));
    388 }
    389 
    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 
    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 
    424 void WasmDebugInfo::PrepareStep(StepAction step_action) {
    425   GetInterpreterHandle(this)->PrepareStep(step_action);
    426 }
    427 
    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 
    434 std::vector<std::pair<uint32_t, int>> WasmDebugInfo::GetInterpretedStack(
    435     Address frame_pointer) {
    436   return GetInterpreterHandle(this)->GetInterpretedStack(frame_pointer);
    437 }
    438 
    439 std::unique_ptr<wasm::InterpretedFrame> WasmDebugInfo::GetInterpretedFrame(
    440     Address frame_pointer, int idx) {
    441   return GetInterpreterHandle(this)->GetInterpretedFrame(frame_pointer, idx);
    442 }
    443 
    444 uint64_t WasmDebugInfo::NumInterpretedCalls() {
    445   auto handle = GetInterpreterHandleOrNull(this);
    446   return handle ? handle->NumInterpretedCalls() : 0;
    447 }
    448