wasm-debug.cc 16.7 KB
Newer Older
1 2 3 4
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5
#include "src/assembler-inl.h"
6
#include "src/assert-scope.h"
7
#include "src/compiler/wasm-compiler.h"
8 9
#include "src/debug/debug.h"
#include "src/factory.h"
10
#include "src/frames-inl.h"
11 12
#include "src/isolate.h"
#include "src/wasm/module-decoder.h"
13 14
#include "src/wasm/wasm-interpreter.h"
#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 20 21

using namespace v8::internal;
using namespace v8::internal::wasm;

22 23
namespace {

24 25 26 27
// Forward declaration.
class InterpreterHandle;
InterpreterHandle* GetInterpreterHandle(WasmDebugInfo* debug_info);

28 29 30 31
class InterpreterHandle {
  AccountingAllocator allocator_;
  WasmInstance instance_;
  WasmInterpreter interpreter_;
32
  Isolate* isolate_;
33 34
  StepAction next_step_action_ = StepNone;
  int last_step_stack_depth_ = 0;
35 36 37 38

 public:
  // Initialize in the right order, using helper methods to make this possible.
  // 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 42
        interpreter_(GetBytesEnv(&instance_, debug_info), &allocator_),
        isolate_(isolate) {
43 44
    if (debug_info->wasm_instance()->has_memory_buffer()) {
      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 49 50 51
    } else {
      DCHECK_EQ(0, instance_.module->min_mem_pages);
      instance_.mem_start = nullptr;
      instance_.mem_size = 0;
52 53 54
    }
  }

55 56
  static ModuleBytesEnv GetBytesEnv(WasmInstance* instance,
                                    WasmDebugInfo* debug_info) {
57 58
    // Return raw pointer into heap. The WasmInterpreter will make its own copy
    // of this data anyway, and there is no heap allocation in-between.
59
    SeqOneByteString* bytes_str =
60 61 62 63 64
        debug_info->wasm_instance()->compiled_module()->module_bytes();
    Vector<const byte> bytes(bytes_str->GetChars(), bytes_str->length());
    return ModuleBytesEnv(instance->module, instance, bytes);
  }

65 66
  WasmInterpreter* interpreter() { return &interpreter_; }
  const WasmModule* module() { return instance_.module; }
67

68 69 70 71 72 73 74 75 76 77 78 79
  void PrepareStep(StepAction step_action) {
    next_step_action_ = step_action;
    last_step_stack_depth_ = CurrentStackDepth();
  }

  void ClearStepping() { next_step_action_ = StepNone; }

