Chromium Code Reviews| Index: test/cctest/wasm/test-wasm-breakpoints.cc |
| diff --git a/test/cctest/wasm/test-wasm-breakpoints.cc b/test/cctest/wasm/test-wasm-breakpoints.cc |
| index d2374a44c02c99ff16091d62bd42c1f54ee7b39d..2fa6020767f5972f94c6fde1f866f54a5849ee73 100644 |
| --- a/test/cctest/wasm/test-wasm-breakpoints.cc |
| +++ b/test/cctest/wasm/test-wasm-breakpoints.cc |
| @@ -3,6 +3,7 @@ |
| // found in the LICENSE file. |
| #include "src/debug/debug-interface.h" |
| +#include "src/property-descriptor.h" |
| #include "src/wasm/wasm-macro-gen.h" |
| #include "src/wasm/wasm-objects.h" |
| @@ -50,6 +51,93 @@ void CheckLocationsFail(WasmCompiledModule *compiled_module, |
| CHECK(!success); |
| } |
| +class BreakHandler { |
| + public: |
| + explicit BreakHandler(Isolate* isolate) : isolate_(isolate) { |
| + current_handler = this; |
| + isolate->debug()->SetMessageHandler(&HandleMessage); |
| + } |
| + ~BreakHandler() { |
| + CHECK_EQ(this, current_handler); |
| + current_handler = nullptr; |
| + isolate_->debug()->SetMessageHandler(nullptr); |
| + } |
| + |
| + int count() const { return count_; } |
| + |
| + private: |
| + Isolate* isolate_; |
| + int count_ = 0; |
| + |
| + static BreakHandler* current_handler; |
| + |
| + static void HandleMessage(const v8::Debug::Message& message) { |
| + // Ignore responses. |
| + if (!message.IsEvent()) return; |
| + // Ignore everything except break events. |
| + if (message.GetEvent() != v8::DebugEvent::Break) return; |
| + |
| + printf("break!\n"); |
| + CHECK_NOT_NULL(current_handler); |
| + current_handler->count_ += 1; |
| + // Don't run into an endless loop. |
| + CHECK_GT(100, current_handler->count_); |
| + |
| + const char command[] = "{\"type\":\"request\", \"command\":\"continue\"}"; |
| + uint16_t command_u16[arraysize(command) - 1]; |
| + for (unsigned i = 0; i < arraysize(command) - 1; ++i) { |
| + command_u16[i] = command[i]; |
| + } |
| + current_handler->isolate_->debug()->EnqueueCommandMessage( |
| + ArrayVector(command_u16), message.GetClientData()); |
| + } |
| +}; |
| + |
| +// static |
| +BreakHandler* BreakHandler::current_handler = nullptr; |
| + |
| +Handle<JSObject> MakeFakeBreakpoint(Isolate* isolate, int position) { |
| + Handle<JSObject> obj = |
| + isolate->factory()->NewJSObject(isolate->object_function()); |
| + // Generate an "isTriggered" method that always returns true. |
| + // This can/must be refactored once we remove remaining JS parts from the |
| + // debugger (bug 5530). |
| + Handle<String> source = isolate->factory()->NewStringFromStaticChars("true"); |
| + Handle<Context> context(isolate->context(), isolate); |
| + Handle<JSFunction> triggered_fun = |
| + Compiler::GetFunctionFromString(context, source, NO_PARSE_RESTRICTION) |
| + .ToHandleChecked(); |
| + PropertyDescriptor desc; |
| + desc.set_value(triggered_fun); |
| + Handle<String> name = |
| + isolate->factory()->InternalizeUtf8String(CStrVector("isTriggered")); |
| + CHECK( |
| + JSObject::DefineOwnProperty(isolate, obj, name, &desc, Object::DONT_THROW) |
| + .FromMaybe(false)); |
| + return obj; |
| +} |
| + |
| +void SetBreakpoint(WasmRunnerBase& runner, int function_index, int byte_offset, |
| + int expected_set_byte_offset = -1) { |
| + int func_offset = |
| + runner.module().module->functions[function_index].code_start_offset; |
| + int code_offset = func_offset + byte_offset; |
| + if (expected_set_byte_offset == -1) expected_set_byte_offset = byte_offset; |
| + Handle<WasmInstanceObject> instance = runner.module().instance_object(); |
| + Handle<WasmCompiledModule> compiled_module(instance->compiled_module()); |
| + Handle<JSObject> fake_breakpoint_object = |
| + MakeFakeBreakpoint(runner.main_isolate(), code_offset); |
| + CHECK(WasmCompiledModule::SetBreakPoint(compiled_module, &code_offset, |
| + fake_breakpoint_object)); |
| + int set_byte_offset = code_offset - func_offset; |
| + CHECK_EQ(expected_set_byte_offset, set_byte_offset); |
| + // Also set breakpoint on the debug info of the instance directly, since the |
| + // instance chain is not setup properly in tests. |
| + Handle<WasmDebugInfo> debug_info = |
| + WasmInstanceObject::GetOrCreateDebugInfo(instance); |
| + WasmDebugInfo::SetBreakpoint(debug_info, function_index, set_byte_offset); |
| +} |
| + |
| } // namespace |
| TEST(CollectPossibleBreakpoints) { |
| @@ -59,12 +147,112 @@ TEST(CollectPossibleBreakpoints) { |
| Handle<WasmInstanceObject> instance = runner.module().instance_object(); |
| std::vector<debug::Location> locations; |
| + // Check all locations for function 0. |
| CheckLocations(instance->compiled_module(), {0, 0}, {1, 0}, |
| {{0, 1}, {0, 2}, {0, 4}, {0, 6}, {0, 7}}); |
| + // Check a range ending at an instruction. |
| CheckLocations(instance->compiled_module(), {0, 2}, {0, 4}, {{0, 2}}); |
| + // Check a range ending one behind an instruction. |
| CheckLocations(instance->compiled_module(), {0, 2}, {0, 5}, {{0, 2}, {0, 4}}); |
| + // Check a range starting at an instruction. |
| CheckLocations(instance->compiled_module(), {0, 7}, {0, 8}, {{0, 7}}); |
| + // Check from an instruction to beginning of next function. |
| CheckLocations(instance->compiled_module(), {0, 7}, {1, 0}, {{0, 7}}); |
| + // Check from end of one function (no valid instruction position) to beginning |
| + // of next function. Must be empty, but not fail. |
| CheckLocations(instance->compiled_module(), {0, 8}, {1, 0}, {}); |
| + // Check from one after the end of the function. Must fail. |
| CheckLocationsFail(instance->compiled_module(), {0, 9}, {1, 0}); |
| } |
| + |
| +TEST(TestSimpleBreak) { |
| + WasmRunner<int> runner(kExecuteCompiled); |
| + Isolate* isolate = runner.main_isolate(); |
| + |
| + BUILD(runner, WASM_NOP, WASM_I32_ADD(WASM_I32V_1(11), WASM_I32V_1(3))); |
| + |
| + Handle<JSFunction> main_fun_wrapper = |
| + runner.module().WrapCode(runner.function_index()); |
| + SetBreakpoint(runner, runner.function_index(), 4, 4); |
| + |
| + BreakHandler count_breaks(isolate); |
| + CHECK_EQ(0, count_breaks.count()); |
| + |
| + Handle<Object> global(isolate->context()->global_object(), isolate); |
| + MaybeHandle<Object> retval = |
| + Execution::Call(isolate, main_fun_wrapper, global, 0, nullptr); |
| + CHECK(!retval.is_null()); |
| + int result; |
| + CHECK(retval.ToHandleChecked()->ToInt32(&result)); |
| + CHECK_EQ(14, result); |
| + |
| + CHECK_EQ(1, count_breaks.count()); |
| +} |
| + |
| +TEST(TestArgumentPassing) { |
|
titzer
2017/01/20 13:08:04
Can you split this test up as per previous comment
Clemens Hammacher
2017/01/20 13:15:41
As discussed offline: Removed argument passing tes
|
| + // The second and third argument will be combined to an i64. |
| + WasmRunner<double, int, int, int, float, double> runner(kExecuteCompiled); |
| + WasmFunctionCompiler& f2 = |
| + runner.NewFunction<double, int, int64_t, float, double>(); |
| + |
| + // Convert all arguments to double, add them and return the sum. |
| + BUILD(f2, |
| + WASM_F64_ADD( // <0+1+2> + <3> |
| + WASM_F64_ADD( // <0+1> + <2> |
| + WASM_F64_ADD( // <0> + <1> |
| + WASM_F64_SCONVERT_I32(WASM_GET_LOCAL(0)), // <0> to double |
| + WASM_F64_SCONVERT_I64(WASM_GET_LOCAL(1))), // <1> to double |
| + WASM_F64_CONVERT_F32(WASM_GET_LOCAL(2))), // <2> to double |
| + WASM_GET_LOCAL(3))); // <3> |
| + |
| + BUILD(runner, WASM_GET_LOCAL(0), // first arg |
| + WASM_I64_IOR(WASM_I64_UCONVERT_I32(WASM_GET_LOCAL(1)), // second arg |
| + WASM_I64_SHL(WASM_I64_UCONVERT_I32(WASM_GET_LOCAL(2)), |
| + WASM_I64V_1(32))), |
| + WASM_GET_LOCAL(3), // third arg |
| + WASM_GET_LOCAL(4), // fourth arg |
| + WASM_CALL_FUNCTION0(f2.function_index())); |
| + |
| + // Set a breakpoint to check that we really execute in the interpreter. |
| + SetBreakpoint(runner, f2.function_index(), 1, 1); |
| + |
| + Handle<JSFunction> main_fun_wrapper = |
| + runner.module().WrapCode(runner.function_index()); |
| + Isolate* isolate = runner.main_isolate(); |
| + Handle<Object> global(isolate->context()->global_object(), isolate); |
| + |
| + BreakHandler count_breaks(isolate); |
| + |
| + auto CheckCall = [&](int32_t a, int64_t b, float c, double d) { |
| + Handle<Object> args[5] = { |
| + isolate->factory()->NewNumberFromInt(a), |
| + isolate->factory()->NewNumberFromInt((b & 0xffffffff)), |
| + isolate->factory()->NewNumberFromInt((b >> 32) & 0xffffffff), |
| + isolate->factory()->NewHeapNumber(c), |
| + isolate->factory()->NewHeapNumber(d)}; |
| + |
| + int num_breaks_before = count_breaks.count(); |
| + MaybeHandle<Object> retval = Execution::Call(isolate, main_fun_wrapper, |
| + global, arraysize(args), args); |
| + // Check that we really went through the interpreter, then check the result. |
| + CHECK_EQ(num_breaks_before + 1, count_breaks.count()); |
| + double result = retval.ToHandleChecked()->Number(); |
| + double sum = static_cast<double>(a) + static_cast<double>(b) + |
| + static_cast<double>(c) + static_cast<double>(d); |
| + CHECK_EQ(sum, result); |
| + }; |
| + |
| + CheckCall(1, 2, 3, 4); |
| + CheckCall(1, 2, 4, 8); |
| + CheckCall(std::numeric_limits<int32_t>::max(), |
| + std::numeric_limits<int64_t>::max(), 0, 0); |
| + CheckCall(0, 0, 0.1f, 0.3); |
| + CheckCall(0x0ff00000, 0, 0, 0); |
| + CheckCall(0x00000ff0, 0, 0, 0); |
| + CheckCall(0, 0x0000000000000ff0, 0, 0); |
| + CheckCall(0, 0x000000000ff00000, 0, 0); |
| + CheckCall(0, 0x00000ff000000000, 0, 0); |
| + CheckCall(0, 0x0ff0000000000000, 0, 0); |
| + CheckCall(-3, -11, 0, 0); |
| + CheckCall(0, 0, -3, -11); |
| +} |