Commit 96774fa5 authored by titzer's avatar titzer Committed by Commit bot

[wasm] Add more tests for interpreter breakpoints.

R=binji@chromium.org,ahaas@chromium.org
BUG=

Review-Url: https://codereview.chromium.org/2014373003
Cr-Commit-Position: refs/heads/master@{#36577}
parent eff24bef
......@@ -927,6 +927,7 @@ class ThreadImpl : public WasmInterpreter::Thread {
stack_(zone),
frames_(zone),
state_(WasmInterpreter::STOPPED),
break_pc_(kInvalidPc),
trap_reason_(kTrapCount) {}
virtual ~ThreadImpl() {}
......@@ -945,12 +946,13 @@ class ThreadImpl : public WasmInterpreter::Thread {
stack_.push_back(args[i]);
}
frames_.back().ret_pc = InitLocals(code);
TRACE(" => push func#%u @%zu\n", code->function->func_index,
TRACE(" => PushFrame(#%u @%zu)\n", code->function->func_index,
frames_.back().ret_pc);
}
virtual WasmInterpreter::State Run() {
do {
TRACE(" => Run()\n");
if (state_ == WasmInterpreter::STOPPED ||
state_ == WasmInterpreter::PAUSED) {
state_ = WasmInterpreter::RUNNING;
......@@ -961,8 +963,13 @@ class ThreadImpl : public WasmInterpreter::Thread {
}
virtual WasmInterpreter::State Step() {
UNIMPLEMENTED();
return WasmInterpreter::STOPPED;
TRACE(" => Step()\n");
if (state_ == WasmInterpreter::STOPPED ||
state_ == WasmInterpreter::PAUSED) {
state_ = WasmInterpreter::RUNNING;
Execute(frames_.back().code, frames_.back().ret_pc, 1);
}
return state_;
}
virtual void Pause() { UNIMPLEMENTED(); }
......@@ -994,6 +1001,8 @@ class ThreadImpl : public WasmInterpreter::Thread {
return stack_[0];
}
virtual pc_t GetBreakpointPc() { return break_pc_; }
bool Terminated() {
return state_ == WasmInterpreter::TRAPPED ||
state_ == WasmInterpreter::FINISHED;
......@@ -1018,6 +1027,7 @@ class ThreadImpl : public WasmInterpreter::Thread {
ZoneVector<WasmVal> stack_;
ZoneVector<Frame> frames_;
WasmInterpreter::State state_;
pc_t break_pc_;
TrapReason trap_reason_;
CodeMap* codemap() { return codemap_; }
......@@ -1077,8 +1087,10 @@ class ThreadImpl : public WasmInterpreter::Thread {
}
bool SkipBreakpoint(InterpreterCode* code, pc_t pc) {
// TODO(titzer): skip a breakpoint if we are resuming from it, or it
// is set for another thread only.
if (pc == break_pc_) {
break_pc_ = kInvalidPc;
return true;
}
return false;
}
......@@ -1165,17 +1177,22 @@ class ThreadImpl : public WasmInterpreter::Thread {
continue;
}
const char* skip = "";
const char* skip = " ";
int len = 1;
byte opcode = code->start[pc];
byte orig = opcode;
if (opcode == kInternalBreakpoint) {
orig = code->orig_start[pc];
if (SkipBreakpoint(code, pc)) {
// skip breakpoint by switching on original code.
orig = code->orig_start[pc];
skip = "[skip] ";
} else {
state_ = WasmInterpreter::PAUSED;
TRACE("@%-3zu: [break] %-24s:", pc,
WasmOpcodes::OpcodeName(static_cast<WasmOpcode>(orig)));
TraceValueStack();
TRACE("\n");
break_pc_ = pc;
return CommitPc(pc);
}
}
......@@ -1684,13 +1701,13 @@ void WasmInterpreter::Run() { internals_->threads_[0].Run(); }
void WasmInterpreter::Pause() { internals_->threads_[0].Pause(); }
bool WasmInterpreter::SetBreakpoint(const WasmFunction* function, int pc,
bool WasmInterpreter::SetBreakpoint(const WasmFunction* function, pc_t pc,
bool enabled) {
InterpreterCode* code = internals_->codemap_.FindCode(function);
if (!code) return false;
int size = static_cast<int>(code->end - code->start);
size_t size = static_cast<size_t>(code->end - code->start);
// Check bounds for {pc}.
if (pc < 0 || pc >= size) return false;
if (pc < code->locals.decls_encoded_size || pc >= size) return false;
// Make a copy of the code before enabling a breakpoint.
if (enabled && code->orig_start == code->start) {
code->start = reinterpret_cast<byte*>(zone_.New(size));
......@@ -1706,12 +1723,12 @@ bool WasmInterpreter::SetBreakpoint(const WasmFunction* function, int pc,
return prev;
}
bool WasmInterpreter::GetBreakpoint(const WasmFunction* function, int pc) {
bool WasmInterpreter::GetBreakpoint(const WasmFunction* function, pc_t pc) {
InterpreterCode* code = internals_->codemap_.FindCode(function);
if (!code) return false;
int size = static_cast<int>(code->end - code->start);
size_t size = static_cast<size_t>(code->end - code->start);
// Check bounds for {pc}.
if (pc < 0 || pc >= size) return false;
if (pc < code->locals.decls_encoded_size || pc >= size) return false;
// Check if a breakpoint is present at that place in the code.
return code->start[pc] == kInternalBreakpoint;
}
......
......@@ -26,6 +26,8 @@ typedef size_t sp_t;
typedef int32_t pcdiff_t;
typedef uint32_t spdiff_t;
const pc_t kInvalidPc = 0x80000000;
// Visible for testing. A {ControlTransfer} helps the interpreter figure out
// the target program counter and stack manipulations for a branch.
struct ControlTransfer {
......@@ -126,6 +128,7 @@ class WasmInterpreter {
virtual ~Thread() {}
// Stack inspection and modification.
virtual pc_t GetBreakpointPc() = 0;
virtual int GetFrameCount() = 0;
virtual const WasmFrame* GetFrame(int index) = 0;
virtual WasmFrame* GetMutableFrame(int index) = 0;
......@@ -148,10 +151,10 @@ class WasmInterpreter {
// Set a breakpoint at {pc} in {function} to be {enabled}. Returns the
// previous state of the breakpoint at {pc}.
bool SetBreakpoint(const WasmFunction* function, int pc, bool enabled);
bool SetBreakpoint(const WasmFunction* function, pc_t pc, bool enabled);
// Gets the current state of the breakpoint at {function}.
bool GetBreakpoint(const WasmFunction* function, int pc);
bool GetBreakpoint(const WasmFunction* function, pc_t pc);
// Enable or disable tracing for {function}. Return the previous state.
bool SetTracing(const WasmFunction* function, bool enabled);
......
......@@ -135,17 +135,153 @@ TEST(Run_Wasm_nested_ifs_i) {
CHECK_EQ(14, r.Call(0, 0));
}
TEST(Step_I32Add) {
WasmRunner<int32_t> r(kExecuteInterpreted, MachineType::Int32(),
MachineType::Int32());
BUILD(r, WASM_I32_ADD(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)));
// Make tests more robust by not hard-coding offsets of various operations.
// The {Find} method finds the offsets for the given bytecodes, returning
// the offsets in an array.
SmartArrayPointer<int> Find(byte* code, size_t code_size, int n, ...) {
va_list vl;
va_start(vl, n);
SmartArrayPointer<int> offsets(new int[n]);
for (int i = 0; i < n; i++) {
offsets[i] = -1;
}
int pos = 0;
WasmOpcode current = static_cast<WasmOpcode>(va_arg(vl, int));
for (size_t i = 0; i < code_size; i++) {
if (code[i] == current) {
offsets[pos++] = static_cast<int>(i);
if (pos == n) break;
current = static_cast<WasmOpcode>(va_arg(vl, int));
}
}
va_end(vl);
return offsets;
}
TEST(Breakpoint_I32Add) {
static const int kLocalsDeclSize = 1;
static const int kNumBreakpoints = 3;
byte code[] = {WASM_I32_ADD(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1))};
SmartArrayPointer<int> offsets =
Find(code, sizeof(code), kNumBreakpoints, kExprGetLocal, kExprGetLocal,
kExprI32Add);
WasmRunner<int32_t> r(kExecuteInterpreted, MachineType::Uint32(),
MachineType::Uint32());
r.Build(code, code + arraysize(code));
WasmInterpreter* interpreter = r.interpreter();
interpreter->SetBreakpoint(r.function(), 0, true);
WasmInterpreter::Thread& thread = interpreter->GetThread(0);
for (int i = 0; i < kNumBreakpoints; i++) {
interpreter->SetBreakpoint(r.function(), kLocalsDeclSize + offsets[i],
true);
}
FOR_UINT32_INPUTS(a) {
for (uint32_t b = 11; b < 3000000000u; b += 1000000000u) {
thread.Reset();
WasmVal args[] = {WasmVal(*a), WasmVal(b)};
thread.PushFrame(r.function(), args);
for (int i = 0; i < kNumBreakpoints; i++) {
thread.Run(); // run to next breakpoint
// Check the thread stopped at the right pc.
CHECK_EQ(WasmInterpreter::PAUSED, thread.state());
CHECK_EQ(kLocalsDeclSize + offsets[i], thread.GetBreakpointPc());
}
thread.Run(); // run to completion
// Check the thread finished with the right value.
CHECK_EQ(WasmInterpreter::FINISHED, thread.state());
uint32_t expected = (*a) + (b);
CHECK_EQ(expected, thread.GetReturnValue().to<uint32_t>());
}
}
}
TEST(Step_I32Mul) {
static const int kTraceLength = 4;
byte code[] = {WASM_I32_MUL(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1))};
WasmRunner<int32_t> r(kExecuteInterpreted, MachineType::Uint32(),
MachineType::Uint32());
r.Call(1, 1);
interpreter->Run();
CHECK_EQ(2, interpreter->GetThread(0).GetReturnValue().to<int32_t>());
r.Build(code, code + arraysize(code));
WasmInterpreter* interpreter = r.interpreter();
WasmInterpreter::Thread& thread = interpreter->GetThread(0);
FOR_UINT32_INPUTS(a) {
for (uint32_t b = 33; b < 3000000000u; b += 1000000000u) {
thread.Reset();
WasmVal args[] = {WasmVal(*a), WasmVal(b)};
thread.PushFrame(r.function(), args);
// Run instructions one by one.
for (int i = 0; i < kTraceLength - 1; i++) {
thread.Step();
// Check the thread stopped.
CHECK_EQ(WasmInterpreter::PAUSED, thread.state());
}
// Run last instruction.
thread.Step();
// Check the thread finished with the right value.
CHECK_EQ(WasmInterpreter::FINISHED, thread.state());
uint32_t expected = (*a) * (b);
CHECK_EQ(expected, thread.GetReturnValue().to<uint32_t>());
}
}
}
TEST(Breakpoint_I32And_disable) {
static const int kLocalsDeclSize = 1;
static const int kNumBreakpoints = 1;
byte code[] = {WASM_I32_AND(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1))};
SmartArrayPointer<int> offsets =
Find(code, sizeof(code), kNumBreakpoints, kExprI32And);
WasmRunner<int32_t> r(kExecuteInterpreted, MachineType::Uint32(),
MachineType::Uint32());
r.Build(code, code + arraysize(code));
WasmInterpreter* interpreter = r.interpreter();
WasmInterpreter::Thread& thread = interpreter->GetThread(0);
FOR_UINT32_INPUTS(a) {
for (uint32_t b = 11; b < 3000000000u; b += 1000000000u) {
// Run with and without breakpoints.
for (int do_break = 0; do_break < 2; do_break++) {
interpreter->SetBreakpoint(r.function(), kLocalsDeclSize + offsets[0],
do_break);
thread.Reset();
WasmVal args[] = {WasmVal(*a), WasmVal(b)};
thread.PushFrame(r.function(), args);
if (do_break) {
thread.Run(); // run to next breakpoint
// Check the thread stopped at the right pc.
CHECK_EQ(WasmInterpreter::PAUSED, thread.state());
CHECK_EQ(kLocalsDeclSize + offsets[0], thread.GetBreakpointPc());
}
thread.Run(); // run to completion
// Check the thread finished with the right value.
CHECK_EQ(WasmInterpreter::FINISHED, thread.state());
uint32_t expected = (*a) & (b);
CHECK_EQ(expected, thread.GetReturnValue().to<uint32_t>());
}
}
}
}
} // namespace wasm
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment