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 |