| 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/frames-inl.h" |
| 6 #include "src/property-descriptor.h" | 7 #include "src/property-descriptor.h" |
| 8 #include "src/utils.h" |
| 7 #include "src/wasm/wasm-macro-gen.h" | 9 #include "src/wasm/wasm-macro-gen.h" |
| 8 #include "src/wasm/wasm-objects.h" | 10 #include "src/wasm/wasm-objects.h" |
| 9 | 11 |
| 10 #include "test/cctest/cctest.h" | 12 #include "test/cctest/cctest.h" |
| 11 #include "test/cctest/compiler/value-helper.h" | 13 #include "test/cctest/compiler/value-helper.h" |
| 12 #include "test/cctest/wasm/wasm-run-utils.h" | 14 #include "test/cctest/wasm/wasm-run-utils.h" |
| 13 #include "test/common/wasm/test-signatures.h" | 15 #include "test/common/wasm/test-signatures.h" |
| 14 | 16 |
| 15 using namespace v8::internal; | 17 using namespace v8::internal; |
| 16 using namespace v8::internal::wasm; | 18 using namespace v8::internal::wasm; |
| (...skipping 29 matching lines...) Expand all Loading... |
| 46 void CheckLocationsFail(WasmCompiledModule *compiled_module, | 48 void CheckLocationsFail(WasmCompiledModule *compiled_module, |
| 47 debug::Location start, debug::Location end) { | 49 debug::Location start, debug::Location end) { |
| 48 std::vector<debug::Location> locations; | 50 std::vector<debug::Location> locations; |
| 49 bool success = | 51 bool success = |
| 50 compiled_module->GetPossibleBreakpoints(start, end, &locations); | 52 compiled_module->GetPossibleBreakpoints(start, end, &locations); |
| 51 CHECK(!success); | 53 CHECK(!success); |
| 52 } | 54 } |
| 53 | 55 |
| 54 class BreakHandler { | 56 class BreakHandler { |
| 55 public: | 57 public: |
| 56 explicit BreakHandler(Isolate* isolate) : isolate_(isolate) { | 58 enum Action { |
| 59 Continue = StepAction::LastStepAction + 1, |
| 60 StepNext = StepAction::StepNext, |
| 61 StepIn = StepAction::StepIn, |
| 62 StepOut = StepAction::StepOut, |
| 63 StepFrame = StepAction::StepFrame |
| 64 }; |
| 65 struct BreakPoint { |
| 66 int position; |
| 67 Action action; |
| 68 BreakPoint(int position, Action action) |
| 69 : position(position), action(action) {} |
| 70 }; |
| 71 |
| 72 explicit BreakHandler(Isolate* isolate, |
| 73 std::initializer_list<BreakPoint> expected_breaks) |
| 74 : isolate_(isolate), expected_breaks_(expected_breaks) { |
| 57 current_handler = this; | 75 current_handler = this; |
| 58 v8::Debug::SetDebugEventListener(reinterpret_cast<v8::Isolate*>(isolate), | 76 v8::Debug::SetDebugEventListener(reinterpret_cast<v8::Isolate*>(isolate), |
| 59 DebugEventListener); | 77 DebugEventListener); |
| 60 } | 78 } |
| 61 ~BreakHandler() { | 79 ~BreakHandler() { |
| 80 // Check that all expected breakpoints have been hit. |
| 81 CHECK_EQ(count_, expected_breaks_.size()); |
| 82 // BreakHandlers must be correctly stacked. |
| 62 CHECK_EQ(this, current_handler); | 83 CHECK_EQ(this, current_handler); |
| 63 current_handler = nullptr; | 84 current_handler = nullptr; |
| 64 v8::Debug::SetDebugEventListener(reinterpret_cast<v8::Isolate*>(isolate_), | 85 v8::Debug::SetDebugEventListener(reinterpret_cast<v8::Isolate*>(isolate_), |
| 65 nullptr); | 86 nullptr); |
| 66 } | 87 } |
| 67 | 88 |
| 68 int count() const { return count_; } | 89 int count() const { return count_; } |
| 69 | 90 |
| 70 private: | 91 private: |
| 71 Isolate* isolate_; | 92 Isolate* isolate_; |
| 72 int count_ = 0; | 93 int count_ = 0; |
| 94 std::vector<BreakPoint> expected_breaks_; |
| 73 | 95 |
| 74 static BreakHandler* current_handler; | 96 static BreakHandler* current_handler; |
| 75 | 97 |
| 98 void HandleBreak() { |
| 99 printf("Break #%d\n", count_); |
| 100 CHECK_GT(expected_breaks_.size(), count_); |
| 101 |
| 102 // Check the current position. |
| 103 StackTraceFrameIterator frame_it(isolate_); |
| 104 auto summ = FrameSummary::GetTop(frame_it.frame()).AsWasmInterpreted(); |
| 105 CHECK_EQ(expected_breaks_[count_].position, summ.byte_offset()); |
| 106 |
| 107 Action next_action = expected_breaks_[count_].action; |
| 108 switch (next_action) { |
| 109 case Continue: |
| 110 break; |
| 111 case StepNext: |
| 112 case StepIn: |
| 113 case StepOut: |
| 114 case StepFrame: |
| 115 isolate_->debug()->PrepareStep(static_cast<StepAction>(next_action)); |
| 116 break; |
| 117 default: |
| 118 UNREACHABLE(); |
| 119 } |
| 120 ++count_; |
| 121 } |
| 122 |
| 76 static void DebugEventListener(const v8::Debug::EventDetails& event_details) { | 123 static void DebugEventListener(const v8::Debug::EventDetails& event_details) { |
| 77 if (event_details.GetEvent() != v8::DebugEvent::Break) return; | 124 if (event_details.GetEvent() != v8::DebugEvent::Break) return; |
| 78 | 125 |
| 79 printf("break!\n"); | |
| 80 CHECK_NOT_NULL(current_handler); | 126 CHECK_NOT_NULL(current_handler); |
| 81 current_handler->count_ += 1; | 127 current_handler->HandleBreak(); |
| 82 // Don't run into an endless loop. | |
| 83 CHECK_GT(100, current_handler->count_); | |
| 84 } | 128 } |
| 85 }; | 129 }; |
| 86 | 130 |
| 87 // static | 131 // static |
| 88 BreakHandler* BreakHandler::current_handler = nullptr; | 132 BreakHandler* BreakHandler::current_handler = nullptr; |
| 89 | 133 |
| 90 Handle<JSObject> MakeFakeBreakpoint(Isolate* isolate, int position) { | 134 Handle<JSObject> MakeFakeBreakpoint(Isolate* isolate, int position) { |
| 91 Handle<JSObject> obj = | 135 Handle<JSObject> obj = |
| 92 isolate->factory()->NewJSObject(isolate->object_function()); | 136 isolate->factory()->NewJSObject(isolate->object_function()); |
| 93 // Generate an "isTriggered" method that always returns true. | 137 // Generate an "isTriggered" method that always returns true. |
| (...skipping 30 matching lines...) Expand all Loading... |
| 124 CHECK_EQ(expected_set_byte_offset, set_byte_offset); | 168 CHECK_EQ(expected_set_byte_offset, set_byte_offset); |
| 125 // Also set breakpoint on the debug info of the instance directly, since the | 169 // Also set breakpoint on the debug info of the instance directly, since the |
| 126 // instance chain is not setup properly in tests. | 170 // instance chain is not setup properly in tests. |
| 127 Handle<WasmDebugInfo> debug_info = | 171 Handle<WasmDebugInfo> debug_info = |
| 128 WasmInstanceObject::GetOrCreateDebugInfo(instance); | 172 WasmInstanceObject::GetOrCreateDebugInfo(instance); |
| 129 WasmDebugInfo::SetBreakpoint(debug_info, function_index, set_byte_offset); | 173 WasmDebugInfo::SetBreakpoint(debug_info, function_index, set_byte_offset); |
| 130 } | 174 } |
| 131 | 175 |
| 132 } // namespace | 176 } // namespace |
| 133 | 177 |
| 134 TEST(CollectPossibleBreakpoints) { | 178 TEST(WasmCollectPossibleBreakpoints) { |
| 135 WasmRunner<int> runner(kExecuteCompiled); | 179 WasmRunner<int> runner(kExecuteCompiled); |
| 136 | 180 |
| 137 BUILD(runner, WASM_NOP, WASM_I32_ADD(WASM_ZERO, WASM_ONE)); | 181 BUILD(runner, WASM_NOP, WASM_I32_ADD(WASM_ZERO, WASM_ONE)); |
| 138 | 182 |
| 139 Handle<WasmInstanceObject> instance = runner.module().instance_object(); | 183 Handle<WasmInstanceObject> instance = runner.module().instance_object(); |
| 140 std::vector<debug::Location> locations; | 184 std::vector<debug::Location> locations; |
| 141 // Check all locations for function 0. | 185 // Check all locations for function 0. |
| 142 CheckLocations(instance->compiled_module(), {0, 0}, {1, 0}, | 186 CheckLocations(instance->compiled_module(), {0, 0}, {1, 0}, |
| 143 {{0, 1}, {0, 2}, {0, 4}, {0, 6}, {0, 7}}); | 187 {{0, 1}, {0, 2}, {0, 4}, {0, 6}, {0, 7}}); |
| 144 // Check a range ending at an instruction. | 188 // Check a range ending at an instruction. |
| 145 CheckLocations(instance->compiled_module(), {0, 2}, {0, 4}, {{0, 2}}); | 189 CheckLocations(instance->compiled_module(), {0, 2}, {0, 4}, {{0, 2}}); |
| 146 // Check a range ending one behind an instruction. | 190 // Check a range ending one behind an instruction. |
| 147 CheckLocations(instance->compiled_module(), {0, 2}, {0, 5}, {{0, 2}, {0, 4}}); | 191 CheckLocations(instance->compiled_module(), {0, 2}, {0, 5}, {{0, 2}, {0, 4}}); |
| 148 // Check a range starting at an instruction. | 192 // Check a range starting at an instruction. |
| 149 CheckLocations(instance->compiled_module(), {0, 7}, {0, 8}, {{0, 7}}); | 193 CheckLocations(instance->compiled_module(), {0, 7}, {0, 8}, {{0, 7}}); |
| 150 // Check from an instruction to beginning of next function. | 194 // Check from an instruction to beginning of next function. |
| 151 CheckLocations(instance->compiled_module(), {0, 7}, {1, 0}, {{0, 7}}); | 195 CheckLocations(instance->compiled_module(), {0, 7}, {1, 0}, {{0, 7}}); |
| 152 // Check from end of one function (no valid instruction position) to beginning | 196 // Check from end of one function (no valid instruction position) to beginning |
| 153 // of next function. Must be empty, but not fail. | 197 // of next function. Must be empty, but not fail. |
| 154 CheckLocations(instance->compiled_module(), {0, 8}, {1, 0}, {}); | 198 CheckLocations(instance->compiled_module(), {0, 8}, {1, 0}, {}); |
| 155 // Check from one after the end of the function. Must fail. | 199 // Check from one after the end of the function. Must fail. |
| 156 CheckLocationsFail(instance->compiled_module(), {0, 9}, {1, 0}); | 200 CheckLocationsFail(instance->compiled_module(), {0, 9}, {1, 0}); |
| 157 } | 201 } |
| 158 | 202 |
| 159 TEST(TestSimpleBreak) { | 203 TEST(WasmSimpleBreak) { |
| 160 WasmRunner<int> runner(kExecuteCompiled); | 204 WasmRunner<int> runner(kExecuteCompiled); |
| 161 Isolate* isolate = runner.main_isolate(); | 205 Isolate* isolate = runner.main_isolate(); |
| 162 | 206 |
| 163 BUILD(runner, WASM_NOP, WASM_I32_ADD(WASM_I32V_1(11), WASM_I32V_1(3))); | 207 BUILD(runner, WASM_NOP, WASM_I32_ADD(WASM_I32V_1(11), WASM_I32V_1(3))); |
| 164 | 208 |
| 165 Handle<JSFunction> main_fun_wrapper = | 209 Handle<JSFunction> main_fun_wrapper = |
| 166 runner.module().WrapCode(runner.function_index()); | 210 runner.module().WrapCode(runner.function_index()); |
| 167 SetBreakpoint(runner, runner.function_index(), 4, 4); | 211 SetBreakpoint(runner, runner.function_index(), 4, 4); |
| 168 | 212 |
| 169 BreakHandler count_breaks(isolate); | 213 BreakHandler count_breaks(isolate, {{4, BreakHandler::Continue}}); |
| 170 CHECK_EQ(0, count_breaks.count()); | |
| 171 | 214 |
| 172 Handle<Object> global(isolate->context()->global_object(), isolate); | 215 Handle<Object> global(isolate->context()->global_object(), isolate); |
| 173 MaybeHandle<Object> retval = | 216 MaybeHandle<Object> retval = |
| 217 Execution::Call(isolate, main_fun_wrapper, global, 0, nullptr); |
| 218 CHECK(!retval.is_null()); |
| 219 int result; |
| 220 CHECK(retval.ToHandleChecked()->ToInt32(&result)); |
| 221 CHECK_EQ(14, result); |
| 222 } |
| 223 |
| 224 TEST(WasmSimpleStepping) { |
| 225 WasmRunner<int> runner(kExecuteCompiled); |
| 226 BUILD(runner, WASM_I32_ADD(WASM_I32V_1(11), WASM_I32V_1(3))); |
| 227 |
| 228 Isolate* isolate = runner.main_isolate(); |
| 229 Handle<JSFunction> main_fun_wrapper = |
| 230 runner.module().WrapCode(runner.function_index()); |
| 231 |
| 232 // Set breakpoint at the first I32Const. |
| 233 SetBreakpoint(runner, runner.function_index(), 1, 1); |
| 234 |
| 235 BreakHandler count_breaks(isolate, |
| 236 { |
| 237 {1, BreakHandler::StepNext}, // I32Const |
| 238 {3, BreakHandler::StepNext}, // I32Const |
| 239 {5, BreakHandler::Continue} // I32Add |
| 240 }); |
| 241 |
| 242 Handle<Object> global(isolate->context()->global_object(), isolate); |
| 243 MaybeHandle<Object> retval = |
| 174 Execution::Call(isolate, main_fun_wrapper, global, 0, nullptr); | 244 Execution::Call(isolate, main_fun_wrapper, global, 0, nullptr); |
| 175 CHECK(!retval.is_null()); | 245 CHECK(!retval.is_null()); |
| 176 int result; | 246 int result; |
| 177 CHECK(retval.ToHandleChecked()->ToInt32(&result)); | 247 CHECK(retval.ToHandleChecked()->ToInt32(&result)); |
| 178 CHECK_EQ(14, result); | 248 CHECK_EQ(14, result); |
| 249 } |
| 179 | 250 |
| 180 CHECK_EQ(1, count_breaks.count()); | 251 TEST(WasmStepInAndOut) { |
| 252 WasmRunner<int, int> runner(kExecuteCompiled); |
| 253 WasmFunctionCompiler& f2 = runner.NewFunction<void>(); |
| 254 f2.AllocateLocal(ValueType::kWord32); |
| 255 |
| 256 // Call f2 via indirect call, because a direct call requires f2 to exist when |
| 257 // we compile main, but we need to compile main first so that the order of |
| 258 // functions in the code section matches the function indexes. |
| 259 |
| 260 // return arg0 |
| 261 BUILD(runner, WASM_RETURN1(WASM_GET_LOCAL(0))); |
| 262 // for (int i = 0; i < 10; ++i) { f2(i); } |
| 263 BUILD(f2, WASM_LOOP( |
| 264 WASM_BR_IF(0, WASM_BINOP(kExprI32GeU, WASM_GET_LOCAL(0), |
| 265 WASM_I32V_1(10))), |
| 266 WASM_SET_LOCAL( |
| 267 0, WASM_BINOP(kExprI32Sub, WASM_GET_LOCAL(0), WASM_ONE)), |
| 268 WASM_CALL_FUNCTION(runner.function_index(), WASM_GET_LOCAL(0)), |
| 269 WASM_BR(1))); |
| 270 |
| 271 Isolate* isolate = runner.main_isolate(); |
| 272 Handle<JSFunction> main_fun_wrapper = |
| 273 runner.module().WrapCode(f2.function_index()); |
| 274 |
| 275 // Set first breakpoint on the GetLocal (offset 19) before the Call. |
| 276 SetBreakpoint(runner, f2.function_index(), 19, 19); |
| 277 |
| 278 BreakHandler count_breaks(isolate, |
| 279 { |
| 280 {19, BreakHandler::StepIn}, // GetLocal |
| 281 {21, BreakHandler::StepIn}, // Call |
| 282 {1, BreakHandler::StepOut}, // in f2 |
| 283 {23, BreakHandler::Continue} // After Call |
| 284 }); |
| 285 |
| 286 Handle<Object> global(isolate->context()->global_object(), isolate); |
| 287 CHECK(!Execution::Call(isolate, main_fun_wrapper, global, 0, nullptr) |
| 288 .is_null()); |
| 181 } | 289 } |
| OLD | NEW |