  int CurrentStackDepth() {
    DCHECK_EQ(1, interpreter()->GetThreadCount());
    return interpreter()->GetThread(0)->GetFrameCount();
  }

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 84 85
    DCHECK_GE(kMaxInt, sig->parameter_count());
    int num_params = static_cast<int>(sig->parameter_count());
    ScopedVector<WasmVal> wasm_args(num_params);
86
    uint8_t* arg_buf_ptr = arg_buffer;
87 88
    for (int i = 0; i < num_params; ++i) {
      uint32_t param_size = 1 << ElementSizeLog2Of(sig->GetParam(i));
89 90 91
#define CASE_ARG_TYPE(type, ctype)                                  \
  case type:                                                        \
    DCHECK_EQ(param_size, sizeof(ctype));                           \
92
    wasm_args[i] = WasmVal(ReadUnalignedValue<ctype>(arg_buf_ptr)); \
93 94 95 96 97 98 99 100 101 102 103 104 105
    break;
      switch (sig->GetParam(i)) {
        CASE_ARG_TYPE(kWasmI32, uint32_t)
        CASE_ARG_TYPE(kWasmI64, uint64_t)
        CASE_ARG_TYPE(kWasmF32, float)
        CASE_ARG_TYPE(kWasmF64, double)
#undef CASE_ARG_TYPE
        default:
          UNREACHABLE();
      }
      arg_buf_ptr += param_size;
    }

106
    WasmInterpreter::Thread* thread = interpreter_.GetThread(0);
107 108 109 110 111 112
    // We do not support reentering an already running interpreter at the moment
    // (like INTERPRETER -> JS -> WASM -> INTERPRETER).
    DCHECK(thread->state() == WasmInterpreter::STOPPED ||
           thread->state() == WasmInterpreter::FINISHED);
    thread->Reset();
    thread->PushFrame(&module()->functions[func_index], wasm_args.start());
113 114 115 116
    bool finished = false;
    while (!finished) {
      // TODO(clemensh): Add occasional StackChecks.
      WasmInterpreter::State state = ContinueExecution(thread);
117
      switch (state) {
118
        case WasmInterpreter::State::PAUSED:
119
          NotifyDebugEventListeners(thread);
120
          break;
121 122
        case WasmInterpreter::State::FINISHED:
          // Perfect, just break the switch and exit the loop.
123
          finished = true;
124 125 126 127 128 129 130 131 132 133 134
          break;
        case WasmInterpreter::State::TRAPPED:
          // TODO(clemensh): Generate appropriate JS exception.
          UNIMPLEMENTED();
          break;
        // STOPPED and RUNNING should never occur here.
        case WasmInterpreter::State::STOPPED:
        case WasmInterpreter::State::RUNNING:
        default:
          UNREACHABLE();
      }
135
    }
136 137 138 139 140 141 142 143 144 145

    // Copy back the return value
    DCHECK_GE(kV8MaxWasmFunctionReturns, sig->return_count());
    // TODO(wasm): Handle multi-value returns.
    DCHECK_EQ(1, kV8MaxWasmFunctionReturns);
    if (sig->return_count()) {
      WasmVal ret_val = thread->GetReturnValue(0);
#define CASE_RET_TYPE(type, ctype)                                       \
  case type:                                                             \
    DCHECK_EQ(1 << ElementSizeLog2Of(sig->GetReturn(0)), sizeof(ctype)); \
146
    WriteUnalignedValue<ctype>(arg_buffer, ret_val.to<ctype>());         \
147 148 149 150 151 152 153 154 155 156 157 158
    break;
      switch (sig->GetReturn(0)) {
        CASE_RET_TYPE(kWasmI32, uint32_t)
        CASE_RET_TYPE(kWasmI64, uint64_t)
        CASE_RET_TYPE(kWasmF32, float)
        CASE_RET_TYPE(kWasmF64, double)
#undef CASE_RET_TYPE
        default:
          UNREACHABLE();
      }
    }
  }
159

160 161 162 163 164 165 166 167
  WasmInterpreter::State ContinueExecution(WasmInterpreter::Thread* thread) {
    switch (next_step_action_) {
      case StepNone:
        return thread->Run();
      case StepIn:
        return thread->Step();
      case StepOut:
        thread->AddBreakFlags(WasmInterpreter::BreakFlag::AfterReturn);
168
        return thread->Run();
169 170 171 172 173 174 175 176 177 178 179 180 181 182
      case StepNext: {
        int stack_depth = thread->GetFrameCount();
        if (stack_depth == last_step_stack_depth_) return thread->Step();
        thread->AddBreakFlags(stack_depth > last_step_stack_depth_
                                  ? WasmInterpreter::BreakFlag::AfterReturn
                                  : WasmInterpreter::BreakFlag::AfterCall);
        return thread->Run();
      }
      default:
        UNREACHABLE();
        return WasmInterpreter::STOPPED;
    }
  }

183 184 185 186 187 188 189 190 191
  Handle<WasmInstanceObject> GetInstanceObject() {
    StackTraceFrameIterator it(isolate_);
    WasmInterpreterEntryFrame* frame =
        WasmInterpreterEntryFrame::cast(it.frame());
    Handle<WasmInstanceObject> instance_obj(frame->wasm_instance(), isolate_);
    DCHECK_EQ(this, GetInterpreterHandle(instance_obj->debug_info()));
    return instance_obj;
  }

192
  void NotifyDebugEventListeners(WasmInterpreter::Thread* thread) {
193 194 195 196 197 198 199
    // Enter the debugger.
    DebugScope debug_scope(isolate_->debug());
    if (debug_scope.failed()) return;

    // Postpone interrupt during breakpoint processing.
    PostponeInterruptsScope postpone(isolate_);

200
    // Check whether we hit a breakpoint.
201
    if (isolate_->debug()->break_points_active()) {
202 203 204 205 206 207 208 209 210 211 212 213 214
      Handle<WasmCompiledModule> compiled_module(
          GetInstanceObject()->compiled_module(), isolate_);
      int position = GetTopPosition(compiled_module);
      Handle<FixedArray> breakpoints;
      if (compiled_module->CheckBreakPoints(position).ToHandle(&breakpoints)) {
        // We hit one or several breakpoints. Clear stepping, notify the
        // listeners and return.
        ClearStepping();
        Handle<Object> hit_breakpoints_js =
            isolate_->factory()->NewJSArrayWithElements(breakpoints);
        isolate_->debug()->OnDebugBreak(hit_breakpoints_js);
        return;
      }
215 216
    }

217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
    // We did not hit a breakpoint, so maybe this pause is related to stepping.
    bool hit_step = false;
    switch (next_step_action_) {
      case StepNone:
        break;
      case StepIn:
        hit_step = true;
        break;
      case StepOut:
        hit_step = thread->GetFrameCount() < last_step_stack_depth_;
        break;
      case StepNext: {
        hit_step = thread->GetFrameCount() == last_step_stack_depth_;
        break;
      }
      default:
        UNREACHABLE();
234
    }
235 236 237
    if (!hit_step) return;
    ClearStepping();
    isolate_->debug()->OnDebugBreak(isolate_->factory()->undefined_value());
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275
  }

