Line data Source code
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
3 : // found in the LICENSE file.
4 :
5 : #include "src/assembler-inl.h"
6 : #include "src/debug/debug-interface.h"
7 : #include "src/frames-inl.h"
8 : #include "src/property-descriptor.h"
9 : #include "src/utils.h"
10 : #include "src/wasm/wasm-objects-inl.h"
11 :
12 : #include "test/cctest/cctest.h"
13 : #include "test/cctest/compiler/value-helper.h"
14 : #include "test/cctest/wasm/wasm-run-utils.h"
15 : #include "test/common/wasm/test-signatures.h"
16 : #include "test/common/wasm/wasm-macro-gen.h"
17 :
18 : namespace v8 {
19 : namespace internal {
20 : namespace wasm {
21 :
22 : namespace {
23 :
24 60 : void CheckLocations(
25 : WasmModuleObject module_object, debug::Location start, debug::Location end,
26 : std::initializer_list<debug::Location> expected_locations_init) {
27 : std::vector<debug::BreakLocation> locations;
28 60 : bool success = module_object->GetPossibleBreakpoints(start, end, &locations);
29 60 : CHECK(success);
30 :
31 120 : printf("got %d locations: ", static_cast<int>(locations.size()));
32 220 : for (size_t i = 0, e = locations.size(); i != e; ++i) {
33 100 : printf("%s<%d,%d>", i == 0 ? "" : ", ", locations[i].GetLineNumber(),
34 300 : locations[i].GetColumnNumber());
35 : }
36 : printf("\n");
37 :
38 : std::vector<debug::Location> expected_locations(expected_locations_init);
39 180 : CHECK_EQ(expected_locations.size(), locations.size());
40 160 : for (size_t i = 0, e = locations.size(); i != e; ++i) {
41 300 : CHECK_EQ(expected_locations[i].GetLineNumber(),
42 : locations[i].GetLineNumber());
43 300 : CHECK_EQ(expected_locations[i].GetColumnNumber(),
44 : locations[i].GetColumnNumber());
45 : }
46 60 : }
47 :
48 10 : void CheckLocationsFail(WasmModuleObject module_object, debug::Location start,
49 : debug::Location end) {
50 : std::vector<debug::BreakLocation> locations;
51 10 : bool success = module_object->GetPossibleBreakpoints(start, end, &locations);
52 10 : CHECK(!success);
53 10 : }
54 :
55 : class BreakHandler : public debug::DebugDelegate {
56 : public:
57 : enum Action {
58 : Continue = StepAction::LastStepAction + 1,
59 : StepNext = StepAction::StepNext,
60 : StepIn = StepAction::StepIn,
61 : StepOut = StepAction::StepOut
62 : };
63 : struct BreakPoint {
64 : int position;
65 : Action action;
66 : BreakPoint(int position, Action action)
67 30 : : position(position), action(action) {}
68 : };
69 :
70 30 : explicit BreakHandler(Isolate* isolate,
71 : std::initializer_list<BreakPoint> expected_breaks)
72 30 : : isolate_(isolate), expected_breaks_(expected_breaks) {
73 30 : v8::debug::SetDebugDelegate(reinterpret_cast<v8::Isolate*>(isolate_), this);
74 30 : }
75 30 : ~BreakHandler() override {
76 : // Check that all expected breakpoints have been hit.
77 90 : CHECK_EQ(count_, expected_breaks_.size());
78 : v8::debug::SetDebugDelegate(reinterpret_cast<v8::Isolate*>(isolate_),
79 30 : nullptr);
80 30 : }
81 :
82 : int count() const { return count_; }
83 :
84 : private:
85 : Isolate* isolate_;
86 : int count_ = 0;
87 : std::vector<BreakPoint> expected_breaks_;
88 :
89 80 : void BreakProgramRequested(v8::Local<v8::Context> paused_context,
90 : const std::vector<int>&) override {
91 80 : printf("Break #%d\n", count_);
92 240 : CHECK_GT(expected_breaks_.size(), count_);
93 :
94 : // Check the current position.
95 130 : StackTraceFrameIterator frame_it(isolate_);
96 80 : auto summ = FrameSummary::GetTop(frame_it.frame()).AsWasmInterpreted();
97 160 : CHECK_EQ(expected_breaks_[count_].position, summ.byte_offset());
98 :
99 80 : Action next_action = expected_breaks_[count_].action;
100 80 : switch (next_action) {
101 : case Continue:
102 : break;
103 : case StepNext:
104 : case StepIn:
105 : case StepOut:
106 100 : isolate_->debug()->PrepareStep(static_cast<StepAction>(next_action));
107 50 : break;
108 : default:
109 0 : UNREACHABLE();
110 : }
111 80 : ++count_;
112 80 : }
113 : };
114 :
115 :
116 40 : void SetBreakpoint(WasmRunnerBase& runner, int function_index, int byte_offset,
117 : int expected_set_byte_offset = -1) {
118 : int func_offset =
119 40 : runner.builder().GetFunctionAt(function_index)->code.offset();
120 40 : int code_offset = func_offset + byte_offset;
121 40 : if (expected_set_byte_offset == -1) expected_set_byte_offset = byte_offset;
122 : Handle<WasmInstanceObject> instance = runner.builder().instance_object();
123 : Handle<WasmModuleObject> module_object(instance->module_object(),
124 120 : runner.main_isolate());
125 : static int break_index = 0;
126 : Handle<BreakPoint> break_point =
127 : runner.main_isolate()->factory()->NewBreakPoint(
128 40 : break_index++, runner.main_isolate()->factory()->empty_string());
129 40 : CHECK(WasmModuleObject::SetBreakPoint(module_object, &code_offset,
130 : break_point));
131 40 : int set_byte_offset = code_offset - func_offset;
132 40 : CHECK_EQ(expected_set_byte_offset, set_byte_offset);
133 : // Also set breakpoint on the debug info of the instance directly, since the
134 : // instance chain is not setup properly in tests.
135 : Handle<WasmDebugInfo> debug_info =
136 40 : WasmInstanceObject::GetOrCreateDebugInfo(instance);
137 40 : WasmDebugInfo::SetBreakpoint(debug_info, function_index, set_byte_offset);
138 40 : }
139 :
140 : // Wrapper with operator<<.
141 : struct WasmValWrapper {
142 : WasmValue val;
143 :
144 520 : bool operator==(const WasmValWrapper& other) const {
145 520 : return val == other.val;
146 : }
147 : };
148 :
149 : // Only needed in debug builds. Avoid unused warning otherwise.
150 : #ifdef DEBUG
151 : std::ostream& operator<<(std::ostream& out, const WasmValWrapper& wrapper) {
152 : switch (wrapper.val.type()) {
153 : case kWasmI32:
154 : out << "i32: " << wrapper.val.to<int32_t>();
155 : break;
156 : case kWasmI64:
157 : out << "i64: " << wrapper.val.to<int64_t>();
158 : break;
159 : case kWasmF32:
160 : out << "f32: " << wrapper.val.to<float>();
161 : break;
162 : case kWasmF64:
163 : out << "f64: " << wrapper.val.to<double>();
164 : break;
165 : default:
166 : UNIMPLEMENTED();
167 : }
168 : return out;
169 : }
170 : #endif
171 :
172 : class CollectValuesBreakHandler : public debug::DebugDelegate {
173 : public:
174 550 : struct BreakpointValues {
175 : std::vector<WasmValue> locals;
176 : std::vector<WasmValue> stack;
177 : };
178 :
179 10 : explicit CollectValuesBreakHandler(
180 : Isolate* isolate, std::initializer_list<BreakpointValues> expected_values)
181 10 : : isolate_(isolate), expected_values_(expected_values) {
182 10 : v8::debug::SetDebugDelegate(reinterpret_cast<v8::Isolate*>(isolate_), this);
183 10 : }
184 20 : ~CollectValuesBreakHandler() override {
185 : v8::debug::SetDebugDelegate(reinterpret_cast<v8::Isolate*>(isolate_),
186 10 : nullptr);
187 10 : }
188 :
189 : private:
190 : Isolate* isolate_;
191 : int count_ = 0;
192 : std::vector<BreakpointValues> expected_values_;
193 :
194 110 : void BreakProgramRequested(v8::Local<v8::Context> paused_context,
195 : const std::vector<int>&) override {
196 110 : printf("Break #%d\n", count_);
197 220 : CHECK_GT(expected_values_.size(), count_);
198 110 : auto& expected = expected_values_[count_];
199 110 : ++count_;
200 :
201 220 : HandleScope handles(isolate_);
202 :
203 110 : StackTraceFrameIterator frame_it(isolate_);
204 110 : auto summ = FrameSummary::GetTop(frame_it.frame()).AsWasmInterpreted();
205 : Handle<WasmInstanceObject> instance = summ.wasm_instance();
206 :
207 : auto frame =
208 220 : instance->debug_info()->GetInterpretedFrame(frame_it.frame()->fp(), 0);
209 660 : CHECK_EQ(expected.locals.size(), frame->GetLocalCount());
210 990 : for (int i = 0; i < frame->GetLocalCount(); ++i) {
211 1320 : CHECK_EQ(WasmValWrapper{expected.locals[i]},
212 : WasmValWrapper{frame->GetLocalValue(i)});
213 : }
214 :
215 300 : CHECK_EQ(expected.stack.size(), frame->GetStackHeight());
216 270 : for (int i = 0; i < frame->GetStackHeight(); ++i) {
217 240 : CHECK_EQ(WasmValWrapper{expected.stack[i]},
218 : WasmValWrapper{frame->GetStackValue(i)});
219 : }
220 :
221 220 : isolate_->debug()->PrepareStep(StepAction::StepIn);
222 110 : }
223 : };
224 :
225 : // Special template to explicitly cast to WasmValue.
226 : template <typename Arg>
227 : WasmValue MakeWasmVal(Arg arg) {
228 : return WasmValue(arg);
229 : }
230 : // Translate long to i64 (ambiguous otherwise).
231 : template <>
232 : WasmValue MakeWasmVal(long arg) { // NOLINT: allow long parameter
233 : return WasmValue(static_cast<int64_t>(arg));
234 : }
235 :
236 : template <typename... Args>
237 120 : std::vector<WasmValue> wasmVec(Args... args) {
238 : std::array<WasmValue, sizeof...(args)> arr{{MakeWasmVal(args)...}};
239 120 : return std::vector<WasmValue>{arr.begin(), arr.end()};
240 : }
241 :
242 : } // namespace
243 :
244 28357 : WASM_COMPILED_EXEC_TEST(WasmCollectPossibleBreakpoints) {
245 10 : WasmRunner<int> runner(execution_tier);
246 :
247 10 : BUILD(runner, WASM_NOP, WASM_I32_ADD(WASM_ZERO, WASM_ONE));
248 :
249 10 : WasmInstanceObject instance = *runner.builder().instance_object();
250 10 : WasmModuleObject module_object = instance->module_object();
251 :
252 : std::vector<debug::Location> locations;
253 : // Check all locations for function 0.
254 : CheckLocations(module_object, {0, 0}, {1, 0},
255 10 : {{0, 1}, {0, 2}, {0, 4}, {0, 6}, {0, 7}});
256 : // Check a range ending at an instruction.
257 10 : CheckLocations(module_object, {0, 2}, {0, 4}, {{0, 2}});
258 : // Check a range ending one behind an instruction.
259 10 : CheckLocations(module_object, {0, 2}, {0, 5}, {{0, 2}, {0, 4}});
260 : // Check a range starting at an instruction.
261 10 : CheckLocations(module_object, {0, 7}, {0, 8}, {{0, 7}});
262 : // Check from an instruction to beginning of next function.
263 10 : CheckLocations(module_object, {0, 7}, {1, 0}, {{0, 7}});
264 : // Check from end of one function (no valid instruction position) to beginning
265 : // of next function. Must be empty, but not fail.
266 10 : CheckLocations(module_object, {0, 8}, {1, 0}, {});
267 : // Check from one after the end of the function. Must fail.
268 10 : CheckLocationsFail(module_object, {0, 9}, {1, 0});
269 10 : }
270 :
271 28357 : WASM_COMPILED_EXEC_TEST(WasmSimpleBreak) {
272 10 : WasmRunner<int> runner(execution_tier);
273 10 : Isolate* isolate = runner.main_isolate();
274 :
275 10 : BUILD(runner, WASM_NOP, WASM_I32_ADD(WASM_I32V_1(11), WASM_I32V_1(3)));
276 :
277 : Handle<JSFunction> main_fun_wrapper =
278 10 : runner.builder().WrapCode(runner.function_index());
279 10 : SetBreakpoint(runner, runner.function_index(), 4, 4);
280 :
281 20 : BreakHandler count_breaks(isolate, {{4, BreakHandler::Continue}});
282 :
283 20 : Handle<Object> global(isolate->context()->global_object(), isolate);
284 : MaybeHandle<Object> retval =
285 10 : Execution::Call(isolate, main_fun_wrapper, global, 0, nullptr);
286 10 : CHECK(!retval.is_null());
287 : int result;
288 10 : CHECK(retval.ToHandleChecked()->ToInt32(&result));
289 10 : CHECK_EQ(14, result);
290 10 : }
291 :
292 28357 : WASM_COMPILED_EXEC_TEST(WasmSimpleStepping) {
293 10 : WasmRunner<int> runner(execution_tier);
294 10 : BUILD(runner, WASM_I32_ADD(WASM_I32V_1(11), WASM_I32V_1(3)));
295 :
296 10 : Isolate* isolate = runner.main_isolate();
297 : Handle<JSFunction> main_fun_wrapper =
298 10 : runner.builder().WrapCode(runner.function_index());
299 :
300 : // Set breakpoint at the first I32Const.
301 10 : SetBreakpoint(runner, runner.function_index(), 1, 1);
302 :
303 : BreakHandler count_breaks(isolate,
304 : {
305 : {1, BreakHandler::StepNext}, // I32Const
306 : {3, BreakHandler::StepNext}, // I32Const
307 : {5, BreakHandler::Continue} // I32Add
308 20 : });
309 :
310 20 : Handle<Object> global(isolate->context()->global_object(), isolate);
311 : MaybeHandle<Object> retval =
312 10 : Execution::Call(isolate, main_fun_wrapper, global, 0, nullptr);
313 10 : CHECK(!retval.is_null());
314 : int result;
315 10 : CHECK(retval.ToHandleChecked()->ToInt32(&result));
316 10 : CHECK_EQ(14, result);
317 10 : }
318 :
319 28357 : WASM_COMPILED_EXEC_TEST(WasmStepInAndOut) {
320 10 : WasmRunner<int, int> runner(execution_tier);
321 30 : WasmFunctionCompiler& f2 = runner.NewFunction<void>();
322 : f2.AllocateLocal(kWasmI32);
323 :
324 : // Call f2 via indirect call, because a direct call requires f2 to exist when
325 : // we compile main, but we need to compile main first so that the order of
326 : // functions in the code section matches the function indexes.
327 :
328 : // return arg0
329 10 : BUILD(runner, WASM_RETURN1(WASM_GET_LOCAL(0)));
330 : // for (int i = 0; i < 10; ++i) { f2(i); }
331 20 : BUILD(f2, WASM_LOOP(
332 : WASM_BR_IF(0, WASM_BINOP(kExprI32GeU, WASM_GET_LOCAL(0),
333 : WASM_I32V_1(10))),
334 : WASM_SET_LOCAL(
335 : 0, WASM_BINOP(kExprI32Sub, WASM_GET_LOCAL(0), WASM_ONE)),
336 : WASM_CALL_FUNCTION(runner.function_index(), WASM_GET_LOCAL(0)),
337 : WASM_DROP, WASM_BR(1)));
338 :
339 10 : Isolate* isolate = runner.main_isolate();
340 : Handle<JSFunction> main_fun_wrapper =
341 10 : runner.builder().WrapCode(f2.function_index());
342 :
343 : // Set first breakpoint on the GetLocal (offset 19) before the Call.
344 10 : SetBreakpoint(runner, f2.function_index(), 19, 19);
345 :
346 : BreakHandler count_breaks(isolate,
347 : {
348 : {19, BreakHandler::StepIn}, // GetLocal
349 : {21, BreakHandler::StepIn}, // Call
350 : {1, BreakHandler::StepOut}, // in f2
351 : {23, BreakHandler::Continue} // After Call
352 20 : });
353 :
354 20 : Handle<Object> global(isolate->context()->global_object(), isolate);
355 20 : CHECK(!Execution::Call(isolate, main_fun_wrapper, global, 0, nullptr)
356 : .is_null());
357 10 : }
358 :
359 28357 : WASM_COMPILED_EXEC_TEST(WasmGetLocalsAndStack) {
360 10 : WasmRunner<void, int> runner(execution_tier);
361 : runner.AllocateLocal(kWasmI64);
362 : runner.AllocateLocal(kWasmF32);
363 : runner.AllocateLocal(kWasmF64);
364 :
365 10 : BUILD(runner,
366 : // set [1] to 17
367 : WASM_SET_LOCAL(1, WASM_I64V_1(17)),
368 : // set [2] to <arg0> = 7
369 : WASM_SET_LOCAL(2, WASM_F32_SCONVERT_I32(WASM_GET_LOCAL(0))),
370 : // set [3] to <arg1>/2 = 8.5
371 : WASM_SET_LOCAL(3, WASM_F64_DIV(WASM_F64_SCONVERT_I64(WASM_GET_LOCAL(1)),
372 : WASM_F64(2))));
373 :
374 10 : Isolate* isolate = runner.main_isolate();
375 : Handle<JSFunction> main_fun_wrapper =
376 10 : runner.builder().WrapCode(runner.function_index());
377 :
378 : // Set breakpoint at the first instruction (7 bytes for local decls: num
379 : // entries + 3x<count, type>).
380 10 : SetBreakpoint(runner, runner.function_index(), 7, 7);
381 :
382 : CollectValuesBreakHandler break_handler(
383 : isolate,
384 : {
385 : // params + locals stack
386 : {wasmVec(7, 0L, 0.f, 0.), wasmVec()}, // 0: i64.const[17]
387 : {wasmVec(7, 0L, 0.f, 0.), wasmVec(17L)}, // 1: set_local[1]
388 : {wasmVec(7, 17L, 0.f, 0.), wasmVec()}, // 2: get_local[0]
389 : {wasmVec(7, 17L, 0.f, 0.), wasmVec(7)}, // 3: f32.convert_s
390 : {wasmVec(7, 17L, 0.f, 0.), wasmVec(7.f)}, // 4: set_local[2]
391 : {wasmVec(7, 17L, 7.f, 0.), wasmVec()}, // 5: get_local[1]
392 : {wasmVec(7, 17L, 7.f, 0.), wasmVec(17L)}, // 6: f64.convert_s
393 : {wasmVec(7, 17L, 7.f, 0.), wasmVec(17.)}, // 7: f64.const[2]
394 : {wasmVec(7, 17L, 7.f, 0.), wasmVec(17., 2.)}, // 8: f64.div
395 : {wasmVec(7, 17L, 7.f, 0.), wasmVec(8.5)}, // 9: set_local[3]
396 : {wasmVec(7, 17L, 7.f, 8.5), wasmVec()}, // 10: end
397 120 : });
398 :
399 20 : Handle<Object> global(isolate->context()->global_object(), isolate);
400 : Handle<Object> args[]{handle(Smi::FromInt(7), isolate)};
401 20 : CHECK(!Execution::Call(isolate, main_fun_wrapper, global, 1, args).is_null());
402 10 : }
403 :
404 : } // namespace wasm
405 : } // namespace internal
406 85011 : } // namespace v8
|