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 } |
OLD | NEW |