  int GetTopPosition(Handle<WasmCompiledModule> compiled_module) {
    DCHECK_EQ(1, interpreter()->GetThreadCount());
    WasmInterpreter::Thread* thread = interpreter()->GetThread(0);
    DCHECK_LT(0, thread->GetFrameCount());

    wasm::InterpretedFrame frame =
        thread->GetFrame(thread->GetFrameCount() - 1);
    return compiled_module->GetFunctionOffset(frame.function()->func_index) +
           frame.pc();
  }

  std::vector<std::pair<uint32_t, int>> GetInterpretedStack(
      Address frame_pointer) {
    // TODO(clemensh): Use frame_pointer.
    USE(frame_pointer);

    DCHECK_EQ(1, interpreter()->GetThreadCount());
    WasmInterpreter::Thread* thread = interpreter()->GetThread(0);
    std::vector<std::pair<uint32_t, int>> stack(thread->GetFrameCount());
    for (int i = 0, e = thread->GetFrameCount(); i < e; ++i) {
      wasm::InterpretedFrame frame = thread->GetFrame(i);
      stack[i] = {frame.function()->func_index, frame.pc()};
    }
    return stack;
  }

  std::unique_ptr<wasm::InterpretedFrame> GetInterpretedFrame(
      Address frame_pointer, int idx) {
    // TODO(clemensh): Use frame_pointer.
    USE(frame_pointer);

    DCHECK_EQ(1, interpreter()->GetThreadCount());
    WasmInterpreter::Thread* thread = interpreter()->GetThread(0);
    return std::unique_ptr<wasm::InterpretedFrame>(
        new wasm::InterpretedFrame(thread->GetMutableFrame(idx)));
  }
276 277 278 279 280

  uint64_t NumInterpretedCalls() {
    DCHECK_EQ(1, interpreter()->GetThreadCount());
    return interpreter()->GetThread(0)->NumInterpretedCalls();
  }
281 282
};

