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