| 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 { Continue, StepNext, StepIn, StepOut }; |
| 59 struct BreakPoint { |
| 60 int position; |
| 61 Action action; |
| 62 BreakPoint(int position, Action action) |
| 63 : position(position), action(action) {} |
| 64 }; |
| 65 |
| 66 explicit BreakHandler(Isolate* isolate, |
| 67 std::initializer_list<BreakPoint> expected_breaks) |
| 68 : isolate_(isolate), expected_breaks_(expected_breaks) { |
| 57 current_handler = this; | 69 current_handler = this; |
| 58 isolate->debug()->SetMessageHandler(&HandleMessage); | 70 isolate->debug()->SetMessageHandler(&HandleMessage); |
| 59 } | 71 } |
| 60 ~BreakHandler() { | 72 ~BreakHandler() { |
| 73 // Check that all expected breakpoints have been hit. |
| 74 CHECK_EQ(count_, expected_breaks_.size()); |
| 75 // BreakHandlers must be correctly stacked. |
| 61 CHECK_EQ(this, current_handler); | 76 CHECK_EQ(this, current_handler); |
| 62 current_handler = nullptr; | 77 current_handler = nullptr; |
| 63 isolate_->debug()->SetMessageHandler(nullptr); | 78 isolate_->debug()->SetMessageHandler(nullptr); |
| 64 } | 79 } |
| 65 | 80 |
| 66 int count() const { return count_; } | 81 int count() const { return count_; } |
| 67 | 82 |
| 68 private: | 83 private: |
| 69 Isolate* isolate_; | 84 Isolate* isolate_; |
| 70 int count_ = 0; | 85 int count_ = 0; |
| 86 std::vector<BreakPoint> expected_breaks_; |
| 71 | 87 |
| 72 static BreakHandler* current_handler; | 88 static BreakHandler* current_handler; |
| 73 | 89 |
| 90 static const char* ActionToArgs(Action action) { |
| 91 switch (action) { |
| 92 case Continue: |
| 93 return ""; |
| 94 case StepNext: |
| 95 return "\"stepaction\":\"next\""; |
| 96 case StepIn: |
| 97 return "\"stepaction\":\"in\""; |
| 98 case StepOut: |
| 99 return "\"stepaction\":\"out\""; |
| 100 default: |
| 101 UNREACHABLE(); |
| 102 return nullptr; |
| 103 } |
| 104 } |
| 105 |
| 106 Action HandleBreak() { |
| 107 printf("Break #%d\n", count_); |
| 108 CHECK_GT(expected_breaks_.size(), count_); |
| 109 |
| 110 // Check the current position. |
| 111 StackTraceFrameIterator frame_it(isolate_); |
| 112 auto summ = FrameSummary::GetTop(frame_it.frame()).AsWasmInterpreted(); |
| 113 CHECK_EQ(expected_breaks_[count_].position, summ.byte_offset()); |
| 114 |
| 115 return expected_breaks_[count_++].action; |
| 116 } |
| 117 |
| 74 static void HandleMessage(const v8::Debug::Message& message) { | 118 static void HandleMessage(const v8::Debug::Message& message) { |
| 75 // Ignore responses. | 119 // Ignore responses. |
| 76 if (!message.IsEvent()) return; | 120 if (!message.IsEvent()) return; |
| 77 // Ignore everything except break events. | 121 // Ignore everything except break events. |
| 78 if (message.GetEvent() != v8::DebugEvent::Break) return; | 122 if (message.GetEvent() != v8::DebugEvent::Break) return; |
| 79 | 123 |
| 80 printf("break!\n"); | |
| 81 CHECK_NOT_NULL(current_handler); | 124 CHECK_NOT_NULL(current_handler); |
| 82 current_handler->count_ += 1; | 125 Action next_action = current_handler->HandleBreak(); |
| 83 // Don't run into an endless loop. | |
| 84 CHECK_GT(100, current_handler->count_); | |
| 85 | 126 |
| 86 const char command[] = "{\"type\":\"request\", \"command\":\"continue\"}"; | 127 EmbeddedVector<char, 256> command; |
| 87 uint16_t command_u16[arraysize(command) - 1]; | 128 command.Truncate(SNPrintF( |
| 88 for (unsigned i = 0; i < arraysize(command) - 1; ++i) { | 129 command, |
| 130 "{\"type\":\"request\", \"command\":\"continue\", \"arguments\":{%s}}", |
| 131 ActionToArgs(next_action))); |
| 132 |
| 133 EmbeddedVector<uint16_t, 256> command_u16; |
| 134 command_u16.Truncate(command.length()); |
| 135 for (unsigned i = 0, e = command.length(); i < e; ++i) { |
| 89 command_u16[i] = command[i]; | 136 command_u16[i] = command[i]; |
| 90 } | 137 } |
| 91 current_handler->isolate_->debug()->EnqueueCommandMessage( | 138 current_handler->isolate_->debug()->EnqueueCommandMessage( |
| 92 ArrayVector(command_u16), message.GetClientData()); | 139 command_u16, message.GetClientData()); |
| 93 } | 140 } |
| 94 }; | 141 }; |
| 95 | 142 |
| 96 // static | 143 // static |
| 97 BreakHandler* BreakHandler::current_handler = nullptr; | 144 BreakHandler* BreakHandler::current_handler = nullptr; |
| 98 | 145 |
| 99 Handle<JSObject> MakeFakeBreakpoint(Isolate* isolate, int position) { | 146 Handle<JSObject> MakeFakeBreakpoint(Isolate* isolate, int position) { |
| 100 Handle<JSObject> obj = | 147 Handle<JSObject> obj = |
| 101 isolate->factory()->NewJSObject(isolate->object_function()); | 148 isolate->factory()->NewJSObject(isolate->object_function()); |
| 102 // Generate an "isTriggered" method that always returns true. | 149 // Generate an "isTriggered" method that always returns true. |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 168 TEST(TestSimpleBreak) { | 215 TEST(TestSimpleBreak) { |
| 169 WasmRunner<int> runner(kExecuteCompiled); | 216 WasmRunner<int> runner(kExecuteCompiled); |
| 170 Isolate* isolate = runner.main_isolate(); | 217 Isolate* isolate = runner.main_isolate(); |
| 171 | 218 |
| 172 BUILD(runner, WASM_NOP, WASM_I32_ADD(WASM_I32V_1(11), WASM_I32V_1(3))); | 219 BUILD(runner, WASM_NOP, WASM_I32_ADD(WASM_I32V_1(11), WASM_I32V_1(3))); |
| 173 | 220 |
| 174 Handle<JSFunction> main_fun_wrapper = | 221 Handle<JSFunction> main_fun_wrapper = |
| 175 runner.module().WrapCode(runner.function_index()); | 222 runner.module().WrapCode(runner.function_index()); |
| 176 SetBreakpoint(runner, runner.function_index(), 4, 4); | 223 SetBreakpoint(runner, runner.function_index(), 4, 4); |
| 177 | 224 |
| 178 BreakHandler count_breaks(isolate); | 225 BreakHandler count_breaks(isolate, {{4, BreakHandler::Continue}}); |
| 179 CHECK_EQ(0, count_breaks.count()); | |
| 180 | 226 |
| 181 Handle<Object> global(isolate->context()->global_object(), isolate); | 227 Handle<Object> global(isolate->context()->global_object(), isolate); |
| 182 MaybeHandle<Object> retval = | 228 MaybeHandle<Object> retval = |
| 229 Execution::Call(isolate, main_fun_wrapper, global, 0, nullptr); |
| 230 CHECK(!retval.is_null()); |
| 231 int result; |
| 232 CHECK(retval.ToHandleChecked()->ToInt32(&result)); |
| 233 CHECK_EQ(14, result); |
| 234 } |
| 235 |
| 236 TEST(TestSimpleStepping) { |
| 237 WasmRunner<int> runner(kExecuteCompiled); |
| 238 BUILD(runner, WASM_I32_ADD(WASM_I32V_1(11), WASM_I32V_1(3))); |
| 239 |
| 240 Isolate* isolate = runner.main_isolate(); |
| 241 Handle<JSFunction> main_fun_wrapper = |
| 242 runner.module().WrapCode(runner.function_index()); |
| 243 |
| 244 // Set breakpoint at the first I32Const. |
| 245 SetBreakpoint(runner, runner.function_index(), 1, 1); |
| 246 |
| 247 BreakHandler count_breaks(isolate, |
| 248 { |
| 249 {1, BreakHandler::StepNext}, // I32Const |
| 250 {3, BreakHandler::StepNext}, // I32Const |
| 251 {5, BreakHandler::Continue} // I32Add |
| 252 }); |
| 253 |
| 254 Handle<Object> global(isolate->context()->global_object(), isolate); |
| 255 MaybeHandle<Object> retval = |
| 183 Execution::Call(isolate, main_fun_wrapper, global, 0, nullptr); | 256 Execution::Call(isolate, main_fun_wrapper, global, 0, nullptr); |
| 184 CHECK(!retval.is_null()); | 257 CHECK(!retval.is_null()); |
| 185 int result; | 258 int result; |
| 186 CHECK(retval.ToHandleChecked()->ToInt32(&result)); | 259 CHECK(retval.ToHandleChecked()->ToInt32(&result)); |
| 187 CHECK_EQ(14, result); | 260 CHECK_EQ(14, result); |
| 261 } |
| 188 | 262 |
| 189 CHECK_EQ(1, count_breaks.count()); | 263 TEST(TestStepInAndOut) { |
| 264 WasmRunner<int, int> runner(kExecuteCompiled); |
| 265 WasmFunctionCompiler& f2 = runner.NewFunction<void>(); |
| 266 f2.AllocateLocal(ValueType::kWord32); |
| 267 |
| 268 // Call f2 via indirect call, because a direct call requires f2 to exist when |
| 269 // we compile main, but we need to compile main first so that the order of |
| 270 // functions in the code section matches the function indexes. |
| 271 |
| 272 // return arg0 |
| 273 BUILD(runner, WASM_RETURN1(WASM_GET_LOCAL(0))); |
| 274 // for (int i = 0; i < 10; ++i) { f2(i); } |
| 275 BUILD(f2, WASM_LOOP( |
| 276 WASM_BR_IF(0, WASM_BINOP(kExprI32GeU, WASM_GET_LOCAL(0), |
| 277 WASM_I32V_1(10))), |
| 278 WASM_SET_LOCAL( |
| 279 0, WASM_BINOP(kExprI32Sub, WASM_GET_LOCAL(0), WASM_ONE)), |
| 280 WASM_CALL_FUNCTION(runner.function_index(), WASM_GET_LOCAL(0)), |
| 281 WASM_BR(1))); |
| 282 |
| 283 Isolate* isolate = runner.main_isolate(); |
| 284 Handle<JSFunction> main_fun_wrapper = |
| 285 runner.module().WrapCode(f2.function_index()); |
| 286 |
| 287 // Set first breakpoint on the GetLocal (offset 19) before the Call. |
| 288 SetBreakpoint(runner, f2.function_index(), 19, 19); |
| 289 |
| 290 BreakHandler count_breaks(isolate, |
| 291 { |
| 292 {19, BreakHandler::StepIn}, // GetLocal |
| 293 {21, BreakHandler::StepIn}, // Call |
| 294 {1, BreakHandler::StepOut}, // in f2 |
| 295 {23, BreakHandler::Continue} // After Call |
| 296 }); |
| 297 |
| 298 Handle<Object> global(isolate->context()->global_object(), isolate); |
| 299 CHECK(!Execution::Call(isolate, main_fun_wrapper, global, 0, nullptr) |
| 300 .is_null()); |
| 190 } | 301 } |
| OLD | NEW |