LCOV - code coverage report
Current view: top level - test/cctest/wasm - test-wasm-breakpoints.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 134 135 99.3 %
Date: 2019-04-19 Functions: 29 31 93.5 %

          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          48 : 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          48 :   bool success = module_object->GetPossibleBreakpoints(start, end, &locations);
      29          48 :   CHECK(success);
      30             : 
      31          48 :   printf("got %d locations: ", static_cast<int>(locations.size()));
      32         208 :   for (size_t i = 0, e = locations.size(); i != e; ++i) {
      33         160 :     printf("%s<%d,%d>", i == 0 ? "" : ", ", locations[i].GetLineNumber(),
      34             :            locations[i].GetColumnNumber());
      35             :   }
      36             :   printf("\n");
      37             : 
      38             :   std::vector<debug::Location> expected_locations(expected_locations_init);
      39          48 :   CHECK_EQ(expected_locations.size(), locations.size());
      40         208 :   for (size_t i = 0, e = locations.size(); i != e; ++i) {
      41         160 :     CHECK_EQ(expected_locations[i].GetLineNumber(),
      42             :              locations[i].GetLineNumber());
      43         160 :     CHECK_EQ(expected_locations[i].GetColumnNumber(),
      44             :              locations[i].GetColumnNumber());
      45             :   }
      46          48 : }
      47             : 
      48           8 : void CheckLocationsFail(WasmModuleObject module_object, debug::Location start,
      49             :                         debug::Location end) {
      50             :   std::vector<debug::BreakLocation> locations;
      51           8 :   bool success = module_object->GetPossibleBreakpoints(start, end, &locations);
      52           8 :   CHECK(!success);
      53           8 : }
      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          24 :         : position(position), action(action) {}
      68             :   };
      69             : 
      70          24 :   explicit BreakHandler(Isolate* isolate,
      71             :                         std::initializer_list<BreakPoint> expected_breaks)
      72          24 :       : isolate_(isolate), expected_breaks_(expected_breaks) {
      73          24 :     v8::debug::SetDebugDelegate(reinterpret_cast<v8::Isolate*>(isolate_), this);
      74          24 :   }
      75          48 :   ~BreakHandler() override {
      76             :     // Check that all expected breakpoints have been hit.
      77          48 :     CHECK_EQ(count_, expected_breaks_.size());
      78          24 :     v8::debug::SetDebugDelegate(reinterpret_cast<v8::Isolate*>(isolate_),
      79          24 :                                 nullptr);
      80          24 :   }
      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          64 :   void BreakProgramRequested(v8::Local<v8::Context> paused_context,
      90             :                              const std::vector<int>&) override {
      91          64 :     printf("Break #%d\n", count_);
      92         128 :     CHECK_GT(expected_breaks_.size(), count_);
      93             : 
      94             :     // Check the current position.
      95          64 :     StackTraceFrameIterator frame_it(isolate_);
      96          64 :     auto summ = FrameSummary::GetTop(frame_it.frame()).AsWasmInterpreted();
      97         128 :     CHECK_EQ(expected_breaks_[count_].position, summ.byte_offset());
      98             : 
      99          64 :     Action next_action = expected_breaks_[count_].action;
     100          64 :     switch (next_action) {
     101             :       case Continue:
     102             :         break;
     103             :       case StepNext:
     104             :       case StepIn:
     105             :       case StepOut:
     106          40 :         isolate_->debug()->PrepareStep(static_cast<StepAction>(next_action));
     107          40 :         break;
     108             :       default:
     109           0 :         UNREACHABLE();
     110             :     }
     111          64 :     ++count_;
     112          64 :   }
     113             : };
     114             : 
     115             : 
     116          32 : void SetBreakpoint(WasmRunnerBase& runner, int function_index, int byte_offset,
     117             :                    int expected_set_byte_offset = -1) {
     118             :   int func_offset =
     119          32 :       runner.builder().GetFunctionAt(function_index)->code.offset();
     120          32 :   int code_offset = func_offset + byte_offset;
     121          32 :   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             :                                          runner.main_isolate());
     125             :   static int break_index = 0;
     126             :   Handle<BreakPoint> break_point =
     127             :       runner.main_isolate()->factory()->NewBreakPoint(
     128          32 :           break_index++, runner.main_isolate()->factory()->empty_string());
     129          32 :   CHECK(WasmModuleObject::SetBreakPoint(module_object, &code_offset,
     130             :                                         break_point));
     131          32 :   int set_byte_offset = code_offset - func_offset;
     132          32 :   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          32 :       WasmInstanceObject::GetOrCreateDebugInfo(instance);
     137          32 :   WasmDebugInfo::SetBreakpoint(debug_info, function_index, set_byte_offset);
     138          32 : }
     139             : 
     140             : // Wrapper with operator<<.
     141             : struct WasmValWrapper {
     142             :   WasmValue val;
     143             : 
     144             :   bool operator==(const WasmValWrapper& other) const {
     145             :     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         440 :   struct BreakpointValues {
     175             :     std::vector<WasmValue> locals;
     176             :     std::vector<WasmValue> stack;
     177             :   };
     178             : 
     179           8 :   explicit CollectValuesBreakHandler(
     180             :       Isolate* isolate, std::initializer_list<BreakpointValues> expected_values)
     181           8 :       : isolate_(isolate), expected_values_(expected_values) {
     182           8 :     v8::debug::SetDebugDelegate(reinterpret_cast<v8::Isolate*>(isolate_), this);
     183           8 :   }
     184          24 :   ~CollectValuesBreakHandler() override {
     185           8 :     v8::debug::SetDebugDelegate(reinterpret_cast<v8::Isolate*>(isolate_),
     186           8 :                                 nullptr);
     187           8 :   }
     188             : 
     189             :  private:
     190             :   Isolate* isolate_;
     191             :   int count_ = 0;
     192             :   std::vector<BreakpointValues> expected_values_;
     193             : 
     194          88 :   void BreakProgramRequested(v8::Local<v8::Context> paused_context,
     195             :                              const std::vector<int>&) override {
     196          88 :     printf("Break #%d\n", count_);
     197         176 :     CHECK_GT(expected_values_.size(), count_);
     198          88 :     auto& expected = expected_values_[count_];
     199          88 :     ++count_;
     200             : 
     201          88 :     HandleScope handles(isolate_);
     202             : 
     203          88 :     StackTraceFrameIterator frame_it(isolate_);
     204          88 :     auto summ = FrameSummary::GetTop(frame_it.frame()).AsWasmInterpreted();
     205             :     Handle<WasmInstanceObject> instance = summ.wasm_instance();
     206             : 
     207             :     auto frame =
     208          88 :         instance->debug_info()->GetInterpretedFrame(frame_it.frame()->fp(), 0);
     209         176 :     CHECK_EQ(expected.locals.size(), frame->GetLocalCount());
     210         792 :     for (int i = 0; i < frame->GetLocalCount(); ++i) {
     211        1056 :       CHECK_EQ(WasmValWrapper{expected.locals[i]},
     212             :                WasmValWrapper{frame->GetLocalValue(i)});
     213             :     }
     214             : 
     215         176 :     CHECK_EQ(expected.stack.size(), frame->GetStackHeight());
     216         216 :     for (int i = 0; i < frame->GetStackHeight(); ++i) {
     217         192 :       CHECK_EQ(WasmValWrapper{expected.stack[i]},
     218             :                WasmValWrapper{frame->GetStackValue(i)});
     219             :     }
     220             : 
     221          88 :     isolate_->debug()->PrepareStep(StepAction::StepIn);
     222          88 :   }
     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          96 : std::vector<WasmValue> wasmVec(Args... args) {
     238             :   std::array<WasmValue, sizeof...(args)> arr{{MakeWasmVal(args)...}};
     239          96 :   return std::vector<WasmValue>{arr.begin(), arr.end()};
     240             : }
     241             : 
     242             : }  // namespace
     243             : 
     244       26672 : WASM_COMPILED_EXEC_TEST(WasmCollectPossibleBreakpoints) {
     245          16 :   WasmRunner<int> runner(execution_tier);
     246             : 
     247           8 :   BUILD(runner, WASM_NOP, WASM_I32_ADD(WASM_ZERO, WASM_ONE));
     248             : 
     249             :   WasmInstanceObject instance = *runner.builder().instance_object();
     250           8 :   WasmModuleObject module_object = instance->module_object();
     251             : 
     252             :   std::vector<debug::Location> locations;
     253             :   // Check all locations for function 0.
     254          16 :   CheckLocations(module_object, {0, 0}, {1, 0},
     255          24 :                  {{0, 1}, {0, 2}, {0, 4}, {0, 6}, {0, 7}});
     256             :   // Check a range ending at an instruction.
     257           8 :   CheckLocations(module_object, {0, 2}, {0, 4}, {{0, 2}});
     258             :   // Check a range ending one behind an instruction.
     259           8 :   CheckLocations(module_object, {0, 2}, {0, 5}, {{0, 2}, {0, 4}});
     260             :   // Check a range starting at an instruction.
     261           8 :   CheckLocations(module_object, {0, 7}, {0, 8}, {{0, 7}});
     262             :   // Check from an instruction to beginning of next function.
     263           8 :   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           8 :   CheckLocations(module_object, {0, 8}, {1, 0}, {});
     267             :   // Check from one after the end of the function. Must fail.
     268           8 :   CheckLocationsFail(module_object, {0, 9}, {1, 0});
     269           8 : }
     270             : 
     271       26672 : WASM_COMPILED_EXEC_TEST(WasmSimpleBreak) {
     272          16 :   WasmRunner<int> runner(execution_tier);
     273             :   Isolate* isolate = runner.main_isolate();
     274             : 
     275           8 :   BUILD(runner, WASM_NOP, WASM_I32_ADD(WASM_I32V_1(11), WASM_I32V_1(3)));
     276             : 
     277             :   Handle<JSFunction> main_fun_wrapper =
     278           8 :       runner.builder().WrapCode(runner.function_index());
     279           8 :   SetBreakpoint(runner, runner.function_index(), 4, 4);
     280             : 
     281          16 :   BreakHandler count_breaks(isolate, {{4, BreakHandler::Continue}});
     282             : 
     283          16 :   Handle<Object> global(isolate->context()->global_object(), isolate);
     284             :   MaybeHandle<Object> retval =
     285           8 :       Execution::Call(isolate, main_fun_wrapper, global, 0, nullptr);
     286           8 :   CHECK(!retval.is_null());
     287             :   int result;
     288           8 :   CHECK(retval.ToHandleChecked()->ToInt32(&result));
     289           8 :   CHECK_EQ(14, result);
     290           8 : }
     291             : 
     292       26672 : WASM_COMPILED_EXEC_TEST(WasmSimpleStepping) {
     293          16 :   WasmRunner<int> runner(execution_tier);
     294           8 :   BUILD(runner, WASM_I32_ADD(WASM_I32V_1(11), WASM_I32V_1(3)));
     295             : 
     296             :   Isolate* isolate = runner.main_isolate();
     297             :   Handle<JSFunction> main_fun_wrapper =
     298           8 :       runner.builder().WrapCode(runner.function_index());
     299             : 
     300             :   // Set breakpoint at the first I32Const.
     301           8 :   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          16 :                             });
     309             : 
     310          16 :   Handle<Object> global(isolate->context()->global_object(), isolate);
     311             :   MaybeHandle<Object> retval =
     312           8 :       Execution::Call(isolate, main_fun_wrapper, global, 0, nullptr);
     313           8 :   CHECK(!retval.is_null());
     314             :   int result;
     315           8 :   CHECK(retval.ToHandleChecked()->ToInt32(&result));
     316           8 :   CHECK_EQ(14, result);
     317           8 : }
     318             : 
     319       26672 : WASM_COMPILED_EXEC_TEST(WasmStepInAndOut) {
     320          16 :   WasmRunner<int, int> runner(execution_tier);
     321           8 :   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           8 :   BUILD(runner, WASM_RETURN1(WASM_GET_LOCAL(0)));
     330             :   // for (int i = 0; i < 10; ++i) { f2(i); }
     331          16 :   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             :   Isolate* isolate = runner.main_isolate();
     340             :   Handle<JSFunction> main_fun_wrapper =
     341           8 :       runner.builder().WrapCode(f2.function_index());
     342             : 
     343             :   // Set first breakpoint on the GetLocal (offset 19) before the Call.
     344           8 :   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          16 :                             });
     353             : 
     354          16 :   Handle<Object> global(isolate->context()->global_object(), isolate);
     355          16 :   CHECK(!Execution::Call(isolate, main_fun_wrapper, global, 0, nullptr)
     356             :              .is_null());
     357           8 : }
     358             : 
     359       26672 : WASM_COMPILED_EXEC_TEST(WasmGetLocalsAndStack) {
     360          16 :   WasmRunner<void, int> runner(execution_tier);
     361             :   runner.AllocateLocal(kWasmI64);
     362             :   runner.AllocateLocal(kWasmF32);
     363             :   runner.AllocateLocal(kWasmF64);
     364             : 
     365           8 :   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             :   Isolate* isolate = runner.main_isolate();
     375             :   Handle<JSFunction> main_fun_wrapper =
     376           8 :       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           8 :   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          96 :       });
     398             : 
     399          16 :   Handle<Object> global(isolate->context()->global_object(), isolate);
     400             :   Handle<Object> args[]{handle(Smi::FromInt(7), isolate)};
     401          16 :   CHECK(!Execution::Call(isolate, main_fun_wrapper, global, 1, args).is_null());
     402           8 : }
     403             : 
     404             : }  // namespace wasm
     405             : }  // namespace internal
     406       79968 : }  // namespace v8

Generated by: LCOV version 1.10