LCOV - code coverage report
Current view: top level - test/unittests/wasm - wasm-code-manager-unittest.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 198 201 98.5 %
Date: 2019-02-19 Functions: 55 78 70.5 %

          Line data    Source code
       1             : // Copyright 2017 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 "test/unittests/test-utils.h"
       6             : #include "testing/gmock/include/gmock/gmock.h"
       7             : 
       8             : #include "src/wasm/function-compiler.h"
       9             : #include "src/wasm/jump-table-assembler.h"
      10             : #include "src/wasm/wasm-code-manager.h"
      11             : #include "src/wasm/wasm-engine.h"
      12             : #include "src/wasm/wasm-memory.h"
      13             : 
      14             : namespace v8 {
      15             : namespace internal {
      16             : namespace wasm {
      17             : namespace wasm_heap_unittest {
      18             : 
      19          24 : class DisjointAllocationPoolTest : public ::testing::Test {
      20             :  public:
      21             :   void CheckPool(const DisjointAllocationPool& mem,
      22             :                  std::initializer_list<base::AddressRegion> expected_regions);
      23             :   void CheckRange(base::AddressRegion region1, base::AddressRegion region2);
      24             :   DisjointAllocationPool Make(
      25             :       std::initializer_list<base::AddressRegion> regions);
      26             : };
      27             : 
      28          14 : void DisjointAllocationPoolTest::CheckPool(
      29             :     const DisjointAllocationPool& mem,
      30             :     std::initializer_list<base::AddressRegion> expected_regions) {
      31             :   const auto& regions = mem.regions();
      32          28 :   CHECK_EQ(regions.size(), expected_regions.size());
      33          14 :   auto iter = expected_regions.begin();
      34          32 :   for (auto it = regions.begin(), e = regions.end(); it != e; ++it, ++iter) {
      35          36 :     CHECK_EQ(*it, *iter);
      36             :   }
      37          14 : }
      38             : 
      39           3 : void DisjointAllocationPoolTest::CheckRange(base::AddressRegion region1,
      40             :                                             base::AddressRegion region2) {
      41           3 :   CHECK_EQ(region1, region2);
      42           3 : }
      43             : 
      44           0 : DisjointAllocationPool DisjointAllocationPoolTest::Make(
      45             :     std::initializer_list<base::AddressRegion> regions) {
      46             :   DisjointAllocationPool ret;
      47          20 :   for (auto& region : regions) {
      48          20 :     ret.Merge(region);
      49             :   }
      50           0 :   return ret;
      51             : }
      52             : 
      53       15189 : TEST_F(DisjointAllocationPoolTest, ConstructEmpty) {
      54             :   DisjointAllocationPool a;
      55           1 :   CHECK(a.IsEmpty());
      56           1 :   CheckPool(a, {});
      57           1 :   a.Merge({1, 4});
      58           1 :   CheckPool(a, {{1, 4}});
      59           1 : }
      60             : 
      61       15189 : TEST_F(DisjointAllocationPoolTest, ConstructWithRange) {
      62           1 :   DisjointAllocationPool a({1, 4});
      63           1 :   CHECK(!a.IsEmpty());
      64           1 :   CheckPool(a, {{1, 4}});
      65           1 : }
      66             : 
      67       15189 : TEST_F(DisjointAllocationPoolTest, SimpleExtract) {
      68           1 :   DisjointAllocationPool a = Make({{1, 4}});
      69           1 :   base::AddressRegion b = a.Allocate(2);
      70           1 :   CheckPool(a, {{3, 2}});
      71           1 :   CheckRange(b, {1, 2});
      72           1 :   a.Merge(b);
      73           1 :   CheckPool(a, {{1, 4}});
      74           1 :   CHECK_EQ(a.regions().size(), 1);
      75           2 :   CHECK_EQ(a.regions().front().begin(), 1);
      76           1 :   CHECK_EQ(a.regions().front().end(), 5);
      77           1 : }
      78             : 
      79       15189 : TEST_F(DisjointAllocationPoolTest, ExtractAll) {
      80           1 :   DisjointAllocationPool a({1, 4});
      81           1 :   base::AddressRegion b = a.Allocate(4);
      82           1 :   CheckRange(b, {1, 4});
      83           1 :   CHECK(a.IsEmpty());
      84           1 :   a.Merge(b);
      85           1 :   CheckPool(a, {{1, 4}});
      86           1 : }
      87             : 
      88       15189 : TEST_F(DisjointAllocationPoolTest, FailToExtract) {
      89           1 :   DisjointAllocationPool a = Make({{1, 4}});
      90           1 :   base::AddressRegion b = a.Allocate(5);
      91           1 :   CheckPool(a, {{1, 4}});
      92           1 :   CHECK(b.is_empty());
      93           1 : }
      94             : 
      95       15189 : TEST_F(DisjointAllocationPoolTest, FailToExtractExact) {
      96           1 :   DisjointAllocationPool a = Make({{1, 4}, {10, 4}});
      97           1 :   base::AddressRegion b = a.Allocate(5);
      98           1 :   CheckPool(a, {{1, 4}, {10, 4}});
      99           1 :   CHECK(b.is_empty());
     100           1 : }
     101             : 
     102       15189 : TEST_F(DisjointAllocationPoolTest, ExtractExact) {
     103           1 :   DisjointAllocationPool a = Make({{1, 4}, {10, 5}});
     104           1 :   base::AddressRegion b = a.Allocate(5);
     105           1 :   CheckPool(a, {{1, 4}});
     106           1 :   CheckRange(b, {10, 5});
     107           1 : }
     108             : 
     109       15189 : TEST_F(DisjointAllocationPoolTest, Merging) {
     110           1 :   DisjointAllocationPool a = Make({{10, 5}, {20, 5}});
     111           1 :   a.Merge({15, 5});
     112           1 :   CheckPool(a, {{10, 15}});
     113           1 : }
     114             : 
     115       15189 : TEST_F(DisjointAllocationPoolTest, MergingMore) {
     116           1 :   DisjointAllocationPool a = Make({{10, 5}, {20, 5}, {30, 5}});
     117           1 :   a.Merge({15, 5});
     118           1 :   a.Merge({25, 5});
     119           1 :   CheckPool(a, {{10, 25}});
     120           1 : }
     121             : 
     122       15189 : TEST_F(DisjointAllocationPoolTest, MergingSkip) {
     123           1 :   DisjointAllocationPool a = Make({{10, 5}, {20, 5}, {30, 5}});
     124           1 :   a.Merge({25, 5});
     125           1 :   CheckPool(a, {{10, 5}, {20, 15}});
     126           1 : }
     127             : 
     128       15189 : TEST_F(DisjointAllocationPoolTest, MergingSkipLargerSrc) {
     129           1 :   DisjointAllocationPool a = Make({{10, 5}, {20, 5}, {30, 5}});
     130           1 :   a.Merge({25, 5});
     131           1 :   a.Merge({35, 5});
     132           1 :   CheckPool(a, {{10, 5}, {20, 20}});
     133           1 : }
     134             : 
     135       15189 : TEST_F(DisjointAllocationPoolTest, MergingSkipLargerSrcWithGap) {
     136           1 :   DisjointAllocationPool a = Make({{10, 5}, {20, 5}, {30, 5}});
     137           1 :   a.Merge({25, 5});
     138           1 :   a.Merge({36, 4});
     139           1 :   CheckPool(a, {{10, 5}, {20, 15}, {36, 4}});
     140           1 : }
     141             : 
     142             : enum ModuleStyle : int { Fixed = 0, Growable = 1 };
     143             : 
     144       42518 : std::string PrintWasmCodeManageTestParam(
     145             :     ::testing::TestParamInfo<ModuleStyle> info) {
     146       42518 :   switch (info.param) {
     147             :     case Fixed:
     148       21259 :       return "Fixed";
     149             :     case Growable:
     150       21259 :       return "Growable";
     151             :   }
     152           0 :   UNREACHABLE();
     153             : }
     154             : 
     155          56 : class WasmCodeManagerTest : public TestWithContext,
     156             :                             public ::testing::WithParamInterface<ModuleStyle> {
     157             :  public:
     158             :   static constexpr uint32_t kNumFunctions = 10;
     159             :   static constexpr uint32_t kJumpTableSize = RoundUp<kCodeAlignment>(
     160             :       JumpTableAssembler::SizeForNumberOfSlots(kNumFunctions));
     161             : 
     162             :   using NativeModulePtr = std::unique_ptr<NativeModule>;
     163             : 
     164          16 :   NativeModulePtr AllocModule(size_t size, ModuleStyle style) {
     165          32 :     std::shared_ptr<WasmModule> module(new WasmModule);
     166          16 :     module->num_declared_functions = kNumFunctions;
     167          16 :     bool can_request_more = style == Growable;
     168             :     return engine()->NewNativeModule(i_isolate(), kAllWasmFeatures, size,
     169          64 :                                      can_request_more, std::move(module));
     170             :   }
     171             : 
     172          29 :   WasmCode* AddCode(NativeModule* native_module, uint32_t index, size_t size) {
     173          29 :     CodeDesc desc;
     174             :     memset(reinterpret_cast<void*>(&desc), 0, sizeof(CodeDesc));
     175          29 :     std::unique_ptr<byte[]> exec_buff(new byte[size]);
     176          29 :     desc.buffer = exec_buff.get();
     177          29 :     desc.instr_size = static_cast<int>(size);
     178             :     return native_module->AddCode(index, desc, 0, 0, {}, OwnedVector<byte>(),
     179         116 :                                   WasmCode::kFunction, WasmCode::kOther);
     180             :   }
     181             : 
     182          48 :   size_t page() const { return AllocatePageSize(); }
     183             : 
     184             :   WasmEngine* engine() { return i_isolate()->wasm_engine(); }
     185             : 
     186             :   WasmCodeManager* manager() { return engine()->code_manager(); }
     187             : 
     188             :   void SetMaxCommittedMemory(size_t limit) {
     189          14 :     manager()->SetMaxCommittedMemoryForTesting(limit);
     190             :   }
     191             : };
     192             : 
     193       78962 : INSTANTIATE_TEST_SUITE_P(Parameterized, WasmCodeManagerTest,
     194             :                          ::testing::Values(Fixed, Growable),
     195             :                          PrintWasmCodeManageTestParam);
     196             : 
     197       18228 : TEST_P(WasmCodeManagerTest, EmptyCase) {
     198             :   SetMaxCommittedMemory(0 * page());
     199           4 :   CHECK_EQ(0, manager()->remaining_uncommitted_code_space());
     200             : 
     201           4 :   ASSERT_DEATH_IF_SUPPORTED(AllocModule(1 * page(), GetParam()),
     202             :                             "OOM in NativeModule::AllocateForCode commit");
     203             : }
     204             : 
     205       18228 : TEST_P(WasmCodeManagerTest, AllocateAndGoOverLimit) {
     206             :   SetMaxCommittedMemory(1 * page());
     207           4 :   CHECK_EQ(1 * page(), manager()->remaining_uncommitted_code_space());
     208           4 :   NativeModulePtr native_module = AllocModule(1 * page(), GetParam());
     209           2 :   CHECK(native_module);
     210           4 :   CHECK_EQ(0, manager()->remaining_uncommitted_code_space());
     211             :   uint32_t index = 0;
     212           2 :   WasmCode* code = AddCode(native_module.get(), index++, 1 * kCodeAlignment);
     213           2 :   CHECK_NOT_NULL(code);
     214           4 :   CHECK_EQ(0, manager()->remaining_uncommitted_code_space());
     215             : 
     216           2 :   code = AddCode(native_module.get(), index++, 3 * kCodeAlignment);
     217           2 :   CHECK_NOT_NULL(code);
     218           4 :   CHECK_EQ(0, manager()->remaining_uncommitted_code_space());
     219             : 
     220             :   code = AddCode(native_module.get(), index++,
     221           4 :                  page() - 4 * kCodeAlignment - kJumpTableSize);
     222           2 :   CHECK_NOT_NULL(code);
     223           4 :   CHECK_EQ(0, manager()->remaining_uncommitted_code_space());
     224             : 
     225             :   // This fails in "reservation" if we cannot extend the code space, or in
     226             :   // "commit" it we can (since we hit the allocation limit in the
     227             :   // WasmCodeManager). Hence don't check for that part of the OOM message.
     228           6 :   ASSERT_DEATH_IF_SUPPORTED(
     229             :       AddCode(native_module.get(), index++, 1 * kCodeAlignment),
     230             :       "OOM in NativeModule::AllocateForCode");
     231             : }
     232             : 
     233       18228 : TEST_P(WasmCodeManagerTest, TotalLimitIrrespectiveOfModuleCount) {
     234           2 :   SetMaxCommittedMemory(3 * page());
     235           4 :   NativeModulePtr nm1 = AllocModule(2 * page(), GetParam());
     236           4 :   NativeModulePtr nm2 = AllocModule(2 * page(), GetParam());
     237           2 :   CHECK(nm1);
     238           2 :   CHECK(nm2);
     239           4 :   WasmCode* code = AddCode(nm1.get(), 0, 2 * page() - kJumpTableSize);
     240           2 :   CHECK_NOT_NULL(code);
     241           6 :   ASSERT_DEATH_IF_SUPPORTED(AddCode(nm2.get(), 0, 2 * page() - kJumpTableSize),
     242             :                             "OOM in NativeModule::AllocateForCode commit");
     243             : }
     244             : 
     245       18228 : TEST_P(WasmCodeManagerTest, GrowingVsFixedModule) {
     246           2 :   SetMaxCommittedMemory(3 * page());
     247           4 :   NativeModulePtr nm = AllocModule(1 * page(), GetParam());
     248           2 :   size_t module_size = GetParam() == Fixed ? kMaxWasmCodeMemory : 1 * page();
     249             :   size_t remaining_space_in_module = module_size - kJumpTableSize;
     250           2 :   if (GetParam() == Fixed) {
     251             :     // Requesting more than the remaining space fails because the module cannot
     252             :     // grow.
     253           4 :     ASSERT_DEATH_IF_SUPPORTED(
     254             :         AddCode(nm.get(), 0, remaining_space_in_module + kCodeAlignment),
     255             :         "OOM in NativeModule::AllocateForCode");
     256             :   } else {
     257             :     // The module grows by one page. One page remains uncommitted.
     258           2 :     CHECK_NOT_NULL(
     259             :         AddCode(nm.get(), 0, remaining_space_in_module + kCodeAlignment));
     260           1 :     CHECK_EQ(manager()->remaining_uncommitted_code_space(), 1 * page());
     261             :   }
     262             : }
     263             : 
     264       18228 : TEST_P(WasmCodeManagerTest, CommitIncrements) {
     265           2 :   SetMaxCommittedMemory(10 * page());
     266           4 :   NativeModulePtr nm = AllocModule(3 * page(), GetParam());
     267           2 :   WasmCode* code = AddCode(nm.get(), 0, kCodeAlignment);
     268           2 :   CHECK_NOT_NULL(code);
     269           4 :   CHECK_EQ(manager()->remaining_uncommitted_code_space(), 9 * page());
     270           4 :   code = AddCode(nm.get(), 1, 2 * page());
     271           2 :   CHECK_NOT_NULL(code);
     272           4 :   CHECK_EQ(manager()->remaining_uncommitted_code_space(), 7 * page());
     273           4 :   code = AddCode(nm.get(), 2, page() - kCodeAlignment - kJumpTableSize);
     274           2 :   CHECK_NOT_NULL(code);
     275           4 :   CHECK_EQ(manager()->remaining_uncommitted_code_space(), 7 * page());
     276           2 : }
     277             : 
     278       18228 : TEST_P(WasmCodeManagerTest, Lookup) {
     279           2 :   SetMaxCommittedMemory(2 * page());
     280             : 
     281           4 :   NativeModulePtr nm1 = AllocModule(1 * page(), GetParam());
     282           4 :   NativeModulePtr nm2 = AllocModule(1 * page(), GetParam());
     283           4 :   WasmCode* code1_0 = AddCode(nm1.get(), 0, kCodeAlignment);
     284           2 :   CHECK_EQ(nm1.get(), code1_0->native_module());
     285           4 :   WasmCode* code1_1 = AddCode(nm1.get(), 1, kCodeAlignment);
     286           4 :   WasmCode* code2_0 = AddCode(nm2.get(), 0, kCodeAlignment);
     287           4 :   WasmCode* code2_1 = AddCode(nm2.get(), 1, kCodeAlignment);
     288           2 :   CHECK_EQ(nm2.get(), code2_1->native_module());
     289             : 
     290           2 :   CHECK_EQ(0, code1_0->index());
     291           2 :   CHECK_EQ(1, code1_1->index());
     292           2 :   CHECK_EQ(0, code2_0->index());
     293           2 :   CHECK_EQ(1, code2_1->index());
     294             : 
     295             :   // we know the manager object is allocated here, so we shouldn't
     296             :   // find any WasmCode* associated with that ptr.
     297             :   WasmCode* not_found =
     298           2 :       manager()->LookupCode(reinterpret_cast<Address>(manager()));
     299           2 :   CHECK_NULL(not_found);
     300           2 :   WasmCode* found = manager()->LookupCode(code1_0->instruction_start());
     301           2 :   CHECK_EQ(found, code1_0);
     302             :   found = manager()->LookupCode(code2_1->instruction_start() +
     303           4 :                                 (code2_1->instructions().size() / 2));
     304           2 :   CHECK_EQ(found, code2_1);
     305           2 :   found = manager()->LookupCode(code2_1->instruction_start() +
     306           4 :                                 code2_1->instructions().size() - 1);
     307           2 :   CHECK_EQ(found, code2_1);
     308             :   found = manager()->LookupCode(code2_1->instruction_start() +
     309           4 :                                 code2_1->instructions().size());
     310           2 :   CHECK_NULL(found);
     311             :   Address mid_code1_1 =
     312           2 :       code1_1->instruction_start() + (code1_1->instructions().size() / 2);
     313           2 :   CHECK_EQ(code1_1, manager()->LookupCode(mid_code1_1));
     314             :   nm1.reset();
     315           2 :   CHECK_NULL(manager()->LookupCode(mid_code1_1));
     316           2 : }
     317             : 
     318       18228 : TEST_P(WasmCodeManagerTest, LookupWorksAfterRewrite) {
     319           2 :   SetMaxCommittedMemory(2 * page());
     320             : 
     321           4 :   NativeModulePtr nm1 = AllocModule(1 * page(), GetParam());
     322             : 
     323           4 :   WasmCode* code0 = AddCode(nm1.get(), 0, kCodeAlignment);
     324           4 :   WasmCode* code1 = AddCode(nm1.get(), 1, kCodeAlignment);
     325           2 :   CHECK_EQ(0, code0->index());
     326           2 :   CHECK_EQ(1, code1->index());
     327           2 :   CHECK_EQ(code1, manager()->LookupCode(code1->instruction_start()));
     328           2 :   WasmCode* code1_1 = AddCode(nm1.get(), 1, kCodeAlignment);
     329           2 :   CHECK_EQ(1, code1_1->index());
     330           2 :   CHECK_EQ(code1, manager()->LookupCode(code1->instruction_start()));
     331           2 :   CHECK_EQ(code1_1, manager()->LookupCode(code1_1->instruction_start()));
     332           2 : }
     333             : 
     334             : }  // namespace wasm_heap_unittest
     335             : }  // namespace wasm
     336             : }  // namespace internal
     337        9111 : }  // namespace v8

Generated by: LCOV version 1.10