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 |