283 284
InterpreterHandle* GetOrCreateInterpreterHandle(
    Isolate* isolate, Handle<WasmDebugInfo> debug_info) {
285 286 287
  Handle<Object> handle(debug_info->get(WasmDebugInfo::kInterpreterHandle),
                        isolate);
  if (handle->IsUndefined(isolate)) {
288
    InterpreterHandle* cpp_handle = new InterpreterHandle(isolate, *debug_info);
289 290 291 292 293 294 295
    handle = Managed<InterpreterHandle>::New(isolate, cpp_handle);
    debug_info->set(WasmDebugInfo::kInterpreterHandle, *handle);
  }

  return Handle<Managed<InterpreterHandle>>::cast(handle)->get();
}

296 297 298 299 300 301
InterpreterHandle* GetInterpreterHandle(WasmDebugInfo* debug_info) {
  Object* handle_obj = debug_info->get(WasmDebugInfo::kInterpreterHandle);
  DCHECK(!handle_obj->IsUndefined(debug_info->GetIsolate()));
  return Managed<InterpreterHandle>::cast(handle_obj)->get();
}

302 303 304 305 306 307
InterpreterHandle* GetInterpreterHandleOrNull(WasmDebugInfo* debug_info) {
  Object* handle_obj = debug_info->get(WasmDebugInfo::kInterpreterHandle);
  if (handle_obj->IsUndefined(debug_info->GetIsolate())) return nullptr;
  return Managed<InterpreterHandle>::cast(handle_obj)->get();
}

308
int GetNumFunctions(WasmInstanceObject* instance) {
309 310 311 312 313 314 315
  size_t num_functions =
      instance->compiled_module()->module()->functions.size();
  DCHECK_GE(kMaxInt, num_functions);
  return static_cast<int>(num_functions);
}

Handle<FixedArray> GetOrCreateInterpretedFunctions(
316
    Isolate* isolate, Handle<WasmDebugInfo> debug_info) {
317 318 319 320 321 322 323 324 325 326
  Handle<Object> obj(debug_info->get(WasmDebugInfo::kInterpretedFunctions),
                     isolate);
  if (!obj->IsUndefined(isolate)) return Handle<FixedArray>::cast(obj);

  Handle<FixedArray> new_arr = isolate->factory()->NewFixedArray(
      GetNumFunctions(debug_info->wasm_instance()));
  debug_info->set(WasmDebugInfo::kInterpretedFunctions, *new_arr);
  return new_arr;
}

327
void RedirectCallsitesInCode(Code* code, Code* old_target, Code* new_target) {
328 329 330 331
  DisallowHeapAllocation no_gc;
  for (RelocIterator it(code, RelocInfo::kCodeTargetMask); !it.done();
       it.next()) {
    DCHECK(RelocInfo::IsCodeTarget(it.rinfo()->rmode()));
332
    Code* target = Code::GetCodeFromTargetAddress(it.rinfo()->target_address());
333 334 335 336 337
    if (target != old_target) continue;
    it.rinfo()->set_target_address(new_target->instruction_start());
  }
}

338 339
void RedirectCallsitesInInstance(Isolate* isolate, WasmInstanceObject* instance,
                                 Code* old_target, Code* new_target) {
340 341
  DisallowHeapAllocation no_gc;
  // Redirect all calls in wasm functions.
342
  FixedArray* code_table = instance->compiled_module()->ptr_to_code_table();
343 344 345 346 347 348
  for (int i = 0, e = GetNumFunctions(instance); i < e; ++i) {
    RedirectCallsitesInCode(Code::cast(code_table->get(i)), old_target,
                            new_target);
  }

  // Redirect all calls in exported functions.
349
  FixedArray* weak_exported_functions =
350 351
      instance->compiled_module()->ptr_to_weak_exported_functions();
  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 356 357 358 359 360
    RedirectCallsitesInCode(code, old_target, new_target);
  }
}

}  // namespace

