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