LCOV - code coverage report
Current view: top level - test/cctest/wasm - test-wasm-breakpoints.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 140 141 99.3 %
Date: 2019-01-20 Functions: 30 32 93.8 %

          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

Generated by: LCOV version 1.10