361
Handle<WasmDebugInfo> WasmDebugInfo::New(Handle<WasmInstanceObject> instance) {
362 363
  Isolate* isolate = instance->GetIsolate();
  Factory* factory = isolate->factory();
364 365
  Handle<FixedArray> arr = factory->NewFixedArray(kFieldCount, TENURED);
  arr->set(kInstance, *instance);
366 367 368
  return Handle<WasmDebugInfo>::cast(arr);
}

369
bool WasmDebugInfo::IsDebugInfo(Object* object) {
370
  if (!object->IsFixedArray()) return false;
371
  FixedArray* arr = FixedArray::cast(object);
372 373
  if (arr->length() != kFieldCount) return false;
  if (!IsWasmInstance(arr->get(kInstance))) return false;
374
  Isolate* isolate = arr->GetIsolate();
375 376 377 378
  if (!arr->get(kInterpreterHandle)->IsUndefined(isolate) &&
      !arr->get(kInterpreterHandle)->IsForeign())
    return false;
  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 391
void WasmDebugInfo::SetBreakpoint(Handle<WasmDebugInfo> debug_info,
                                  int func_index, int offset) {
392 393
  Isolate* isolate = debug_info->GetIsolate();
  InterpreterHandle* handle = GetOrCreateInterpreterHandle(isolate, debug_info);
394
  RedirectToInterpreter(debug_info, func_index);
395
  const WasmFunction* func = &handle->module()->functions[func_index];
396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421
  handle->interpreter()->SetBreakpoint(func, offset, true);
}

void WasmDebugInfo::RedirectToInterpreter(Handle<WasmDebugInfo> debug_info,
                                          int func_index) {
  Isolate* isolate = debug_info->GetIsolate();
  DCHECK_LE(0, func_index);
  DCHECK_GT(debug_info->wasm_instance()->module()->functions.size(),
            func_index);
  Handle<FixedArray> interpreted_functions =
      GetOrCreateInterpretedFunctions(isolate, debug_info);
  if (!interpreted_functions->get(func_index)->IsUndefined(isolate)) return;

  // Ensure that the interpreter is instantiated.
  GetOrCreateInterpreterHandle(isolate, debug_info);
  Handle<WasmInstanceObject> instance(debug_info->wasm_instance(), isolate);
  Handle<Code> new_code = compiler::CompileWasmInterpreterEntry(
      isolate, func_index,
      instance->compiled_module()->module()->functions[func_index].sig,
      instance);

  Handle<FixedArray> code_table = instance->compiled_module()->code_table();
  Handle<Code> old_code(Code::cast(code_table->get(func_index)), isolate);
  interpreted_functions->set(func_index, *new_code);

  RedirectCallsitesInInstance(isolate, *instance, *old_code, *new_code);
422 423
}

424 425 426 427
void WasmDebugInfo::PrepareStep(StepAction step_action) {
  GetInterpreterHandle(this)->PrepareStep(step_action);
}

428
void WasmDebugInfo::RunInterpreter(int func_index, uint8_t* arg_buffer) {
429
  DCHECK_LE(0, func_index);
430 431 432 433 434 435 436 437 438 439 440 441
  GetInterpreterHandle(this)->Execute(static_cast<uint32_t>(func_index),
                                      arg_buffer);
}

std::vector<std::pair<uint32_t, int>> WasmDebugInfo::GetInterpretedStack(
    Address frame_pointer) {
  return GetInterpreterHandle(this)->GetInterpretedStack(frame_pointer);
}

std::unique_ptr<wasm::InterpretedFrame> WasmDebugInfo::GetInterpretedFrame(
    Address frame_pointer, int idx) {
  return GetInterpreterHandle(this)->GetInterpretedFrame(frame_pointer, idx);
442
}
443 444 445 446 447

uint64_t WasmDebugInfo::NumInterpretedCalls() {
  auto handle = GetInterpreterHandleOrNull(this);
  return handle ? handle->NumInterpretedCalls() : 0;
}