Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2016 the V8 project authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "src/debug/debug-interface.h" | 5 #include "src/debug/debug-interface.h" |
| 6 #include "src/property-descriptor.h" | |
| 6 #include "src/wasm/wasm-macro-gen.h" | 7 #include "src/wasm/wasm-macro-gen.h" |
| 7 #include "src/wasm/wasm-objects.h" | 8 #include "src/wasm/wasm-objects.h" |
| 8 | 9 |
| 9 #include "test/cctest/cctest.h" | 10 #include "test/cctest/cctest.h" |
| 10 #include "test/cctest/compiler/value-helper.h" | 11 #include "test/cctest/compiler/value-helper.h" |
| 11 #include "test/cctest/wasm/wasm-run-utils.h" | 12 #include "test/cctest/wasm/wasm-run-utils.h" |
| 12 #include "test/common/wasm/test-signatures.h" | 13 #include "test/common/wasm/test-signatures.h" |
| 13 | 14 |
| 14 using namespace v8::internal; | 15 using namespace v8::internal; |
| 15 using namespace v8::internal::wasm; | 16 using namespace v8::internal::wasm; |
| (...skipping 27 matching lines...) Expand all Loading... | |
| 43 } | 44 } |
| 44 } | 45 } |
| 45 void CheckLocationsFail(WasmCompiledModule *compiled_module, | 46 void CheckLocationsFail(WasmCompiledModule *compiled_module, |
| 46 debug::Location start, debug::Location end) { | 47 debug::Location start, debug::Location end) { |
| 47 std::vector<debug::Location> locations; | 48 std::vector<debug::Location> locations; |
| 48 bool success = | 49 bool success = |
| 49 compiled_module->GetPossibleBreakpoints(start, end, &locations); | 50 compiled_module->GetPossibleBreakpoints(start, end, &locations); |
| 50 CHECK(!success); | 51 CHECK(!success); |
| 51 } | 52 } |
| 52 | 53 |
| 54 class BreakHandler { | |
| 55 public: | |
| 56 explicit BreakHandler(Isolate* isolate) : isolate_(isolate) { | |
| 57 current_handler = this; | |
| 58 isolate->debug()->SetMessageHandler(&HandleMessage); | |
| 59 } | |
| 60 ~BreakHandler() { | |
| 61 CHECK_EQ(this, current_handler); | |
| 62 current_handler = nullptr; | |
| 63 isolate_->debug()->SetMessageHandler(nullptr); | |
| 64 } | |
| 65 | |
| 66 int count() const { return count_; } | |
| 67 | |
| 68 private: | |
| 69 Isolate* isolate_; | |
| 70 int count_ = 0; | |
| 71 | |
| 72 static BreakHandler* current_handler; | |
| 73 | |
| 74 static void HandleMessage(const v8::Debug::Message& message) { | |
| 75 // Ignore responses. | |
| 76 if (!message.IsEvent()) return; | |
| 77 // Ignore everything except break events. | |
| 78 if (message.GetEvent() != v8::DebugEvent::Break) return; | |
| 79 | |
| 80 printf("break!\n"); | |
| 81 CHECK_NOT_NULL(current_handler); | |
| 82 current_handler->count_ += 1; | |
| 83 // Don't run into an endless loop. | |
| 84 CHECK_GT(100, current_handler->count_); | |
| 85 | |
| 86 const char command[] = "{\"type\":\"request\", \"command\":\"continue\"}"; | |
| 87 uint16_t command_u16[arraysize(command) - 1]; | |
| 88 for (unsigned i = 0; i < arraysize(command) - 1; ++i) { | |
| 89 command_u16[i] = command[i]; | |
| 90 } | |
| 91 current_handler->isolate_->debug()->EnqueueCommandMessage( | |
| 92 ArrayVector(command_u16), message.GetClientData()); | |
| 93 } | |
| 94 }; | |
| 95 | |
| 96 // static | |
| 97 BreakHandler* BreakHandler::current_handler = nullptr; | |
| 98 | |
| 99 Handle<JSObject> MakeFakeBreakpoint(Isolate* isolate, int position) { | |
| 100 Handle<JSObject> obj = | |
| 101 isolate->factory()->NewJSObject(isolate->object_function()); | |
| 102 // Generate an "isTriggered" method that always returns true. | |
| 103 // This can/must be refactored once we remove remaining JS parts from the | |
| 104 // debugger (bug 5530). | |
| 105 Handle<String> source = isolate->factory()->NewStringFromStaticChars("true"); | |
| 106 Handle<Context> context(isolate->context(), isolate); | |
| 107 Handle<JSFunction> triggered_fun = | |
| 108 Compiler::GetFunctionFromString(context, source, NO_PARSE_RESTRICTION) | |
| 109 .ToHandleChecked(); | |
| 110 PropertyDescriptor desc; | |
| 111 desc.set_value(triggered_fun); | |
| 112 Handle<String> name = | |
| 113 isolate->factory()->InternalizeUtf8String(CStrVector("isTriggered")); | |
| 114 CHECK( | |
| 115 JSObject::DefineOwnProperty(isolate, obj, name, &desc, Object::DONT_THROW) | |
| 116 .FromMaybe(false)); | |
| 117 return obj; | |
| 118 } | |
| 119 | |
| 120 void SetBreakpoint(WasmRunnerBase& runner, int function_index, int byte_offset, | |
| 121 int expected_set_byte_offset = -1) { | |
| 122 int func_offset = | |
| 123 runner.module().module->functions[function_index].code_start_offset; | |
| 124 int code_offset = func_offset + byte_offset; | |
| 125 if (expected_set_byte_offset == -1) expected_set_byte_offset = byte_offset; | |
| 126 Handle<WasmInstanceObject> instance = runner.module().instance_object(); | |
| 127 Handle<WasmCompiledModule> compiled_module(instance->compiled_module()); | |
| 128 Handle<JSObject> fake_breakpoint_object = | |
| 129 MakeFakeBreakpoint(runner.main_isolate(), code_offset); | |
| 130 CHECK(WasmCompiledModule::SetBreakPoint(compiled_module, &code_offset, | |
| 131 fake_breakpoint_object)); | |
| 132 int set_byte_offset = code_offset - func_offset; | |
| 133 CHECK_EQ(expected_set_byte_offset, set_byte_offset); | |
| 134 // Also set breakpoint on the debug info of the instance directly, since the | |
| 135 // instance chain is not setup properly in tests. | |
| 136 Handle<WasmDebugInfo> debug_info = | |
| 137 WasmInstanceObject::GetOrCreateDebugInfo(instance); | |
| 138 WasmDebugInfo::SetBreakpoint(debug_info, function_index, set_byte_offset); | |
| 139 } | |
| 140 | |
| 53 } // namespace | 141 } // namespace |
| 54 | 142 |
| 55 TEST(CollectPossibleBreakpoints) { | 143 TEST(CollectPossibleBreakpoints) { |
| 56 WasmRunner<int> runner(kExecuteCompiled); | 144 WasmRunner<int> runner(kExecuteCompiled); |
| 57 | 145 |
| 58 BUILD(runner, WASM_NOP, WASM_I32_ADD(WASM_ZERO, WASM_ONE)); | 146 BUILD(runner, WASM_NOP, WASM_I32_ADD(WASM_ZERO, WASM_ONE)); |
| 59 | 147 |
| 60 Handle<WasmInstanceObject> instance = runner.module().instance_object(); | 148 Handle<WasmInstanceObject> instance = runner.module().instance_object(); |
| 61 std::vector<debug::Location> locations; | 149 std::vector<debug::Location> locations; |
| 150 // Check all locations for function 0. | |
| 62 CheckLocations(instance->compiled_module(), {0, 0}, {1, 0}, | 151 CheckLocations(instance->compiled_module(), {0, 0}, {1, 0}, |
| 63 {{0, 1}, {0, 2}, {0, 4}, {0, 6}, {0, 7}}); | 152 {{0, 1}, {0, 2}, {0, 4}, {0, 6}, {0, 7}}); |
| 153 // Check a range ending at an instruction. | |
| 64 CheckLocations(instance->compiled_module(), {0, 2}, {0, 4}, {{0, 2}}); | 154 CheckLocations(instance->compiled_module(), {0, 2}, {0, 4}, {{0, 2}}); |
| 155 // Check a range ending one behind an instruction. | |
| 65 CheckLocations(instance->compiled_module(), {0, 2}, {0, 5}, {{0, 2}, {0, 4}}); | 156 CheckLocations(instance->compiled_module(), {0, 2}, {0, 5}, {{0, 2}, {0, 4}}); |
| 157 // Check a range starting at an instruction. | |
| 66 CheckLocations(instance->compiled_module(), {0, 7}, {0, 8}, {{0, 7}}); | 158 CheckLocations(instance->compiled_module(), {0, 7}, {0, 8}, {{0, 7}}); |
| 159 // Check from an instruction to beginning of next function. | |
| 67 CheckLocations(instance->compiled_module(), {0, 7}, {1, 0}, {{0, 7}}); | 160 CheckLocations(instance->compiled_module(), {0, 7}, {1, 0}, {{0, 7}}); |
| 161 // Check from end of one function (no valid instruction position) to beginning | |
| 162 // of next function. Must be empty, but not fail. | |
| 68 CheckLocations(instance->compiled_module(), {0, 8}, {1, 0}, {}); | 163 CheckLocations(instance->compiled_module(), {0, 8}, {1, 0}, {}); |
| 164 // Check from one after the end of the function. Must fail. | |
| 69 CheckLocationsFail(instance->compiled_module(), {0, 9}, {1, 0}); | 165 CheckLocationsFail(instance->compiled_module(), {0, 9}, {1, 0}); |
| 70 } | 166 } |
| 167 | |
| 168 TEST(TestSimpleBreak) { | |
| 169 WasmRunner<int> runner(kExecuteCompiled); | |
| 170 Isolate* isolate = runner.main_isolate(); | |
| 171 | |
| 172 BUILD(runner, WASM_NOP, WASM_I32_ADD(WASM_I32V_1(11), WASM_I32V_1(3))); | |
| 173 | |
| 174 Handle<JSFunction> main_fun_wrapper = | |
| 175 runner.module().WrapCode(runner.function_index()); | |
| 176 SetBreakpoint(runner, runner.function_index(), 4, 4); | |
| 177 | |
| 178 BreakHandler count_breaks(isolate); | |
| 179 CHECK_EQ(0, count_breaks.count()); | |
| 180 | |
| 181 Handle<Object> global(isolate->context()->global_object(), isolate); | |
| 182 MaybeHandle<Object> retval = | |
| 183 Execution::Call(isolate, main_fun_wrapper, global, 0, nullptr); | |
| 184 CHECK(!retval.is_null()); | |
| 185 int result; | |
| 186 CHECK(retval.ToHandleChecked()->ToInt32(&result)); | |
| 187 CHECK_EQ(14, result); | |
| 188 | |
| 189 CHECK_EQ(1, count_breaks.count()); | |
| 190 } | |
| 191 | |
| 192 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
| |
| 193 // The second and third argument will be combined to an i64. | |
| 194 WasmRunner<double, int, int, int, float, double> runner(kExecuteCompiled); | |
| 195 WasmFunctionCompiler& f2 = | |
| 196 runner.NewFunction<double, int, int64_t, float, double>(); | |
| 197 | |
| 198 // Convert all arguments to double, add them and return the sum. | |
| 199 BUILD(f2, | |
| 200 WASM_F64_ADD( // <0+1+2> + <3> | |
| 201 WASM_F64_ADD( // <0+1> + <2> | |
| 202 WASM_F64_ADD( // <0> + <1> | |
| 203 WASM_F64_SCONVERT_I32(WASM_GET_LOCAL(0)), // <0> to double | |
| 204 WASM_F64_SCONVERT_I64(WASM_GET_LOCAL(1))), // <1> to double | |
| 205 WASM_F64_CONVERT_F32(WASM_GET_LOCAL(2))), // <2> to double | |
| 206 WASM_GET_LOCAL(3))); // <3> | |
| 207 | |
| 208 BUILD(runner, WASM_GET_LOCAL(0), // first arg | |
| 209 WASM_I64_IOR(WASM_I64_UCONVERT_I32(WASM_GET_LOCAL(1)), // second arg | |
| 210 WASM_I64_SHL(WASM_I64_UCONVERT_I32(WASM_GET_LOCAL(2)), | |
| 211 WASM_I64V_1(32))), | |
| 212 WASM_GET_LOCAL(3), // third arg | |
| 213 WASM_GET_LOCAL(4), // fourth arg | |
| 214 WASM_CALL_FUNCTION0(f2.function_index())); | |
| 215 | |
| 216 // Set a breakpoint to check that we really execute in the interpreter. | |
| 217 SetBreakpoint(runner, f2.function_index(), 1, 1); | |
| 218 | |
| 219 Handle<JSFunction> main_fun_wrapper = | |
| 220 runner.module().WrapCode(runner.function_index()); | |
| 221 Isolate* isolate = runner.main_isolate(); | |
| 222 Handle<Object> global(isolate->context()->global_object(), isolate); | |
| 223 | |
| 224 BreakHandler count_breaks(isolate); | |
| 225 | |
| 226 auto CheckCall = [&](int32_t a, int64_t b, float c, double d) { | |
| 227 Handle<Object> args[5] = { | |
| 228 isolate->factory()->NewNumberFromInt(a), | |
| 229 isolate->factory()->NewNumberFromInt((b & 0xffffffff)), | |
| 230 isolate->factory()->NewNumberFromInt((b >> 32) & 0xffffffff), | |
| 231 isolate->factory()->NewHeapNumber(c), | |
| 232 isolate->factory()->NewHeapNumber(d)}; | |
| 233 | |
| 234 int num_breaks_before = count_breaks.count(); | |
| 235 MaybeHandle<Object> retval = Execution::Call(isolate, main_fun_wrapper, | |
| 236 global, arraysize(args), args); | |
| 237 // Check that we really went through the interpreter, then check the result. | |
| 238 CHECK_EQ(num_breaks_before + 1, count_breaks.count()); | |
| 239 double result = retval.ToHandleChecked()->Number(); | |
| 240 double sum = static_cast<double>(a) + static_cast<double>(b) + | |
| 241 static_cast<double>(c) + static_cast<double>(d); | |
| 242 CHECK_EQ(sum, result); | |
| 243 }; | |
| 244 | |
| 245 CheckCall(1, 2, 3, 4); | |
| 246 CheckCall(1, 2, 4, 8); | |
| 247 CheckCall(std::numeric_limits<int32_t>::max(), | |
| 248 std::numeric_limits<int64_t>::max(), 0, 0); | |
| 249 CheckCall(0, 0, 0.1f, 0.3); | |
| 250 CheckCall(0x0ff00000, 0, 0, 0); | |
| 251 CheckCall(0x00000ff0, 0, 0, 0); | |
| 252 CheckCall(0, 0x0000000000000ff0, 0, 0); | |
| 253 CheckCall(0, 0x000000000ff00000, 0, 0); | |
| 254 CheckCall(0, 0x00000ff000000000, 0, 0); | |
| 255 CheckCall(0, 0x0ff0000000000000, 0, 0); | |
| 256 CheckCall(-3, -11, 0, 0); | |
| 257 CheckCall(0, 0, -3, -11); | |
| 258 } | |
| OLD | NEW |